diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java index d73d760a6..90c1a5bb0 100644 --- a/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java +++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/DatabasePlatform.java @@ -812,7 +812,7 @@ public class DatabasePlatform { /** * Return the SQL to create an initial partition for the given table. */ - public String tablePartitionInit(String tableName, PartitionMode mode, String property, String singlePrimaryKey) { + public String tablePartitionInit(String tableName, PartitionMode mode) { return null; } diff --git a/ebean-api/src/main/java/io/ebean/util/SplitName.java b/ebean-api/src/main/java/io/ebean/util/SplitName.java index 3a6cd4f2f..718eb1991 100644 --- a/ebean-api/src/main/java/io/ebean/util/SplitName.java +++ b/ebean-api/src/main/java/io/ebean/util/SplitName.java @@ -63,7 +63,6 @@ public final class SplitName { } private static String[] split(String name, boolean last) { - int pos = last ? name.lastIndexOf('.') : name.indexOf('.'); if (pos == -1) { if (last) { diff --git a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java index 7a19240e8..dee11bd71 100644 --- a/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java +++ b/ebean-ddl-generator/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java @@ -238,7 +238,7 @@ public class DdlGenerator implements SpiDdlGenerator { if (!databasePlatform.tablePartitionsExist(connection, tableName)) { log.info("No table partitions for table {}", tableName); PartitionMeta meta = table.getPartitionMeta(); - String initPart = databasePlatform.tablePartitionInit(tableName, meta.getMode(), meta.getProperty(), table.singlePrimaryKey()); + String initPart = databasePlatform.tablePartitionInit(tableName, meta.getMode()); sb.append(initPart).append("\n"); } } diff --git a/ebean-ddl-generator/src/main/resources/io/ebeaninternal/dbmigration/builtin-extra-ddl-partitioning.xml b/ebean-ddl-generator/src/main/resources/io/ebeaninternal/dbmigration/builtin-extra-ddl-partitioning.xml index 0a805edd0..0e3608090 100644 --- a/ebean-ddl-generator/src/main/resources/io/ebeaninternal/dbmigration/builtin-extra-ddl-partitioning.xml +++ b/ebean-ddl-generator/src/main/resources/io/ebeaninternal/dbmigration/builtin-extra-ddl-partitioning.xml @@ -17,6 +17,7 @@ begin period_start timestamptz, period_end timestamptz, period_name text, + schema_name text, base_name text, part_name text ); @@ -34,7 +35,11 @@ language plpgsql set timezone to 'UTC' as $$ begin - execute format('create table if not exists %I partition of %I for values from (''%s'') TO (''%s'')', meta.part_name, meta.base_name, meta.period_start, meta.period_end); + if (meta.schema_name = '') then + execute format('create table if not exists %I partition of %I for values from (''%s'') TO (''%s'')', meta.part_name, meta.base_name, meta.period_start, meta.period_end); + else + execute format('create table if not exists %I partition of %I.%I for values from (''%s'') TO (''%s'')', meta.part_name, meta.schema_name, meta.base_name, meta.period_start, meta.period_end); + end if; return meta.part_name; end; $$; @@ -49,7 +54,8 @@ $$; create or replace function _partition_meta( mode text, asOf date, - baseName text) + baseName text, + schemaName text default '') returns partition_meta language plpgsql set timezone to 'UTC' @@ -81,8 +87,8 @@ begin select asOfUtc, asOfUtc + interval '1 month' into meta.period_start, meta.period_end; end if; - select partName, baseName, format('%s_%s', baseName, partName) - into meta.period_name, meta.base_name, meta.part_name; + select partName, schemaName, baseName, format('%s_%s', baseName, partName) + into meta.period_name, meta.schema_name, meta.base_name, meta.part_name; return meta; end; @@ -133,70 +139,31 @@ $$; -- Function: partition -- -- Helper to ensure we create partitions into the future as needed for day, week, month --- and year based partitioning. Typically we call this periodically (e.g. every day). +-- and year based partitioning. Typically, we call this periodically (e.g. every day). -- -- Examples: -- --- select partition('week', 'trip', 4); +-- select partition('week', 'trip', 4); -- select partition('month', 'event', 1); +-- select partition('year', 'log', 1, 'my_schema'); -- ------------------------------------------------------------------------------------ create or replace function partition( mode text, -- one of 'day','week','month','year' baseName text, -- base table name partitionCount integer default 0, -- number of additional partitions + schemaName text default '', -- schema fromDate date default current_date) -- date to create first partition for returns text language plpgsql set timezone to 'UTC' as $$ begin - perform _partition_create(_partition_meta(mode, poDate, baseName)) + perform _partition_create(_partition_meta(mode, poDate, baseName, schemaName)) from _partition_over(mode, fromDate, partitionCount) poDate; return 'done'; end; $$; - - ------------------------------------------------------------------------------------- --- Function: partition_init --- --- Similar to partition but allows the first partition to be bigger with an explicit --- initDate typically to allow back dated rows to go into the initial partition. --- --- Examples: --- --- select partition_init(date '2001-01-01', 'week', 'event'); --- ------------------------------------------------------------------------------------- -create or replace function partition_init( - initDate date, -- first partition period start date - mode text, -- one of 'day','week','month','year' - baseName text, -- base table name - partitionCount integer default 0, -- number of additional partitions - fromDate date default current_date) -- date to create first partition for - returns text -language plpgsql -set timezone to 'UTC' -as $$ -declare - meta partition_meta; -begin - -- override the period start for the first partition - meta = _partition_meta(mode, fromDate, baseName); - meta.period_start = initDate; - perform _partition_create(meta); - - if (partitionCount > 0) then - -- create additional partitions normally - fromDate = fromDate + interval '1 day'; - perform _partition_create(_partition_meta(mode, poDate, baseName)) - from _partition_over(mode, fromDate, partitionCount) poDate; - end if; - - return 'done'; -end; -$$; diff --git a/platforms/postgres/src/main/java/io/ebean/platform/postgres/PostgresPlatform.java b/platforms/postgres/src/main/java/io/ebean/platform/postgres/PostgresPlatform.java index 714b52271..0c1b33053 100644 --- a/platforms/postgres/src/main/java/io/ebean/platform/postgres/PostgresPlatform.java +++ b/platforms/postgres/src/main/java/io/ebean/platform/postgres/PostgresPlatform.java @@ -11,6 +11,7 @@ import io.ebean.config.dbplatform.DbType; import io.ebean.config.dbplatform.IdType; import io.ebean.config.dbplatform.PlatformIdGenerator; import io.ebean.config.dbplatform.SqlErrorCodes; +import io.ebean.util.SplitName; import javax.sql.DataSource; import java.sql.Connection; @@ -163,11 +164,23 @@ public class PostgresPlatform extends DatabasePlatform { * Only use this if extra-ddl doesn't have some initial partitions defined (which it should). */ @Override - public String tablePartitionInit(String tableName, PartitionMode mode, String property, String pkey) { - // default partition required pg11 but this is only used for testing but bumped test docker container to pg11 by default + public String tablePartitionInit(String tableName, PartitionMode mode) { + // default partition required pg11 but this is only used for testing but bumped test docker container to pg14 by default + String[] schemaTable = SplitName.split(tableName); + + String baseTable; + String plusSchema; + if (schemaTable[0] == null) { + plusSchema = ""; + baseTable = tableName; + } else { + // table in an explicit schema + plusSchema = ",'" + schemaTable[0] + "'"; + baseTable = schemaTable[1]; + } return "create table " + tableName + "_default" + " partition of " + tableName + " default;\n" + - "select partition('" + mode.name().toLowerCase() + "','" + tableName + "',1);"; + "select partition('" + mode.name().toLowerCase() + "','" + baseTable + "',1" + plusSchema + ");"; } } diff --git a/platforms/postgres/src/test/java/io/ebean/platform/postgres/PostgresPlatformTest.java b/platforms/postgres/src/test/java/io/ebean/platform/postgres/PostgresPlatformTest.java index cb2075158..747fbbcff 100644 --- a/platforms/postgres/src/test/java/io/ebean/platform/postgres/PostgresPlatformTest.java +++ b/platforms/postgres/src/test/java/io/ebean/platform/postgres/PostgresPlatformTest.java @@ -1,12 +1,11 @@ package io.ebean.platform.postgres; -import io.ebean.Query; +import io.ebean.annotation.PartitionMode; import io.ebean.annotation.Platform; import io.ebean.config.PlatformConfig; -import io.ebean.config.dbplatform.DbPlatformType; import io.ebean.config.dbplatform.DatabasePlatform; +import io.ebean.config.dbplatform.DbPlatformType; import io.ebean.config.dbplatform.DbType; - import org.junit.jupiter.api.Test; import static io.ebean.Query.LockType.*; @@ -14,11 +13,10 @@ import static io.ebean.Query.LockWait.*; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -public class PostgresPlatformTest { +class PostgresPlatformTest { @Test - public void testUuidType() { - + void testUuidType() { PostgresPlatform platform = new PostgresPlatform(); platform.configure(new PlatformConfig()); @@ -29,8 +27,21 @@ public class PostgresPlatformTest { } @Test - public void default_forUpdate_expect_noKeyUsed() { + void tablePartitionInit() { + String sql = new PostgresPlatform().tablePartitionInit("foo", PartitionMode.WEEK); + assertThat(sql).isEqualTo("create table foo_default partition of foo default;\n" + + "select partition('week','foo',1);"); + } + @Test + void tablePartitionInit_withSchema() { + String sql = new PostgresPlatform().tablePartitionInit("bar.foo", PartitionMode.WEEK); + assertThat(sql).isEqualTo("create table bar.foo_default partition of bar.foo default;\n" + + "select partition('week','foo',1,'bar');"); + } + + @Test + void default_forUpdate_expect_noKeyUsed() { PostgresPlatform platform = new PostgresPlatform(); PlatformConfig config = new PlatformConfig(); @@ -58,8 +69,7 @@ public class PostgresPlatformTest { } @Test - public void lockWithKey_forUpdate() { - + void lockWithKey_forUpdate() { PostgresPlatform platform = new PostgresPlatform(); PlatformConfig config = new PlatformConfig();