mirror of https://github.com/citusdata/citus.git
Disallow distributing by numeric with negative scale
PG15 allows numeric scale to be negative or greater than precision. This causes issues and we may end up routing queries to a wrong shard due to differing hash results after rounding. Formerly, when specifying NUMERIC(precision, scale), the scale had to be in the range [0, precision], which was per SQL spec. PG15 extends the range of allowed scales to [-1000, 1000]. A negative scale implies rounding before the decimal point. For example, a column might be declared with a scale of -3 to round values to the nearest thousand. Note that the display scale remains non-negative, so in this case the display scale will be zero, and all digits before the decimal point will be displayed. Relevant PG commit: 085f931f52494e1f304e35571924efa6fcdc2b44pull/6297/head
parent
d7f41cacbe
commit
85b19c851a
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue