diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index d81276cc9..b81407d2d 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -1356,6 +1356,7 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt) qry->commandType = CMD_INSERT; pstate->p_is_insert = true; + pstate->p_has_ignore = stmt->hasIgnore; /* set io state for backend status for the thread, we will use it to check user space */ pgstat_set_io_state(IOSTATE_WRITE); @@ -3325,6 +3326,7 @@ static Query* transformUpdateStmt(ParseState* pstate, UpdateStmt* stmt) qry->commandType = CMD_UPDATE; pstate->p_is_insert = false; + pstate->p_has_ignore = stmt->hasIgnore; /* set io state for backend status for the thread, we will use it to check user space */ pgstat_set_io_state(IOSTATE_READ); diff --git a/src/common/backend/parser/parse_node.cpp b/src/common/backend/parser/parse_node.cpp index 3430ad52e..7829d58f9 100644 --- a/src/common/backend/parser/parse_node.cpp +++ b/src/common/backend/parser/parse_node.cpp @@ -58,6 +58,7 @@ ParseState* make_parsestate(ParseState* parentParseState) pstate->ignoreplus = false; pstate->p_plusjoin_rte_info = NULL; pstate->p_rawdefaultlist = NIL; + pstate->p_has_ignore = false; if (parentParseState != NULL) { pstate->p_sourcetext = parentParseState->p_sourcetext; diff --git a/src/common/backend/parser/parse_target.cpp b/src/common/backend/parser/parse_target.cpp index 63d8a3df5..8a398a464 100644 --- a/src/common/backend/parser/parse_target.cpp +++ b/src/common/backend/parser/parse_target.cpp @@ -33,6 +33,7 @@ #include "utils/rel.h" #include "utils/rel_gs.h" #include "utils/typcache.h" +#include "executor/executor.h" #include "gs_ledger/ledger_utils.h" static void markTargetListOrigin(ParseState* pstate, TargetEntry* tle, Var* var, int levelsup); @@ -513,17 +514,24 @@ Expr* transformAssignedExpr(ParseState* pstate, Expr* expr, char* colname, int a colname, format_type_be(attrtype), format_type_be(type_id)), errhint("You will need to rewrite or cast the expression."), parser_errposition(pstate, exprLocation(orig_expr)))); + } else { + if (!pstate->p_has_ignore) { + ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("column \"%s\" is of type %s" + " but expression is of type %s", + colname, format_type_be(attrtype), format_type_be(type_id)), + errhint("You will need to rewrite or cast the expression."), + parser_errposition(pstate, exprLocation(orig_expr)))); + } + expr = (Expr*)makeConst(attrtype, attrtypmod, attrcollation, rd->rd_att->attrs[attrno - 1]->attlen, + GetTypeZeroValue(rd->rd_att->attrs[attrno - 1]), false, + rd->rd_att->attrs[attrno - 1]->attbyval); + ereport(WARNING, (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("column \"%s\" is of type %s" + " but expression is of type %s. Data truncated automatically.", + colname, format_type_be(attrtype), format_type_be(type_id)))); } - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("column \"%s\" is of type %s" - " but expression is of type %s", - colname, - format_type_be(attrtype), - format_type_be(type_id)), - errhint("You will need to rewrite or cast the expression."), - parser_errposition(pstate, exprLocation(orig_expr)))); - } + } } ELOG_FIELD_NAME_END; diff --git a/src/gausskernel/runtime/executor/execUtils.cpp b/src/gausskernel/runtime/executor/execUtils.cpp index 58e0be071..921785abb 100644 --- a/src/gausskernel/runtime/executor/execUtils.cpp +++ b/src/gausskernel/runtime/executor/execUtils.cpp @@ -2438,9 +2438,9 @@ void PthreadRwLockInit(pthread_rwlock_t* rwlock, pthread_rwlockattr_t *attr) } /* - * Get defaulted value of specific time type + * Get defaulted value of specific type */ -static Datum GetTimeTypeZeroValue(Form_pg_attribute att_tup) +Datum GetTypeZeroValue(Form_pg_attribute att_tup) { Datum result; switch (att_tup->atttypid) { @@ -2473,21 +2473,10 @@ static Datum GetTimeTypeZeroValue(Form_pg_attribute att_tup) smalldatetime_in, CStringGetDatum("1970-01-01 08:00:00"), ObjectIdGetDatum(0), Int32GetDatum(-1)); break; } - default: { + case DATEOID: { result = timestamp2date(SetEpochTimestamp()); break; } - } - return result; -} - -/* - * Get defaulted value of specific non-time type - */ -static Datum GetNotTimeTypeZeroValue(Form_pg_attribute att_tup) -{ - Datum result; - switch (att_tup->atttypid) { case UUIDOID: { result = (Datum)DirectFunctionCall3(uuid_in, CStringGetDatum("00000000-0000-0000-0000-000000000000"), ObjectIdGetDatum(0), Int32GetDatum(-1)); @@ -2576,15 +2565,7 @@ Tuple ReplaceTupleNullCol(TupleDesc tupleDesc, TupleTableSlot *slot) int attrChk; for (attrChk = 1; attrChk <= natts; attrChk++) { if (tupleDesc->attrs[attrChk - 1]->attnotnull && tableam_tslot_attisnull(slot, attrChk)) { - bool isTimeType = (tupleDesc->attrs[attrChk - 1]->atttypid == DATEOID || - tupleDesc->attrs[attrChk - 1]->atttypid == TIMESTAMPOID || - tupleDesc->attrs[attrChk - 1]->atttypid == TIMESTAMPTZOID || - tupleDesc->attrs[attrChk - 1]->atttypid == TIMETZOID || - tupleDesc->attrs[attrChk - 1]->atttypid == INTERVALOID || - tupleDesc->attrs[attrChk - 1]->atttypid == TINTERVALOID || - tupleDesc->attrs[attrChk - 1]->atttypid == SMALLDATETIMEOID); - values[attrChk - 1] = isTimeType ? GetTimeTypeZeroValue(tupleDesc->attrs[attrChk - 1]) - : GetNotTimeTypeZeroValue(tupleDesc->attrs[attrChk - 1]); + values[attrChk - 1] = GetTypeZeroValue(tupleDesc->attrs[attrChk - 1]); replaces[attrChk - 1] = true; } } diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index cab12f209..fb6e2b886 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -466,6 +466,7 @@ extern void ExecSimpleRelationDelete(EState *estate, EPQState *epqstate, TupleTa FakeRelationPartition *relAndPart); extern void CheckCmdReplicaIdentity(Relation rel, CmdType cmd); extern void GetFakeRelAndPart(EState *estate, Relation rel, TupleTableSlot *slot, FakeRelationPartition *relAndPart); +extern Datum GetTypeZeroValue(Form_pg_attribute att_tup); extern Tuple ReplaceTupleNullCol(TupleDesc tupleDesc, TupleTableSlot* slot); // AutoMutexLock diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 89aeabfc6..976dd0a3e 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -189,6 +189,7 @@ struct ParseState { List *sw_fromClause; WithClause *origin_with; bool p_hasStartWith; + bool p_has_ignore; /* whether SQL has ignore hint */ /* * Optional hook functions for parser callbacks. These are null unless diff --git a/src/test/regress/expected/ignore/ignore_type_transform.out b/src/test/regress/expected/ignore/ignore_type_transform.out index aed08f43f..14a15f30d 100644 --- a/src/test/regress/expected/ignore/ignore_type_transform.out +++ b/src/test/regress/expected/ignore/ignore_type_transform.out @@ -791,5 +791,123 @@ select * from t_int; 12 (1 row) +-- test for inconvertible type transform. +drop table if exists t_int; +create table t_int(num int); +insert /*+ ignore_error */ into t_int values('2011-8-2'::timestamp); +WARNING: column "num" is of type integer but expression is of type timestamp without time zone. Data truncated automatically. +CONTEXT: referenced column: num +select * from t_int; + num +----- + 0 +(1 row) + +delete from t_int; +drop table if exists t_timestamp; +NOTICE: table "t_timestamp" does not exist, skipping +create table t_timestamp(val timestamp); +insert into t_timestamp values('2011-8-2'); +insert /*+ ignore_error */ into t_int select val from t_timestamp; +WARNING: column "num" is of type integer but expression is of type timestamp without time zone. Data truncated automatically. +CONTEXT: referenced column: num +select * from t_int; + num +----- + 0 +(1 row) + +delete from t_int; +insert into t_int values(999); +update /*+ ignore_error */ t_int set num = '2011-8-2'::timestamp; +WARNING: column "num" is of type integer but expression is of type timestamp without time zone. Data truncated automatically. +CONTEXT: referenced column: num +select * from t_int; + num +----- + 0 +(1 row) + +drop table if exists t_multi; +NOTICE: table "t_multi" does not exist, skipping +create table t_multi(c1 int unique, c2 int); +NOTICE: CREATE TABLE / UNIQUE will create implicit index "t_multi_c1_key" for table "t_multi" +insert /*+ ignore_error */ into t_multi values('2011-8-2'::timestamp, 1); +WARNING: column "c1" is of type integer but expression is of type timestamp without time zone. Data truncated automatically. +CONTEXT: referenced column: c1 +insert /*+ ignore_error */ into t_multi values(0, 0) on duplicate key update c2 = 2; +select * from t_multi; + c1 | c2 +----+---- + 0 | 2 +(1 row) + +insert /*+ ignore_error */ into t_multi values('2011-8-2'::timestamp , 3) on duplicate key update c2 = 3; +WARNING: column "c1" is of type integer but expression is of type timestamp without time zone. Data truncated automatically. +CONTEXT: referenced column: c1 +select * from t_multi; + c1 | c2 +----+---- + 0 | 3 +(1 row) + +drop table if exists t_float8; +NOTICE: table "t_float8" does not exist, skipping +create table t_float8(num float8); +insert /*+ ignore_error */ into t_float8 values('2011-8-2'::timestamp); +WARNING: column "num" is of type double precision but expression is of type timestamp without time zone. Data truncated automatically. +CONTEXT: referenced column: num +select * from t_float8; + num +----- + 0 +(1 row) + +delete from t_float8; +insert into t_float8 values(123.999); +update /*+ ignore_error */ t_float8 set num = '2011-8-2'::timestamp; +WARNING: column "num" is of type double precision but expression is of type timestamp without time zone. Data truncated automatically. +CONTEXT: referenced column: num +select * from t_float8; + num +----- + 0 +(1 row) + +drop table if exists t_uuid; +NOTICE: table "t_uuid" does not exist, skipping +create table t_uuid(val uuid); +insert /*+ ignore_error */ into t_uuid values(0); +WARNING: column "val" is of type uuid but expression is of type integer. Data truncated automatically. +CONTEXT: referenced column: val +select * from t_uuid; + val +-------------------------------------- + 00000000-0000-0000-0000-000000000000 +(1 row) + +drop table if exists t_date; +NOTICE: table "t_date" does not exist, skipping +create table t_date(val date); +insert /*+ ignore_error */ into t_date values(0); +WARNING: column "val" is of type date but expression is of type integer. Data truncated automatically. +CONTEXT: referenced column: val +select * from t_date; + val +------------ + 01-01-1970 +(1 row) + +delete from t_date; +insert into t_date values('2011-8-2'); +update /*+ ignore_error */ t_date set val = 0; +WARNING: column "val" is of type date but expression is of type integer. Data truncated automatically. +CONTEXT: referenced column: val +select * from t_date; + val +------------ + 01-01-1970 +(1 row) + \c postgres drop database if exists sql_ignore_type_transform_test; diff --git a/src/test/regress/sql/ignore/ignore_type_transform.sql b/src/test/regress/sql/ignore/ignore_type_transform.sql index 118ccb372..f367aae55 100644 --- a/src/test/regress/sql/ignore/ignore_type_transform.sql +++ b/src/test/regress/sql/ignore/ignore_type_transform.sql @@ -342,5 +342,56 @@ create table t_int(num int); insert /*+ ignore_error */ into t_int values('12a34'); select * from t_int; +-- test for inconvertible type transform. +drop table if exists t_int; +create table t_int(num int); +insert /*+ ignore_error */ into t_int values('2011-8-2'::timestamp); +select * from t_int; + +delete from t_int; +drop table if exists t_timestamp; +create table t_timestamp(val timestamp); +insert into t_timestamp values('2011-8-2'); +insert /*+ ignore_error */ into t_int select val from t_timestamp; +select * from t_int; + +delete from t_int; +insert into t_int values(999); +update /*+ ignore_error */ t_int set num = '2011-8-2'::timestamp; +select * from t_int; + +drop table if exists t_multi; +create table t_multi(c1 int unique, c2 int); +insert /*+ ignore_error */ into t_multi values('2011-8-2'::timestamp, 1); +insert /*+ ignore_error */ into t_multi values(0, 0) on duplicate key update c2 = 2; +select * from t_multi; +insert /*+ ignore_error */ into t_multi values('2011-8-2'::timestamp , 3) on duplicate key update c2 = 3; +select * from t_multi; + +drop table if exists t_float8; +create table t_float8(num float8); +insert /*+ ignore_error */ into t_float8 values('2011-8-2'::timestamp); +select * from t_float8; + +delete from t_float8; +insert into t_float8 values(123.999); +update /*+ ignore_error */ t_float8 set num = '2011-8-2'::timestamp; +select * from t_float8; + +drop table if exists t_uuid; +create table t_uuid(val uuid); +insert /*+ ignore_error */ into t_uuid values(0); +select * from t_uuid; + +drop table if exists t_date; +create table t_date(val date); +insert /*+ ignore_error */ into t_date values(0); +select * from t_date; + +delete from t_date; +insert into t_date values('2011-8-2'); +update /*+ ignore_error */ t_date set val = 0; +select * from t_date; + \c postgres drop database if exists sql_ignore_type_transform_test; \ No newline at end of file