From 47cdfb7dc07e94df7e35de4d45c58c6fb30d44cb Mon Sep 17 00:00:00 2001 From: laishenghao Date: Wed, 4 Jun 2025 16:28:53 +0800 Subject: [PATCH] =?UTF-8?q?INSERT=E4=B8=8EREPLACE=E6=80=A7=E8=83=BD?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=90=88=E5=85=A5600?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bin/gs_guc/cluster_guc.conf | 1 + src/common/backend/parser/analyze.cpp | 616 +++++++++++++++--- src/common/backend/utils/misc/guc/guc_sql.cpp | 11 + .../utils/misc/postgresql_single.conf.sample | 1 + .../knl/knl_guc/knl_session_attr_sql.h | 1 + src/include/parser/analyze.h | 18 + src/test/regress/expected/parse_fusion.out | 388 +++++++++++ src/test/regress/parallel_schedule0A | 2 +- src/test/regress/sql/parse_fusion.sql | 223 +++++++ 9 files changed, 1180 insertions(+), 81 deletions(-) create mode 100644 src/test/regress/expected/parse_fusion.out create mode 100644 src/test/regress/sql/parse_fusion.sql diff --git a/src/bin/gs_guc/cluster_guc.conf b/src/bin/gs_guc/cluster_guc.conf index 4554287d6..da95a6a3f 100755 --- a/src/bin/gs_guc/cluster_guc.conf +++ b/src/bin/gs_guc/cluster_guc.conf @@ -22,6 +22,7 @@ # message: only print relation parameter informaton # [coordinator/datanode] +enable_parse_fusion|bool|0,0|NULL|Enable parse fusion feature.| bm25_k1|real|0,3|NULL|NULL| bm25_b|real|0,1|NULL|NULL| max_score_ratio|real|0.5,1.3|NULL|NULL| diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index bf2f9abef..7b98da2cc 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -35,6 +35,7 @@ #include "catalog/pgxc_class.h" #include "catalog/indexing.h" #include "catalog/namespace.h" +#include "utils/date.h" #include "utils/fmgroids.h" #include "utils/snapmgr.h" #endif @@ -147,6 +148,17 @@ static void set_ancestor_ps_contain_foreigntbl(ParseState* subParseState); static bool include_groupingset(Node* groupClause); static void transformGroupConstToColumn(ParseState* pstate, Node* groupClause, List* targetList); static bool checkAllowedTableCombination(ParseState* pstate); + +static bool IsValuesCanTransformDirectly(ParseState* pstate, InsertStmt* stmt); +static List* GetTargetColumnAttrs(ParseState* pstate, List* cols, int exprCnt); +static void GenerateTargetList(Query* query, RangeTblEntry* rte, int rtindex, List* targetColsAttrs); +static void CheckInsertTargetRelation(ParseState* pstate, InsertStmt* stmt, Relation targetrel, bool isRelationNullOk); +static void CheckColumnExprsConsistency(ParseState* pstate, List* cols, List* firstRowList, int finalTargetCnt); +static void GetColumnTypeAttrs(List* targetColsAttrs, ColumnTypeForm* typeItems); +static Node* TransformAconstToTarget(A_Const* con, ColumnTypeForm& typeItem, bool hasIgnore); +static List* TransformAllValuesDirectly(ParseState* pstate, SelectStmt* selectStmt, List* targetColsAttrs); +static Query* TryTransformInsertDirectly(ParseState* pstate, InsertStmt* stmt); + #ifdef ENABLE_MULTIPLE_NODES static bool ContainSubLinkWalker(Node* node, void* context); static bool ContainSubLink(Node* clause); @@ -585,10 +597,14 @@ Query* transformStmt(ParseState* pstate, Node* parseTree, bool isFirstNode, bool /* * Optimizable statements */ - case T_InsertStmt: - result = transformInsertStmt(pstate, (InsertStmt*)parseTree); + case T_InsertStmt: { + InsertStmt* insertStmt = (InsertStmt*)parseTree; + result = TryTransformInsertDirectly(pstate, insertStmt); + if (!result) { + result = transformInsertStmt(pstate, insertStmt); + } break; - + } case T_DeleteStmt: result = transformDeleteStmt(pstate, (DeleteStmt*)parseTree); break; @@ -1908,14 +1924,8 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt) qry->resultRelations = setTargetTables(pstate, list_make1(stmt->relation), false, false, targetPerms); targetrel = (Relation)linitial(pstate->p_target_relation); - /* - * Insert into relation pg_auth_history is not allowed. - * We update it only when some user's password has been changed. - */ - if (targetrel != NULL && RelationGetRelid(targetrel) == AuthHistoryRelationId) { - ereport(ERROR, - (errcode(ERRCODE_INVALID_OPERATION), errmsg("Not allowed to insert into relation pg_auth_history."))); - } + + CheckInsertTargetRelation(pstate, stmt, targetrel, true); if (qry->is_dist_insertselect) { Oid relid = RelationGetRelid(targetrel); @@ -1929,75 +1939,6 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt) ReleaseSysCache(classtup); } - - if (targetrel != NULL && - ((unsigned int)RelationGetInternalMask(targetrel) & INTERNAL_MASK_DINSERT)) { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("Un-support feature"), - errdetail("internal relation doesn't allow INSERT"))); - } -#ifdef ENABLE_MULTIPLE_NODES - if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) { -#endif - if (targetrel != NULL - && RelationIsMatview(targetrel) && !stmt->isRewritten) { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("Unsupported feature"), - errdetail("Materialized view doesn't allow INSERT"))); - } -#ifdef ENABLE_MULTIPLE_NODES - } -#endif - - if (targetrel != NULL && stmt->upsertClause != NULL) { - /* non-supported upsert cases */ - if (!u_sess->attr.attr_sql.enable_upsert_to_merge && RelationIsColumnFormat(targetrel)) { - ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on column orientated table.")))); - } - - if (RelationIsForeignTable(targetrel) || RelationIsStream(targetrel)) { - ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on foreign table.")))); - } - - if (RelationIsView(targetrel)) { - ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on VIEW.")))); - } - - if (RelationIsContquery(targetrel)) { - ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on CONTQUERY.")))); - } - } - - /* non-supported IGNORE cases */ - if (pstate->p_has_ignore && targetrel != NULL) { - if (RelationIsColumnFormat(targetrel)) { - ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("IGNORE is not supported on INSERT column orientated table.")))); - } - } - - /* data redistribution for DFS table. - * check if the target relation is being redistributed(insert mode). - * For example, when table A is redistributing in session 1, operating table - * A with insert command in session 2, the table A can not be inserted data in session 2. - * - * We don't allow insert during online expansion when expansion is set as read only. - * We reply on gs_redis to ganurantee DFS table to be read only during online expansion - * so we don't need to double check if target table is DFS table here anymore. - */ - if (!u_sess->attr.attr_sql.enable_cluster_resize && targetrel != NULL && - RelationInClusterResizingWriteErrorMode(targetrel)) { - ereport(ERROR, - (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), - errmsg("%s is redistributing, please retry later.", targetrel->rd_rel->relname.data))); - } - /* Validate stmt->cols list, or build default list if no list given */ icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos); @@ -6231,3 +6172,518 @@ static bool checkAllowedTableCombination(ParseState* pstate) return !(has_ustore && has_else); } + +static bool IsValuesCanTransformDirectly(ParseState* pstate, InsertStmt* stmt) +{ + if (!u_sess->attr.attr_sql.enableParseFusion || + stmt->is_dist_insertselect) { + return false; + } + + if (stmt->returningList || stmt->withClause + || stmt->upsertClause || stmt->targetList) { + return false; + } + + SelectStmt* selectStmt = (SelectStmt*)stmt->selectStmt; + if (selectStmt == nullptr || selectStmt->valuesLists == NIL) { + return false; + } + + foreach_cell(cell, stmt->cols) { + ResTarget* col = (ResTarget*)lfirst(cell); + if (col->indirection) { + return false; + } + } + + const int firstCnt = list_length((List*)linitial(selectStmt->valuesLists)); + foreach_cell (rowCell, selectStmt->valuesLists) { + List* row = (List*)lfirst(rowCell); + int curCnt = list_length(row); + if (unlikely(curCnt != firstCnt)) { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("VALUES lists must all be the same length"), + parser_errposition(pstate, exprLocation((Node*)row)))); + } + + foreach_cell (conCell, row) { + Node* con = (Node*)lfirst(conCell); + if (!IsA(con, A_Const)) { + return false; + } + Value* val = &((A_Const*)con)->val; + switch (nodeTag(val)) { + case T_Integer: + case T_Float: + case T_String: + case T_Null: + break; + default: + return false; + } + } + } + + return true; +} + +static void CheckColumnExprsConsistency(ParseState* pstate, List* cols, List* firstRowList, int finalTargetCnt) +{ + const int firstRowCnt = list_length(firstRowList); + if (unlikely(firstRowCnt > finalTargetCnt)) { + Node* posNode = (Node*)list_nth(firstRowList, finalTargetCnt); + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("INSERT has more expressions than target columns"), + parser_errposition(pstate, exprLocation(posNode)))); + } else if (unlikely(cols && firstRowCnt < finalTargetCnt)) { + Node* posNode = (Node*)list_nth(cols, firstRowCnt); + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("INSERT has more target columns than expressions"), + parser_errposition(pstate, exprLocation(posNode)))); + } +} + +static List* GetTargetColumnAttrs(ParseState* pstate, List* cols, int exprCnt) +{ + List* targetColsAttrs = NIL; + Relation targetrel = (Relation)linitial(pstate->p_target_relation); + FormData_pg_attribute* attrArr = targetrel->rd_att->attrs; + + if (cols == NIL) { + const int numcol = RelationGetNumberOfAttributes(targetrel); + bool isBlockchainRel = targetrel->rd_isblockchain; + + for (int i = 0; i < numcol && i < exprCnt; i++) { + if (attrArr[i].attisdropped) { + continue; + } + /* If the hidden column in timeseries relation, skip it */ + if (TsRelWithImplDistColumn(attrArr, i) && + RelationIsTsStore(targetrel)) { + continue; + } + + if (isBlockchainRel && + strcmp(NameStr(attrArr[i].attname), "hash") == 0) { + continue; + } + targetColsAttrs = lappend(targetColsAttrs, &attrArr[i]); + } + } else { + /* obtains columns attributes */ + Bitmapset* wholecols = NULL; + foreach_cell(cell, cols) { + ResTarget* col = (ResTarget*)lfirst(cell); + char* name = col->name; + int attrno = attnameAttNum(targetrel, name, false); + if (attrno == InvalidAttrNumber) { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + name, RelationGetRelationName(targetrel)), + parser_errposition(pstate, col->location))); + } + + if (bms_is_member(attrno, wholecols)) { + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_COLUMN), + errmsg("column \"%s\" specified more than once", name), + parser_errposition(pstate, col->location))); + } + + wholecols = bms_add_member(wholecols, attrno); + targetColsAttrs = lappend(targetColsAttrs, &attrArr[attrno - 1]); + } + } + + return targetColsAttrs; +} + +static void GenerateTargetList(Query* query, RangeTblEntry* rte, int rtindex, List* targetColsAttrs) +{ + int varattno = 0; + + query->targetList = NIL; + foreach_cell(colCell, targetColsAttrs) { + FormData_pg_attribute* col = (FormData_pg_attribute*)lfirst(colCell); + varattno++; + + Var* expr = makeVar(rtindex, + varattno, + col->atttypid, /* type oid */ + col->atttypmod, /* type mode */ + InvalidOid, /* colcollation */ + 0); /* sublevels_up */ + + expr->location = -1; + + char* name = pstrdup(NameStr(col->attname)); + TargetEntry* entry = makeTargetEntry((Expr*)expr, col->attnum, name, false); + query->targetList = lappend(query->targetList, entry); + + rte->insertedCols = bms_add_member(rte->insertedCols, col->attnum - FirstLowInvalidHeapAttributeNumber); + } +} + +static void GetColumnTypeAttrs(List* targetColsAttrs, ColumnTypeForm* typeItems) +{ + HeapTuple tup = nullptr; + Form_pg_type typeStruct = nullptr; + int colIdx = 0; + + foreach_cell(attrCell, targetColsAttrs) { + FormData_pg_attribute* attr = (FormData_pg_attribute*)lfirst(attrCell); + ColumnTypeForm& typeItem = typeItems[colIdx]; + typeItem.originTypOid = attr->atttypid; + typeItem.baseTypOid = attr->atttypid; + + /* + * We loop to find the bottom base type in a stack of domains. + */ + for (;;) { + tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeItem.baseTypOid)); + if (unlikely(!HeapTupleIsValid(tup))) { + ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), + errmsg("cache lookup failed for type %u", typeItem.baseTypOid))); + } + + typeStruct = (Form_pg_type)GETSTRUCT(tup); + if (unlikely(typeStruct->typtype == TYPTYPE_DOMAIN)) { + typeItem.baseTypOid = typeStruct->typbasetype; + ReleaseSysCache(tup); + } else { + typeItem.typmod = attr->atttypmod; + typeItem.collid = typeStruct->typcollation; + typeItem.typlen = typeStruct->typlen; + typeItem.typbyval = typeStruct->typbyval; + typeItem.typinput = typeStruct->typinput; + typeItem.ioParam = getTypeIOParam(tup); + ReleaseSysCache(tup); + break; + } + } + + ++colIdx; + } +} + +Datum TransformCstringToTarget(ColumnTypeForm& typeItem, char* string, bool hasIgnore) +{ + switch (typeItem.typinput) { + case F_DATE_IN: + return input_date_in(string, hasIgnore); + case F_BPCHARIN: + return input_bpcharin(string, typeItem.ioParam, typeItem.typmod); + case F_VARCHARIN: + return input_varcharin(string, typeItem.ioParam, typeItem.typmod); + case F_TIMESTAMP_IN: + return input_timestamp_in(string, typeItem.ioParam, typeItem.typmod, hasIgnore); + default: + return OidInputFunctionCall(typeItem.typinput, + string, + typeItem.ioParam, + typeItem.typmod, + hasIgnore); + } +} + +static Datum ConvertIntValue(ColumnTypeForm& typeItem, int32 value, bool hasIgnore) +{ + switch (typeItem.baseTypOid) { + case INT4OID: + return Int32GetDatum(value); + case INT8OID: + return Int64GetDatum((int64)value); + case INT1OID: + if (unlikely(value < 0 || value > UCHAR_MAX)) { + if (hasIgnore) { + ereport(WARNING, (errmsg("tinyint out of range"))); + return UInt8GetDatum((uint8)(value < 0 ? 0 : UCHAR_MAX)); + } + ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("tinyint out of range"))); + } + return UInt8GetDatum((uint8)value); + case INT2OID: + if (unlikely(value < PG_INT16_MIN || value > PG_INT16_MAX)) { + if (hasIgnore) { + ereport(WARNING, (errmsg("smallint out of range"))); + return Int16GetDatum((int16)(value < PG_INT16_MIN ? PG_INT16_MIN : PG_INT16_MAX)); + } + ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("smallint out of range"))); + } + return Int16GetDatum((int16)value); + default: { + char* inputStr = (char*)palloc(MAX_INT32_LEN + 1); + pg_ltoa(value, inputStr); + return OidInputFunctionCall(typeItem.typinput, + inputStr, + typeItem.ioParam, + typeItem.typmod, + hasIgnore); + } + } +} + +static Node* TransformAconstToTarget(A_Const* con, ColumnTypeForm& typeItem, bool hasIgnore) +{ + Value* value = &con->val; + Const* newcon = makeNode(Const); + + newcon->consttype = typeItem.baseTypOid; + newcon->consttypmod = typeItem.typmod; + newcon->constcollid = typeItem.collid; + newcon->constlen = typeItem.typlen; + newcon->constbyval = typeItem.typbyval; + newcon->cursor_data.cur_dno = -1; + newcon->location = con->location; + + switch (nodeTag(value)) { + case T_Integer: { + int32 val = intVal(value); + newcon->constisnull = false; + newcon->constvalue = ConvertIntValue(typeItem, val, hasIgnore); + break; + } + case T_Float: + case T_String: { + char* inputStr = strVal(value); + newcon->constisnull = false; + newcon->constvalue = TransformCstringToTarget(typeItem, inputStr, hasIgnore); + break; + } + default: { + /* only T_Null tag can come here */ + Assert(IsA(value, Null)); + newcon->constisnull = true; + newcon->constvalue = 0; + break; + } + } + + /* If target is not a domain, return directly, + * else apply constraints. + */ + if (likely(typeItem.baseTypOid == typeItem.originTypOid)) { + return (Node*)newcon; + } else { + /* + * Now build the domain coercion node. This represents run-time checking + * of any constraints currently attached to the domain. This also ensures + * that the expression is properly labeled as to result type. + */ + CoerceToDomain* domain = makeNode(CoerceToDomain); + domain->arg = (Expr*)newcon; + domain->resulttype = typeItem.originTypOid; + domain->resulttypmod = -1; + domain->coercionformat = COERCE_IMPLICIT_CAST; + domain->location = con->location; + return (Node*)domain; + } +} + +static void CheckInsertTargetRelation(ParseState* pstate, InsertStmt* stmt, Relation targetrel, bool isRelationNullOk) +{ + if (unlikely(targetrel == NULL)) { + if (isRelationNullOk) { + return; + } + ereport(ERROR, (errmodule(MOD_OPT), + errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("targetrel is NULL unexpectedly"))); + } + + /* + * Insert into relation pg_auth_history is not allowed. + * We update it only when some user's password has been changed. + */ + if (unlikely(RelationGetRelid(targetrel) == AuthHistoryRelationId)) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_OPERATION), + errmsg("Not allowed to insert into relation pg_auth_history."))); + } + if (((unsigned int)RelationGetInternalMask(targetrel) & INTERNAL_MASK_DINSERT)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Un-support feature"), + errdetail("internal relation doesn't allow INSERT"))); + } + +#ifdef ENABLE_MULTIPLE_NODES + if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) { +#endif + if (unlikely(RelationIsMatview(targetrel) && !stmt->isRewritten)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unsupported feature"), + errdetail("Materialized view doesn't allow INSERT"))); + } +#ifdef ENABLE_MULTIPLE_NODES + } +#endif + + if (stmt->upsertClause != NULL) { + /* non-supported upsert cases */ + if (unlikely(!u_sess->attr.attr_sql.enable_upsert_to_merge && RelationIsColumnFormat(targetrel))) { + ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on column orientated table.")))); + } + + if (unlikely(RelationIsForeignTable(targetrel) || RelationIsStream(targetrel))) { + ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on foreign table.")))); + } + + if (unlikely(RelationIsView(targetrel))) { + ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on VIEW.")))); + } + + if (unlikely(RelationIsContquery(targetrel))) { + ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("INSERT ON DUPLICATE KEY UPDATE is not supported on CONTQUERY.")))); + } + } + + /* non-supported IGNORE cases */ + if (unlikely(pstate->p_has_ignore && RelationIsColumnFormat(targetrel))) { + ereport(ERROR, ((errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("IGNORE is not supported on INSERT column orientated table.")))); + } + + if (unlikely(!u_sess->attr.attr_sql.enable_cluster_resize && + RelationInClusterResizingWriteErrorMode(targetrel))) { + ereport(ERROR, (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), + errmsg("%s is redistributing, please retry later.", targetrel->rd_rel->relname.data))); + } +} + +static void ParseColumnErrorCallback(void* arg) +{ + ParseColumnCallbackState* pcbstate = (ParseColumnCallbackState*)arg; + if (geterrcode() != ERRCODE_QUERY_CANCELED) { + (void)parser_errposition(pcbstate->pstate, pcbstate->location); + } + + FormData_pg_attribute* attr = (FormData_pg_attribute*)lfirst(pcbstate->attrCell); + char* colname = pstrdup_ext(NameStr(attr->attname)); + if (colname != NULL && strcmp(colname, "?column?")) { + errcontext("referenced column: %s", colname); + } +} + +static List* TransformAllValuesDirectly(ParseState* pstate, SelectStmt* selectStmt, List* targetColsAttrs) +{ + List* allValuesList = nullptr; + bool hasIgnore = pstate->p_has_ignore; + const int colCnt = list_length(targetColsAttrs); + + ColumnTypeForm colTypeItems[colCnt]; + GetColumnTypeAttrs(targetColsAttrs, colTypeItems); + + /* init ParseColumnCallbackState */ + ParseColumnCallbackState pcbstate; + pcbstate.pstate = pstate; + pcbstate.location = 0; + pcbstate.attrCell = nullptr; + pcbstate.errcontext.callback = ParseColumnErrorCallback; + pcbstate.errcontext.arg = (void*)&pcbstate; + + foreach_cell (recordCell, selectStmt->valuesLists) { + List* record = (List*)lfirst(recordCell); + List* oneValues = nullptr; + ListCell* valCell = nullptr; + ListCell* attrCell = nullptr; + int colIdx = 0; + forboth(valCell, record, attrCell, targetColsAttrs) { + A_Const* acon = (A_Const*)lfirst(valCell); + + /* + * set up to point at the constant's text and the column name + * if the input routine throws an error. + */ + pcbstate.location = acon->location; + pcbstate.attrCell= attrCell; + pcbstate.errcontext.previous = t_thrd.log_cxt.error_context_stack; + t_thrd.log_cxt.error_context_stack = &pcbstate.errcontext; + + /* transform const value to target column type value */ + Node* result = TransformAconstToTarget(acon, colTypeItems[colIdx], hasIgnore); + oneValues = lappend(oneValues, result); + ++colIdx; + + /* Pop the error context stack */ + t_thrd.log_cxt.error_context_stack = pcbstate.errcontext.previous; + } + + allValuesList = lappend(allValuesList, oneValues); + } + + return allValuesList; +} + +static Query* TryTransformInsertDirectly(ParseState* pstate, InsertStmt* stmt) +{ + if (!IsValuesCanTransformDirectly(pstate, stmt)) { + return nullptr; + } + + pstate->p_is_insert = true; + pstate->p_has_ignore = stmt->hasIgnore; + + AclMode targetPerms = ACL_INSERT; + if (stmt->isReplace) { + targetPerms |= ACL_DELETE; + } + Query* query = makeNode(Query); + query->commandType = CMD_INSERT; + query->isReplace = stmt->isReplace; + query->is_dist_insertselect = false; + query->resultRelations = setTargetTables(pstate, list_make1(stmt->relation), false, false, targetPerms); + + Relation targetrel = (Relation)linitial(pstate->p_target_relation); + CheckInsertTargetRelation(pstate, stmt, targetrel, false); + + SelectStmt* selectStmt = (SelectStmt*)stmt->selectStmt; + const int exprCnt = list_length((List*)linitial(selectStmt->valuesLists)); + List* targetColsAttrs = GetTargetColumnAttrs(pstate, stmt->cols, exprCnt); + const int colCnt = list_length(targetColsAttrs); + CheckColumnExprsConsistency(pstate, stmt->cols, (List*)linitial(selectStmt->valuesLists), colCnt); + + List* allValuesList = NULL; + const Oid conCollaOId = GetCollationConnection(); + const int conEncoding = get_valid_charset_by_collation(conCollaOId); + const int dbEncoding = GetDatabaseEncoding(); + if (likely(conEncoding == dbEncoding)) { + allValuesList = TransformAllValuesDirectly(pstate, selectStmt, targetColsAttrs); + } else { + DB_ENCODING_SWITCH_TO(conEncoding); + allValuesList = TransformAllValuesDirectly(pstate, selectStmt, targetColsAttrs); + DB_ENCODING_SWITCH_BACK(dbEncoding); + } + + List* collations = NIL; + for (int i = 0; i < colCnt; i++) { + collations = lappend_oid(collations, InvalidOid); + } + + RangeTblEntry* rte = addRangeTableEntryForValues(pstate, allValuesList, collations, NULL, true); + const int ridx = list_length(pstate->p_rtable); + GenerateTargetList(query, rte, ridx, targetColsAttrs); + + query->rtable = pstate->p_rtable; + RangeTblRef* tblRef = makeNode(RangeTblRef); + tblRef->rtindex = ridx; + pstate->p_joinlist = lappend(pstate->p_joinlist, tblRef); + query->jointree = makeFromExpr(pstate->p_joinlist, NULL); + query->tdTruncCastStatus = pstate->tdTruncCastStatus; + query->hasIgnore = stmt->hasIgnore; + query->hintState = stmt->hintState; + query->hasSubLinks = false; + query->hasTargetSRFs = false; + + return query; +} diff --git a/src/common/backend/utils/misc/guc/guc_sql.cpp b/src/common/backend/utils/misc/guc/guc_sql.cpp index aa993e23c..cc77db69d 100755 --- a/src/common/backend/utils/misc/guc/guc_sql.cpp +++ b/src/common/backend/utils/misc/guc/guc_sql.cpp @@ -1491,6 +1491,17 @@ static void InitSqlConfigureNamesBool() NULL, NULL, NULL}, + {{"enable_parse_fusion", + PGC_USERSET, + NODE_ALL, + QUERY_TUNING_METHOD, + gettext_noop("Enable parse fusion feature."), + NULL}, + &u_sess->attr.attr_sql.enableParseFusion, + false, + NULL, + NULL, + NULL}, {{"enable_ai_stats", PGC_USERSET, NODE_ALL, diff --git a/src/common/backend/utils/misc/postgresql_single.conf.sample b/src/common/backend/utils/misc/postgresql_single.conf.sample index 8b0000d9d..5e2d65001 100644 --- a/src/common/backend/utils/misc/postgresql_single.conf.sample +++ b/src/common/backend/utils/misc/postgresql_single.conf.sample @@ -901,3 +901,4 @@ dolphin.nulls_minimal_policy = on # the inverse of the default configuration val #application_name = 'dn_master' #enable_nls = off #wal_file_preinit_threshold = 100 # Threshold for pre-initializing xlogs, in percentages. +#enable_parse_fusion = off # Enable parse fusion feature. diff --git a/src/include/knl/knl_guc/knl_session_attr_sql.h b/src/include/knl/knl_guc/knl_session_attr_sql.h index e6a4908ec..dbbe2cf00 100644 --- a/src/include/knl/knl_guc/knl_session_attr_sql.h +++ b/src/include/knl/knl_guc/knl_session_attr_sql.h @@ -232,6 +232,7 @@ typedef struct knl_session_attr_sql { /* Table skewness warning threshold, range from 0 to 1, 0 indicates feature disabled*/ double table_skewness_warning_threshold; bool enable_opfusion; + bool enableParseFusion; bool enable_beta_opfusion; bool enable_partition_opfusion; int opfusion_debug_mode; diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h index 33dbdd0a6..1a1713873 100644 --- a/src/include/parser/analyze.h +++ b/src/include/parser/analyze.h @@ -75,6 +75,24 @@ typedef struct OperatorPlusProcessContext { bool contain_joinExpr; } OperatorPlusProcessContext; +typedef struct ColumnTypeForm { + Oid originTypOid; + Oid baseTypOid; + int32 typmod; + Oid collid; + int typlen; + bool typbyval; + regproc typinput; + Oid ioParam; +} ColumnTypeForm; + +typedef struct ParseColumnCallbackState { + ParseState* pstate; + int location; + ListCell* attrCell; + ErrorContextCallback errcontext; +} ParseColumnCallbackState; + typedef Query* (*transformSelectStmtHook)(ParseState* pstate, SelectStmt* stmt, bool isFirstNode, bool isCreateView); typedef struct AnalyzerRoutine { diff --git a/src/test/regress/expected/parse_fusion.out b/src/test/regress/expected/parse_fusion.out new file mode 100644 index 000000000..4bc18a82e --- /dev/null +++ b/src/test/regress/expected/parse_fusion.out @@ -0,0 +1,388 @@ +set enable_parse_fusion to on; +-- test ints +drop table if exists perf_pf_t; +NOTICE: table "perf_pf_t" does not exist, skipping +create table perf_pf_t( + c1 int, + c2 tinyint, + c3 smallint, + c4 integer, + c5 bigint, + c6 binary_integer +); +insert into perf_pf_t values (32767, 127, 32000, 2147483647, 9223372036854775807, 100); +insert into perf_pf_t(c6, c5, c4, c3, c2, c1) values (50, 999999999, 100, 30000, 100, -200); +insert into perf_pf_t(c1) values (null); +insert into perf_pf_t(c1) values (2147483648); -- should error +ERROR: value "2147483648" is out of range for type integer +LINE 1: insert into perf_pf_t(c1) values (2147483648); + ^ +CONTEXT: referenced column: c1 +insert into perf_pf_t(c2) values (300); -- should error +ERROR: tinyint out of range +LINE 1: insert into perf_pf_t(c2) values (300); + ^ +CONTEXT: referenced column: c2 +select * from perf_pf_t; + c1 | c2 | c3 | c4 | c5 | c6 +-------+-----+-------+------------+---------------------+----- + 32767 | 127 | 32000 | 2147483647 | 9223372036854775807 | 100 + -200 | 100 | 30000 | 100 | 999999999 | 50 + | | | | | +(3 rows) + +-- test floats +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 numeric(5,2), + c2 decimal(10,3), + c3 dec, + c4 double precision, + c5 float8, + c6 float +); +insert into perf_pf_t values (123.45, 99999.999, 3.14, 1.2345e100, -5.678e20, 0.5); +insert into perf_pf_t(c6, c5, c4, c3, c2, c1) values (0.99, 2.718e30, 9.876e50, 2.718, 12345.678, 99.99); +insert into perf_pf_t(c1) values (null); +insert into perf_pf_t(c1) values (100000.55); -- should error +ERROR: numeric field overflow +LINE 1: insert into perf_pf_t(c1) values (100000.55); + ^ +DETAIL: A field with precision 5, scale 2 must round to an absolute value less than 10^3. +CONTEXT: referenced column: c1 +insert into perf_pf_t(c2) values ('invalid'); -- should error +ERROR: invalid input syntax for type numeric: "invalid" +LINE 1: insert into perf_pf_t(c2) values ('invalid'); + ^ +CONTEXT: referenced column: c2 +select * from perf_pf_t; + c1 | c2 | c3 | c4 | c5 | c6 +--------+-----------+-------+-------------+------------+----- + 123.45 | 99999.999 | 3.14 | 1.2345e+100 | -5.678e+20 | .5 + 99.99 | 12345.678 | 2.718 | 9.876e+50 | 2.718e+30 | .99 + | | | | | +(3 rows) + +-- test strings +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 char(30), + c2 varchar(30), + c3 varchar2(30), + c4 nchar(30), + c5 nvarchar(30), + c6 text +); +insert into perf_pf_t values ('Hello', 'World', 'open', '测试', 'openGauss', 'verygood'); +insert into perf_pf_t(c6, c5, c4, c3, c2, c1) values ('gooood', 'good', '数据', 'Example', 'Test', 'ABC'); +insert into perf_pf_t(c1) values (null); +insert into perf_pf_t(c1) values ('This string is way too long for a char(30) field'); -- should error +ERROR: value too long for type character(30) +LINE 1: insert into perf_pf_t(c1) values ('This string is way too lo... + ^ +CONTEXT: referenced column: c1 +insert into perf_pf_t(c2) values (decode('deadbeef', 'hex')); -- should error +select * from perf_pf_t; + c1 | c2 | c3 | c4 | c5 | c6 +--------------------------------+------------+---------+------------------------------+-----------+---------- + Hello | World | open | 测试 | openGauss | verygood + ABC | Test | Example | 数据 | good | gooood + | | | | | + | \xdeadbeef | | | | +(4 rows) + +-- test times +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 date, + c2 time, + c3 timestamp, + c4 smalldatetime, + c5 reltime, + c6 abstime +); +insert into perf_pf_t values +('2023-10-01', '12:34:56', '2023-10-01 12:34:56', '2023-10-01 12:35', '3 days', '2023-10-01 12:34:56'); +insert into perf_pf_t(c6, c5, c4, c3, c2, c1) values +('2024-01-01 00:00:00', '1 month', '2023-12-31 23:59', '2023-12-31 23:59:59', '23:59:59', '2023-12-31'); +insert into perf_pf_t(c1) values (null); +insert into perf_pf_t(c1) values ('2023-13-01'); -- should error +ERROR: date/time field value out of range: "2023-13-01" +LINE 1: insert into perf_pf_t(c1) values ('2023-13-01'); + ^ +HINT: Perhaps you need a different "datestyle" setting. +CONTEXT: referenced column: c1 +insert into perf_pf_t(c2) values ('25:61:61'); -- should error +ERROR: date/time field value out of range: "25:61:61" +LINE 1: insert into perf_pf_t(c2) values ('25:61:61'); + ^ +CONTEXT: referenced column: c2 +select * from perf_pf_t; + c1 | c2 | c3 | c4 | c5 | c6 +--------------------------+----------+--------------------------+--------------------------+----------+------------------------------ + Sun Oct 01 00:00:00 2023 | 12:34:56 | Sun Oct 01 12:34:56 2023 | Sun Oct 01 12:35:00 2023 | @ 3 days | Sun Oct 01 12:34:56 2023 PDT + Sun Dec 31 00:00:00 2023 | 23:59:59 | Sun Dec 31 23:59:59 2023 | Sun Dec 31 23:59:00 2023 | @ 1 mon | Mon Jan 01 00:00:00 2024 PST + | | | | | +(3 rows) + +-- test intervals +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 interval year, + c2 interval month, + c3 interval day, + c4 interval hour, + c5 interval minute, + c6 interval second +); +insert into perf_pf_t values +('5 years', '12 months', '30 days', '23 hours', '59 minutes', '59 seconds'); +insert into perf_pf_t(c6, c5, c4, c3, c2, c1) values +('100 seconds', '60 minutes', '24 hours', '100 days', '13 months', '100 years'); +insert into perf_pf_t(c1) values (null); +insert into perf_pf_t(c1) values ('1 year 13 months'); -- should error +insert into perf_pf_t(c5) values ('60 minutes'); -- should error +select * from perf_pf_t; + c1 | c2 | c3 | c4 | c5 | c6 +-------------+----------------+------------+------------+-----------+----------------- + @ 5 years | @ 1 year | @ 30 days | @ 23 hours | @ 59 mins | @ 59 secs + @ 100 years | @ 1 year 1 mon | @ 100 days | @ 1 day | @ 1 hour | @ 1 min 40 secs + | | | | | + @ 2 years | | | | | + | | | | @ 1 hour | +(5 rows) + +-- test other types +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 boolean, + c2 money, + c3 cidr, + c4 inet, + c5 macaddr, + c6 uuid +); +insert into perf_pf_t values +(true, 123.45, '192.168.1.0/24', '192.168.1.1', '08:00:2b:01:02:03', 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'); +insert into perf_pf_t(c6, c5, c4, c3, c2, c1) values +('550e8400-e29b-41d4-a716-446655440000', '00:1a:2b:3c:4d:5e', '10.0.0.1', '10.0.0.0/8', 987.65, false); +insert into perf_pf_t(c1) values (null); +insert into perf_pf_t(c1) values ('yes'); -- should error +insert into perf_pf_t(c3) values ('invalid_cidr'); -- should error +ERROR: invalid input syntax for type cidr: "invalid_cidr" +LINE 1: insert into perf_pf_t(c3) values ('invalid_cidr'); + ^ +CONTEXT: referenced column: c3 +select * from perf_pf_t; + c1 | c2 | c3 | c4 | c5 | c6 +----+---------+----------------+-------------+-------------------+-------------------------------------- + t | $123.45 | 192.168.1.0/24 | 192.168.1.1 | 08:00:2b:01:02:03 | a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 + f | $987.65 | 10.0.0.0/8 | 10.0.0.1 | 00:1a:2b:3c:4d:5e | 550e8400-e29b-41d4-a716-446655440000 + | | | | | + t | | | | | +(4 rows) + +-- test unsatisfy with other clauses +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 int, + c2 tinyint, + c3 smallint +); +create unique index on perf_pf_t(c1); +insert into perf_pf_t values (12, 1, 12) + on duplicate key update c2=values(c2), c3=values(c3); +select * from perf_pf_t; + c1 | c2 | c3 +----+----+---- + 12 | 1 | 12 +(1 row) + +-- test unsatisfy with complex columns +drop table if exists perf_pf_t; +drop type if exists compfoo cascade; +NOTICE: type "compfoo" does not exist, skipping +create type compfoo AS (f1 int, f2 text); +create table perf_pf_t( + a int, + b compfoo, + c varbit(20) +); +insert into perf_pf_t(a, b) values(1,'(1,demo)'); +insert into perf_pf_t(a, b) values(1,(1,'demo')); -- RowExpr, not const, original path +insert into perf_pf_t(a, b.f1, b.f2) values(1, 1, 'demo'); -- FieldStore, indirection, original path +insert into perf_pf_t(a, c) values (1, b'010101'); -- original path +select * from perf_pf_t; + a | b | c +---+----------+-------- + 1 | (1,demo) | + 1 | (1,demo) | + 1 | (1,demo) | + 1 | | 010101 +(4 rows) + +-- test error +insert into perf_pf_t(a, not_exist) values(1, null); +ERROR: column "not_exist" of relation "perf_pf_t" does not exist +LINE 1: insert into perf_pf_t(a, not_exist) values(1, null); + ^ +insert into perf_pf_t(a, a) values(1, 1); +ERROR: column "a" specified more than once +LINE 1: insert into perf_pf_t(a, a) values(1, 1); + ^ +insert into perf_pf_t(a, b) values(2); +ERROR: INSERT has more target columns than expressions +LINE 1: insert into perf_pf_t(a, b) values(2); + ^ +select * from perf_pf_t; + a | b | c +---+----------+-------- + 1 | (1,demo) | + 1 | (1,demo) | + 1 | (1,demo) | + 1 | | 010101 +(4 rows) + +-- test error2 +drop table if exists perf_pf_t; +create table perf_pf_t( + a int, + b int, + c int +); +insert into perf_pf_t values(1); -- ok +insert into perf_pf_t values(1), (1, 2); -- should error +ERROR: VALUES lists must all be the same length +LINE 1: insert into perf_pf_t values(1), (1, 2); + ^ +insert into perf_pf_t values(1, 2, 3, 4), (1, 2); -- should error +ERROR: VALUES lists must all be the same length +LINE 1: insert into perf_pf_t values(1, 2, 3, 4), (1, 2); + ^ +insert into perf_pf_t(a, b) values(1, 2, 3), (1, 2); -- should error +ERROR: VALUES lists must all be the same length +LINE 1: insert into perf_pf_t(a, b) values(1, 2, 3), (1, 2); + ^ +insert into perf_pf_t(a, b, c) values(1, 2); -- should error +ERROR: INSERT has more target columns than expressions +LINE 1: insert into perf_pf_t(a, b, c) values(1, 2); + ^ +insert into perf_pf_t(a, b, c) values(1, 2, 3, 4); -- should error +ERROR: INSERT has more expressions than target columns +LINE 1: insert into perf_pf_t(a, b, c) values(1, 2, 3, 4); + ^ +select * from perf_pf_t; + a | b | c +---+---+--- + 1 | | +(1 row) + +-- test error3 +insert into pg_auth_history values(1, 2); +ERROR: Not allowed to insert into relation pg_auth_history. +create materialized view perf_pf_v as select * from perf_pf_t; +insert into perf_pf_v values(1, 2, 3); +ERROR: Unsupported feature +DETAIL: Materialized view doesn't allow INSERT +select * from perf_pf_v; + a | b | c +---+---+--- + 1 | | +(1 row) + +drop materialized view perf_pf_v; +create view perf_pf_vn as select * from perf_pf_t; +insert into perf_pf_vn values(1, 2, 3); +select * from perf_pf_vn; + a | b | c +---+---+--- + 1 | | + 1 | 2 | 3 +(2 rows) + +drop view perf_pf_vn; +-- test error4 +drop table if exists perf_pf_t; +create table perf_pf_t( + a int, + b int, + c int +) with (orientation=column); +insert into perf_pf_t values (1, 2, 3); +insert /*+ ignore_error */ into perf_pf_t values (1, 2, 3); -- should error +select * from perf_pf_t; + a | b | c +---+---+--- + 1 | 2 | 3 + 1 | 2 | 3 +(2 rows) + +-- test error4 +alter table perf_pf_t set (append_mode=read_only); +drop table perf_pf_t; +alter table perf_pf_t set (append_mode=on); +ERROR: relation "perf_pf_t" does not exist +-- test unmatch types +drop table if exists perf_pf_t; +NOTICE: table "perf_pf_t" does not exist, skipping +create table perf_pf_t( + c1 int, + c2 tinyint, + c3 smallint, + c4 text +); +insert into perf_pf_t values('1', '2', '3', 4); +insert into perf_pf_t values (2, 258, 99999999, 'hello world'); +ERROR: tinyint out of range +LINE 1: insert into perf_pf_t values (2, 258, 99999999, 'hello world... + ^ +CONTEXT: referenced column: c2 +insert /*+ ignore_error */ into perf_pf_t values (2, 258, 99999999, 'hello world'); +ERROR: tinyint out of range +LINE 1: ...ert /*+ ignore_error */ into perf_pf_t values (2, 258, 99999... + ^ +CONTEXT: referenced column: c2 +select * from perf_pf_t; + c1 | c2 | c3 | c4 +----+----+----+---- + 1 | 2 | 3 | 4 +(1 row) + +-- test domain types +drop table if exists perf_pf_t cascade; +drop type if exists age cascade; +NOTICE: type "age" does not exist, skipping +CREATE DOMAIN age AS INTEGER + DEFAULT 18 + CHECK (VALUE >= 0 AND VALUE <= 120); +create table perf_pf_t( + a age +); +insert into perf_pf_t values(18); +insert into perf_pf_t values(-18); +ERROR: value for domain age violates check constraint "age_check" +insert into perf_pf_t values(200); +ERROR: value for domain age violates check constraint "age_check" +select * from perf_pf_t; + a +---- + 18 +(1 row) + +-- test unsatisfy with hex +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 int, + c2 text +); +insert into perf_pf_t values (1, '2'), (2, x'2a2a'); -- should ok +select * from perf_pf_t; + c1 | c2 +----+------------------ + 1 | 2 + 2 | 0010101000101010 +(2 rows) + +drop table if exists perf_pf_t cascade; +drop type if exists age cascade; +set enable_parse_fusion to off; diff --git a/src/test/regress/parallel_schedule0A b/src/test/regress/parallel_schedule0A index ee1e23c9b..f461bc5a2 100644 --- a/src/test/regress/parallel_schedule0A +++ b/src/test/regress/parallel_schedule0A @@ -235,7 +235,7 @@ test: single_node_random transactions_test autocommit_test test: prefixkey_index invisible_index test: hash_index_001 test: hash_index_002 -test: single_node_update +test: single_node_update parse_fusion #test single_node_namespace #test: single_node_prepared_xacts #test: single_node_delete diff --git a/src/test/regress/sql/parse_fusion.sql b/src/test/regress/sql/parse_fusion.sql new file mode 100644 index 000000000..aea8fcd45 --- /dev/null +++ b/src/test/regress/sql/parse_fusion.sql @@ -0,0 +1,223 @@ +set enable_parse_fusion to on; + +-- test ints +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 int, + c2 tinyint, + c3 smallint, + c4 integer, + c5 bigint, + c6 binary_integer +); +insert into perf_pf_t values (32767, 127, 32000, 2147483647, 9223372036854775807, 100); +insert into perf_pf_t(c6, c5, c4, c3, c2, c1) values (50, 999999999, 100, 30000, 100, -200); +insert into perf_pf_t(c1) values (null); +insert into perf_pf_t(c1) values (2147483648); -- should error +insert into perf_pf_t(c2) values (300); -- should error +select * from perf_pf_t; + +-- test floats +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 numeric(5,2), + c2 decimal(10,3), + c3 dec, + c4 double precision, + c5 float8, + c6 float +); +insert into perf_pf_t values (123.45, 99999.999, 3.14, 1.2345e100, -5.678e20, 0.5); +insert into perf_pf_t(c6, c5, c4, c3, c2, c1) values (0.99, 2.718e30, 9.876e50, 2.718, 12345.678, 99.99); +insert into perf_pf_t(c1) values (null); +insert into perf_pf_t(c1) values (100000.55); -- should error +insert into perf_pf_t(c2) values ('invalid'); -- should error +select * from perf_pf_t; + +-- test strings +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 char(30), + c2 varchar(30), + c3 varchar2(30), + c4 nchar(30), + c5 nvarchar(30), + c6 text +); +insert into perf_pf_t values ('Hello', 'World', 'open', '测试', 'openGauss', 'verygood'); +insert into perf_pf_t(c6, c5, c4, c3, c2, c1) values ('gooood', 'good', '数据', 'Example', 'Test', 'ABC'); +insert into perf_pf_t(c1) values (null); +insert into perf_pf_t(c1) values ('This string is way too long for a char(30) field'); -- should error +insert into perf_pf_t(c2) values (decode('deadbeef', 'hex')); -- should error +select * from perf_pf_t; + +-- test times +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 date, + c2 time, + c3 timestamp, + c4 smalldatetime, + c5 reltime, + c6 abstime +); +insert into perf_pf_t values +('2023-10-01', '12:34:56', '2023-10-01 12:34:56', '2023-10-01 12:35', '3 days', '2023-10-01 12:34:56'); +insert into perf_pf_t(c6, c5, c4, c3, c2, c1) values +('2024-01-01 00:00:00', '1 month', '2023-12-31 23:59', '2023-12-31 23:59:59', '23:59:59', '2023-12-31'); +insert into perf_pf_t(c1) values (null); +insert into perf_pf_t(c1) values ('2023-13-01'); -- should error +insert into perf_pf_t(c2) values ('25:61:61'); -- should error +select * from perf_pf_t; + +-- test intervals +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 interval year, + c2 interval month, + c3 interval day, + c4 interval hour, + c5 interval minute, + c6 interval second +); +insert into perf_pf_t values +('5 years', '12 months', '30 days', '23 hours', '59 minutes', '59 seconds'); +insert into perf_pf_t(c6, c5, c4, c3, c2, c1) values +('100 seconds', '60 minutes', '24 hours', '100 days', '13 months', '100 years'); +insert into perf_pf_t(c1) values (null); +insert into perf_pf_t(c1) values ('1 year 13 months'); -- should error +insert into perf_pf_t(c5) values ('60 minutes'); -- should error +select * from perf_pf_t; + +-- test other types +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 boolean, + c2 money, + c3 cidr, + c4 inet, + c5 macaddr, + c6 uuid +); +insert into perf_pf_t values +(true, 123.45, '192.168.1.0/24', '192.168.1.1', '08:00:2b:01:02:03', 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'); +insert into perf_pf_t(c6, c5, c4, c3, c2, c1) values +('550e8400-e29b-41d4-a716-446655440000', '00:1a:2b:3c:4d:5e', '10.0.0.1', '10.0.0.0/8', 987.65, false); +insert into perf_pf_t(c1) values (null); +insert into perf_pf_t(c1) values ('yes'); -- should error +insert into perf_pf_t(c3) values ('invalid_cidr'); -- should error +select * from perf_pf_t; + +-- test unsatisfy with other clauses +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 int, + c2 tinyint, + c3 smallint +); +create unique index on perf_pf_t(c1); +insert into perf_pf_t values (12, 1, 12) + on duplicate key update c2=values(c2), c3=values(c3); +select * from perf_pf_t; + +-- test unsatisfy with complex columns +drop table if exists perf_pf_t; +drop type if exists compfoo cascade; +create type compfoo AS (f1 int, f2 text); +create table perf_pf_t( + a int, + b compfoo, + c varbit(20) +); +insert into perf_pf_t(a, b) values(1,'(1,demo)'); +insert into perf_pf_t(a, b) values(1,(1,'demo')); -- RowExpr, not const, original path +insert into perf_pf_t(a, b.f1, b.f2) values(1, 1, 'demo'); -- FieldStore, indirection, original path +insert into perf_pf_t(a, c) values (1, b'010101'); -- original path +select * from perf_pf_t; + +-- test error +insert into perf_pf_t(a, not_exist) values(1, null); +insert into perf_pf_t(a, a) values(1, 1); +insert into perf_pf_t(a, b) values(2); +select * from perf_pf_t; + +-- test error2 +drop table if exists perf_pf_t; +create table perf_pf_t( + a int, + b int, + c int +); +insert into perf_pf_t values(1); -- ok +insert into perf_pf_t values(1), (1, 2); -- should error +insert into perf_pf_t values(1, 2, 3, 4), (1, 2); -- should error +insert into perf_pf_t(a, b) values(1, 2, 3), (1, 2); -- should error +insert into perf_pf_t(a, b, c) values(1, 2); -- should error +insert into perf_pf_t(a, b, c) values(1, 2, 3, 4); -- should error +select * from perf_pf_t; + +-- test error3 +insert into pg_auth_history values(1, 2); +create materialized view perf_pf_v as select * from perf_pf_t; +insert into perf_pf_v values(1, 2, 3); +select * from perf_pf_v; +drop materialized view perf_pf_v; +create view perf_pf_vn as select * from perf_pf_t; +insert into perf_pf_vn values(1, 2, 3); +select * from perf_pf_vn; +drop view perf_pf_vn; +-- test error4 +drop table if exists perf_pf_t; +create table perf_pf_t( + a int, + b int, + c int +) with (orientation=column); +insert into perf_pf_t values (1, 2, 3); +insert /*+ ignore_error */ into perf_pf_t values (1, 2, 3); -- should error +select * from perf_pf_t; + +-- test error4 +alter table perf_pf_t set (append_mode=read_only); +drop table perf_pf_t; +alter table perf_pf_t set (append_mode=on); + +-- test unmatch types +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 int, + c2 tinyint, + c3 smallint, + c4 text +); +insert into perf_pf_t values('1', '2', '3', 4); +insert into perf_pf_t values (2, 258, 99999999, 'hello world'); +insert /*+ ignore_error */ into perf_pf_t values (2, 258, 99999999, 'hello world'); +select * from perf_pf_t; + +-- test domain types +drop table if exists perf_pf_t cascade; +drop type if exists age cascade; +CREATE DOMAIN age AS INTEGER + DEFAULT 18 + CHECK (VALUE >= 0 AND VALUE <= 120); +create table perf_pf_t( + a age +); +insert into perf_pf_t values(18); +insert into perf_pf_t values(-18); +insert into perf_pf_t values(200); +select * from perf_pf_t; + +-- test unsatisfy with hex +drop table if exists perf_pf_t; +create table perf_pf_t( + c1 int, + c2 text +); +insert into perf_pf_t values (1, '2'), (2, x'2a2a'); -- should ok +select * from perf_pf_t; + +drop table if exists perf_pf_t cascade; +drop type if exists age cascade; +set enable_parse_fusion to off;