!5848 A兼容模式下,修改float列类型时添加验证

Merge pull request !5848 from zhubin79/alter-float
This commit is contained in:
opengauss_bot
2024-08-02 01:57:49 +00:00
committed by Gitee
7 changed files with 198 additions and 1 deletions

View File

@ -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),

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -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;

View File

@ -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));