diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index fd35acce2..a3ff6c4cb 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -139,6 +139,14 @@ static Oid DropFKeysAndUndistributeTable(Oid relationId); static void DropFKeysRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag); static void CopyLocalDataIntoShards(Oid relationId); static List * TupleDescColumnNameList(TupleDesc tupleDescriptor); + +#if (PG_VERSION_NUM >= PG_VERSION_15) +static bool DistributionColumnUsesNumericColumnNegativeScale(TupleDesc relationDesc, + Var *distributionColumn); +static int numeric_typmod_scale(int32 typmod); +static bool is_valid_numeric_typmod(int32 typmod); +#endif + static bool DistributionColumnUsesGeneratedStoredColumn(TupleDesc relationDesc, Var *distributionColumn); static bool CanUseExclusiveConnections(Oid relationId, bool localTableEmpty); @@ -1681,6 +1689,20 @@ EnsureRelationCanBeDistributed(Oid relationId, Var *distributionColumn, "AS (...) STORED."))); } +#if (PG_VERSION_NUM >= PG_VERSION_15) + + /* verify target relation is not distributed by a column of type numeric with negative scale */ + if (distributionMethod != DISTRIBUTE_BY_NONE && + DistributionColumnUsesNumericColumnNegativeScale(relationDesc, + distributionColumn)) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot distribute relation: %s", relationName), + errdetail("Distribution column must not use numeric type " + "with negative scale"))); + } +#endif + /* check for support function needed by specified partition method */ if (distributionMethod == DISTRIBUTE_BY_HASH) { @@ -2401,6 +2423,59 @@ RelationUsesIdentityColumns(TupleDesc relationDesc) } +#if (PG_VERSION_NUM >= PG_VERSION_15) + +/* + * is_valid_numeric_typmod checks if the typmod value is valid + * + * Because of the offset, valid numeric typmods are at least VARHDRSZ + * + * Copied from PG. See numeric.c for understanding how this works. + */ +static bool +is_valid_numeric_typmod(int32 typmod) +{ + return typmod >= (int32) VARHDRSZ; +} + + +/* + * numeric_typmod_scale extracts the scale from a numeric typmod. + * + * Copied from PG. See numeric.c for understanding how this works. + * + */ +static int +numeric_typmod_scale(int32 typmod) +{ + return (((typmod - VARHDRSZ) & 0x7ff) ^ 1024) - 1024; +} + + +/* + * DistributionColumnUsesNumericColumnNegativeScale returns whether a given relation uses + * numeric data type with negative scale on distribution column + */ +static bool +DistributionColumnUsesNumericColumnNegativeScale(TupleDesc relationDesc, + Var *distributionColumn) +{ + Form_pg_attribute attributeForm = TupleDescAttr(relationDesc, + distributionColumn->varattno - 1); + + if (attributeForm->atttypid == NUMERICOID && + is_valid_numeric_typmod(attributeForm->atttypmod) && + numeric_typmod_scale(attributeForm->atttypmod) < 0) + { + return true; + } + + return false; +} + + +#endif + /* * DistributionColumnUsesGeneratedStoredColumn returns whether a given relation uses * GENERATED ALWAYS AS (...) STORED on distribution column diff --git a/src/test/regress/expected/pg15.out b/src/test/regress/expected/pg15.out index 1b214f4a9..e4d24af35 100644 --- a/src/test/regress/expected/pg15.out +++ b/src/test/regress/expected/pg15.out @@ -392,9 +392,37 @@ ON (true) WHEN MATCHED THEN UPDATE SET x = (SELECT count(*) FROM tbl2); ERROR: MERGE command is not supported on Citus tables yet +-- test numeric types with negative scale +CREATE TABLE numeric_negative_scale(numeric_column numeric(3,-1), orig_value int); +INSERT into numeric_negative_scale SELECT x,x FROM generate_series(111, 115) x; +-- verify that we can not distribute by a column that has numeric type with negative scale +SELECT create_distributed_table('numeric_negative_scale','numeric_column'); +ERROR: cannot distribute relation: numeric_negative_scale +DETAIL: Distribution column must not use numeric type with negative scale +-- However, we can distribute by other columns +SELECT create_distributed_table('numeric_negative_scale','orig_value'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$pg15.numeric_negative_scale$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT * FROM numeric_negative_scale ORDER BY 1,2; + numeric_column | orig_value +--------------------------------------------------------------------- + 110 | 111 + 110 | 112 + 110 | 113 + 110 | 114 + 120 | 115 +(5 rows) + -- Clean up DROP SCHEMA pg15 CASCADE; -NOTICE: drop cascades to 9 other objects +NOTICE: drop cascades to 10 other objects DETAIL: drop cascades to collation german_phonebook_test drop cascades to collation default_provider drop cascades to table sale @@ -404,3 +432,4 @@ drop cascades to view sale_triggers drop cascades to table generated_stored_ref drop cascades to table tbl1 drop cascades to table tbl2 +drop cascades to table numeric_negative_scale diff --git a/src/test/regress/sql/pg15.sql b/src/test/regress/sql/pg15.sql index 73cee8054..189299bd8 100644 --- a/src/test/regress/sql/pg15.sql +++ b/src/test/regress/sql/pg15.sql @@ -245,5 +245,15 @@ ON (true) WHEN MATCHED THEN UPDATE SET x = (SELECT count(*) FROM tbl2); +-- test numeric types with negative scale +CREATE TABLE numeric_negative_scale(numeric_column numeric(3,-1), orig_value int); +INSERT into numeric_negative_scale SELECT x,x FROM generate_series(111, 115) x; +-- verify that we can not distribute by a column that has numeric type with negative scale +SELECT create_distributed_table('numeric_negative_scale','numeric_column'); +-- However, we can distribute by other columns +SELECT create_distributed_table('numeric_negative_scale','orig_value'); + +SELECT * FROM numeric_negative_scale ORDER BY 1,2; + -- Clean up DROP SCHEMA pg15 CASCADE;