#2701 -[DDL] Postgres DDL generation for table partitioning with schema - helper method to add partitions does not work

This also removes the helper partition_init() stored function as it isn't really needed now that postgres has default partition support (since PG11 actually, partition_init predates PG11).
This commit is contained in:
Rob Bygrave
2022-05-23 20:39:45 +12:00
parent 62106935f4
commit 74b4994221
6 changed files with 52 additions and 63 deletions
@@ -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;
}
@@ -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) {
@@ -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");
}
}
@@ -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;
$$;
</ddl-script>
</extra-ddl>
@@ -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 + ");";
}
}
@@ -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();