!5848 A兼容模式下,修改float列类型时添加验证
Merge pull request !5848 from zhubin79/alter-float
This commit is contained in:
@ -10135,6 +10135,16 @@ static void ATRewriteTableInternal(AlteredTableInfo* tab, Relation oldrel, Relat
|
||||
} else {
|
||||
((HeapScanDesc) scan)->rs_tupdesc = oldTupDesc;
|
||||
while ((tuple = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) {
|
||||
if (tab->check_pass_with_relempty == AT_FASN_FAIL_PRECISION) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("column to be modified must be empty to decrease precision or scale")));
|
||||
} else if (tab->check_pass_with_relempty == AT_FASN_FAIL_TYPE) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("column to be modified must be empty to change datatype")));
|
||||
}
|
||||
|
||||
if (tab->rewrite > 0) {
|
||||
Oid tupOid = InvalidOid;
|
||||
int newvals_num = 0;
|
||||
@ -16333,6 +16343,76 @@ void CheckHugeToast(AlteredTableInfo *tab, Relation rel, AttrNumber attnum)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check numeric type modify.
|
||||
* column to be modified must be empty to decrease precision or scale;
|
||||
* column to be modified must be empty to change datatype.
|
||||
*
|
||||
* Only valid numeric scale decrease, And when set behavior_compat_options = 'float_as_numeric'
|
||||
* (float use numeric indicate), valid float(p) can't decrease precision and can't change to other datatype.
|
||||
*/
|
||||
static int CheckFloatIllegalTypeConversion(Relation rel, Oid origintype, int32 origintypmod, Oid targettype, int32 targettypmod)
|
||||
{
|
||||
int32 oriprecision;
|
||||
int16 oriscale;
|
||||
int32 tarprecision;
|
||||
int16 tarscale;
|
||||
bool compatibilityversion;
|
||||
int result = AT_FASN_PASS;
|
||||
|
||||
if (RelationIsCUFormat(rel) || !(u_sess->attr.attr_sql.sql_compatibility == A_FORMAT)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
compatibilityversion = FLOAT_AS_NUMERIC && t_thrd.proc->workingVersionNum >= FLOAT_VERSION_NUMBER;
|
||||
|
||||
if (origintype == NUMERICOID) {
|
||||
oriprecision = (int32) ((((uint32)(origintypmod - VARHDRSZ)) >> 16) & 0xffff);
|
||||
oriscale = (int16) (((uint32)(origintypmod - VARHDRSZ)) & 0xffff);
|
||||
}
|
||||
|
||||
if (targettype == NUMERICOID) {
|
||||
tarprecision = (int32) ((((uint32)(targettypmod - VARHDRSZ)) >> 16) & 0xffff);
|
||||
tarscale = (int16) (((uint32)(targettypmod - VARHDRSZ)) & 0xffff);
|
||||
|
||||
if (origintype == NUMERICOID) {
|
||||
if (compatibilityversion) {
|
||||
// float decrease precision
|
||||
if (IS_FLOAT_AS_NUMERIC(oriscale) && IS_FLOAT_AS_NUMERIC(tarscale) && tarprecision < oriprecision) {
|
||||
result = AT_FASN_FAIL_PRECISION;
|
||||
}
|
||||
// can't change datatype between float and numeric
|
||||
else if (IS_FLOAT_AS_NUMERIC(oriscale) != IS_FLOAT_AS_NUMERIC(tarscale)) {
|
||||
result = AT_FASN_FAIL_PRECISION;
|
||||
}
|
||||
}
|
||||
|
||||
// numeric decrease scale
|
||||
if (!IS_FLOAT_AS_NUMERIC(oriscale) && !IS_FLOAT_AS_NUMERIC(tarscale) && tarscale < oriscale) {
|
||||
result = AT_FASN_FAIL_PRECISION;
|
||||
}
|
||||
|
||||
return result;
|
||||
} else if (compatibilityversion && IS_FLOAT_AS_NUMERIC(tarscale)
|
||||
&& (origintype == INT4OID || origintype == INT2OID || origintype ==INT1OID)) {
|
||||
// O* has no int type, so all use NUMBER(38) instead.
|
||||
if (ceil(log10(2) * tarprecision) < 38) {
|
||||
result = AT_FASN_FAIL_PRECISION;
|
||||
|
||||
}
|
||||
// convert int to numeric
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (compatibilityversion && origintype == NUMERICOID && IS_FLOAT_AS_NUMERIC(oriscale) && targettype != origintype) {
|
||||
// can't change other float to other datatype.
|
||||
result = AT_FASN_FAIL_TYPE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* ALTER COLUMN TYPE
|
||||
*/
|
||||
@ -16486,6 +16566,8 @@ static void ATPrepAlterColumnType(List** wqueue, AlteredTableInfo* tab, Relation
|
||||
transform = (Node*)makeVar(1, attnum, attTup->atttypid, attTup->atttypmod, attTup->attcollation, 0);
|
||||
}
|
||||
|
||||
tab->check_pass_with_relempty = CheckFloatIllegalTypeConversion(rel, attTup->atttypid, attTup->atttypmod, targettype, targettypmod);
|
||||
|
||||
transform = coerce_to_target_type(pstate,
|
||||
transform,
|
||||
exprType(transform),
|
||||
|
||||
@ -65,6 +65,15 @@
|
||||
#define AT_NUM_PASSES 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* state information of alter float datatype result (at float_as_numeric).
|
||||
*
|
||||
* Phase 3 scan if table not empty and 'check_floatasnumeric' > 0, go err.
|
||||
*/
|
||||
#define AT_FASN_PASS 0
|
||||
#define AT_FASN_FAIL_PRECISION 1
|
||||
#define AT_FASN_FAIL_TYPE 2
|
||||
|
||||
typedef struct AlteredTableInfo {
|
||||
/* Information saved before any work commences: */
|
||||
Oid relid; /* Relation to work on */
|
||||
@ -78,6 +87,7 @@ typedef struct AlteredTableInfo {
|
||||
List* newvals; /* List of NewColumnValue */
|
||||
bool new_notnull; /* T if we added new NOT NULL constraints */
|
||||
int rewrite; /* Reason if a rewrite is forced */
|
||||
int check_pass_with_relempty; /* alter column check condition, require table empty condition in phase 3 */
|
||||
Oid newTableSpace; /* new tablespace; 0 means no change */
|
||||
/* Objects to rebuild after completing ALTER TYPE operations */
|
||||
List* changedConstraintOids; /* OIDs of constraints to rebuild */
|
||||
|
||||
@ -156,6 +156,7 @@ typedef struct NumericData* Numeric;
|
||||
#define NUMERIC_MAX_PRECISION 1000
|
||||
#define NUMERIC_MAX_SCALE 1000
|
||||
#define NUMERIC_MIN_SCALE -84
|
||||
#define IS_FLOAT_AS_NUMERIC(scale) ((scale) == INT16_MIN)
|
||||
|
||||
/*
|
||||
* Internal limits on the scales chosen for calculation results
|
||||
|
||||
@ -123,6 +123,63 @@ SELECT * FROM t1;
|
||||
NaN | NaN | NaN
|
||||
(2 rows)
|
||||
|
||||
DROP TABLE t1;
|
||||
-- alter table test
|
||||
set behavior_compat_options = 'float_as_numeric, truncate_numeric_tail_zero';
|
||||
CREATE TABLE t1(col1 int, col2 float(5), col3 float(44), col4 float(20));
|
||||
INSERT INTO t1 VALUES (1, 12345678901234.567890123456789, 123456789123456789123456789123456789123456789.123456789123456789123456789123456789, 123.123);
|
||||
INSERT INTO t1 VALUES (12, 0.12345678901234567890123456789, 0.123456789123456789123456789123456789123456789123456789123456789123456789123456789,123.123);
|
||||
INSERT INTO t1 VALUES (21, 123456789.12345,123456789123456789123456789.12345678912, 123.123);
|
||||
SELECT pg_get_tabledef('t1');
|
||||
pg_get_tabledef
|
||||
-----------------------------------------
|
||||
SET search_path = test_float; +
|
||||
CREATE TABLE t1 ( +
|
||||
col1 integer, +
|
||||
col2 float(5), +
|
||||
col3 float(44), +
|
||||
col4 float(20) +
|
||||
) +
|
||||
WITH (orientation=row, compression=no);
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM t1 ORDER BY col1;
|
||||
col1 | col2 | col3 | col4
|
||||
------+----------------+-----------------------------------------------+---------
|
||||
1 | 12000000000000 | 123456789123460000000000000000000000000000000 | 123.123
|
||||
12 | .12 | .12345678912346 | 123.123
|
||||
21 | 120000000 | 123456789123460000000000000 | 123.123
|
||||
(3 rows)
|
||||
|
||||
-- alter table not empty
|
||||
ALTER TABLE t1 MODIFY (col1 float(1)); -- error
|
||||
ERROR: column to be modified must be empty to decrease precision or scale
|
||||
-- alter table empty
|
||||
DELETE FROM t1;
|
||||
SELECT * FROM t1 ORDER BY col1;
|
||||
col1 | col2 | col3 | col4
|
||||
------+------+------+------
|
||||
(0 rows)
|
||||
|
||||
ALTER TABLE t1 MODIFY (col1 float(1)); -- success
|
||||
SELECT pg_get_tabledef('t1');
|
||||
pg_get_tabledef
|
||||
-----------------------------------------
|
||||
SET search_path = test_float; +
|
||||
CREATE TABLE t1 ( +
|
||||
col1 float(1), +
|
||||
col2 float(5), +
|
||||
col3 float(44), +
|
||||
col4 float(20) +
|
||||
) +
|
||||
WITH (orientation=row, compression=no);
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM t1 ORDER BY col1;
|
||||
col1 | col2 | col3 | col4
|
||||
------+------+------+------
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE t1;
|
||||
-- PL/SQL test
|
||||
CREATE OR REPLACE PACKAGE pak1 as
|
||||
|
||||
@ -187,6 +187,26 @@ Table "numeric_negative_scale_test.t2"
|
||||
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t2;
|
||||
-- test alter table to decrease scale
|
||||
CREATE TABLE t1 (c1 int, c2 numeric(5, 2), c3 numeric(5, -2));
|
||||
INSERT INTO t1 VALUES (1, 546.12, 456135.12);
|
||||
SELECT * FROM t1;
|
||||
c1 | c2 | c3
|
||||
----+--------+--------
|
||||
1 | 546.12 | 456100
|
||||
(1 row)
|
||||
|
||||
ALTER TABLE t1 MODIFY (c2 numeric(5, 1)); -- error
|
||||
ERROR: column to be modified must be empty to decrease precision or scale
|
||||
ALTER TABLE t1 MODIFY (c3 numeric(5, -3)); -- error
|
||||
ERROR: column to be modified must be empty to decrease precision or scale
|
||||
SELECT * FROM t1;
|
||||
c1 | c2 | c3
|
||||
----+--------+--------
|
||||
1 | 546.12 | 456100
|
||||
(1 row)
|
||||
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t3(a numeric(1,1001));
|
||||
ERROR: NUMERIC scale must be between -84 and 1000
|
||||
LINE 1: CREATE TABLE t3(a numeric(1,1001));
|
||||
|
||||
@ -56,6 +56,24 @@ INSERT INTO t1 SELECT 'NaN', 'NaN', 'NaN';
|
||||
SELECT * FROM t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
-- alter table test
|
||||
set behavior_compat_options = 'float_as_numeric, truncate_numeric_tail_zero';
|
||||
CREATE TABLE t1(col1 int, col2 float(5), col3 float(44), col4 float(20));
|
||||
INSERT INTO t1 VALUES (1, 12345678901234.567890123456789, 123456789123456789123456789123456789123456789.123456789123456789123456789123456789, 123.123);
|
||||
INSERT INTO t1 VALUES (12, 0.12345678901234567890123456789, 0.123456789123456789123456789123456789123456789123456789123456789123456789123456789,123.123);
|
||||
INSERT INTO t1 VALUES (21, 123456789.12345,123456789123456789123456789.12345678912, 123.123);
|
||||
SELECT pg_get_tabledef('t1');
|
||||
SELECT * FROM t1 ORDER BY col1;
|
||||
-- alter table not empty
|
||||
ALTER TABLE t1 MODIFY (col1 float(1)); -- error
|
||||
-- alter table empty
|
||||
DELETE FROM t1;
|
||||
SELECT * FROM t1 ORDER BY col1;
|
||||
ALTER TABLE t1 MODIFY (col1 float(1)); -- success
|
||||
SELECT pg_get_tabledef('t1');
|
||||
SELECT * FROM t1 ORDER BY col1;
|
||||
DROP TABLE t1;
|
||||
|
||||
-- PL/SQL test
|
||||
|
||||
CREATE OR REPLACE PACKAGE pak1 as
|
||||
@ -83,4 +101,4 @@ DROP PACKAGE pak1;
|
||||
|
||||
reset behavior_compat_options;
|
||||
reset current_schema;
|
||||
drop schema test_float cascade;
|
||||
drop schema test_float cascade;
|
||||
|
||||
@ -71,6 +71,15 @@ CREATE TABLE t2(a numeric(1,-84));
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t2;
|
||||
|
||||
-- test alter table to decrease scale
|
||||
CREATE TABLE t1 (c1 int, c2 numeric(5, 2), c3 numeric(5, -2));
|
||||
INSERT INTO t1 VALUES (1, 546.12, 456135.12);
|
||||
SELECT * FROM t1;
|
||||
ALTER TABLE t1 MODIFY (c2 numeric(5, 1)); -- error
|
||||
ALTER TABLE t1 MODIFY (c3 numeric(5, -3)); -- error
|
||||
SELECT * FROM t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
CREATE TABLE t3(a numeric(1,1001));
|
||||
CREATE TABLE t3(a numeric(1,-85));
|
||||
CREATE TABLE t3(a numeric(1,1001));
|
||||
|
||||
Reference in New Issue
Block a user