diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 27 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 14 | ||||
-rw-r--r-- | src/backend/parser/parse_agg.c | 11 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 6 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 3 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 19 | ||||
-rw-r--r-- | src/backend/parser/parse_utilcmd.c | 66 |
7 files changed, 141 insertions, 5 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index d6cdd166073..400558b552b 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -2287,6 +2287,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) RangeTblEntry *target_rte; ListCell *orig_tl; ListCell *tl; + TupleDesc tupdesc = pstate->p_target_relation->rd_att; tlist = transformTargetList(pstate, origTlist, EXPR_KIND_UPDATE_SOURCE); @@ -2345,6 +2346,32 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) if (orig_tl != NULL) elog(ERROR, "UPDATE target count mismatch --- internal error"); + /* + * Record in extraUpdatedCols generated columns referencing updated base + * columns. + */ + if (tupdesc->constr && + tupdesc->constr->has_generated_stored) + { + for (int i = 0; i < tupdesc->constr->num_defval; i++) + { + AttrDefault defval = tupdesc->constr->defval[i]; + Node *expr; + Bitmapset *attrs_used = NULL; + + /* skip if not generated column */ + if (!TupleDescAttr(tupdesc, defval.adnum - 1)->attgenerated) + continue; + + expr = stringToNode(defval.adbin); + pull_varattnos(expr, 1, &attrs_used); + + if (bms_overlap(target_rte->updatedCols, attrs_used)) + target_rte->extraUpdatedCols = bms_add_member(target_rte->extraUpdatedCols, + defval.adnum - FirstLowInvalidHeapAttributeNumber); + } + } + return tlist; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 28f62de97e5..01521789e82 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -681,7 +681,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); SAVEPOINT SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P - START STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P + START STATEMENT STATISTICS STDIN STDOUT STORAGE STORED STRICT_P STRIP_P SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN @@ -3497,6 +3497,16 @@ ColConstraintElem: n->location = @1; $$ = (Node *)n; } + | GENERATED generated_when AS '(' a_expr ')' STORED + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_GENERATED; + n->generated_when = $2; + n->raw_expr = $5; + n->cooked_expr = NULL; + n->location = @1; + $$ = (Node *)n; + } | REFERENCES qualified_name opt_column_list key_match key_actions { Constraint *n = makeNode(Constraint); @@ -3587,6 +3597,7 @@ TableLikeOption: | CONSTRAINTS { $$ = CREATE_TABLE_LIKE_CONSTRAINTS; } | DEFAULTS { $$ = CREATE_TABLE_LIKE_DEFAULTS; } | IDENTITY_P { $$ = CREATE_TABLE_LIKE_IDENTITY; } + | GENERATED { $$ = CREATE_TABLE_LIKE_GENERATED; } | INDEXES { $$ = CREATE_TABLE_LIKE_INDEXES; } | STATISTICS { $$ = CREATE_TABLE_LIKE_STATISTICS; } | STORAGE { $$ = CREATE_TABLE_LIKE_STORAGE; } @@ -15234,6 +15245,7 @@ unreserved_keyword: | STDIN | STDOUT | STORAGE + | STORED | STRICT_P | STRIP_P | SUBSCRIPTION diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 183ea0f2c4a..c745fcdd2b7 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -520,6 +520,14 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr) err = _("grouping operations are not allowed in partition key expressions"); break; + case EXPR_KIND_GENERATED_COLUMN: + + if (isAgg) + err = _("aggregate functions are not allowed in column generation expressions"); + else + err = _("grouping operations are not allowed in column generation expressions"); + + break; case EXPR_KIND_CALL_ARGUMENT: if (isAgg) @@ -922,6 +930,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, case EXPR_KIND_COPY_WHERE: err = _("window functions are not allowed in COPY FROM WHERE conditions"); break; + case EXPR_KIND_GENERATED_COLUMN: + err = _("window functions are not allowed in column generation expressions"); + break; /* * There is intentionally no default: case here, so that the diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 3e648dc8ef9..20d923203f4 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -570,6 +570,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) case EXPR_KIND_PARTITION_EXPRESSION: case EXPR_KIND_CALL_ARGUMENT: case EXPR_KIND_COPY_WHERE: + case EXPR_KIND_GENERATED_COLUMN: /* okay */ break; @@ -1927,6 +1928,9 @@ transformSubLink(ParseState *pstate, SubLink *sublink) case EXPR_KIND_COPY_WHERE: err = _("cannot use subquery in COPY FROM WHERE condition"); break; + case EXPR_KIND_GENERATED_COLUMN: + err = _("cannot use subquery in column generation expression"); + break; /* * There is intentionally no default: case here, so that the @@ -3557,6 +3561,8 @@ ParseExprKindName(ParseExprKind exprKind) return "CALL"; case EXPR_KIND_COPY_WHERE: return "WHERE"; + case EXPR_KIND_GENERATED_COLUMN: + return "GENERATED AS"; /* * There is intentionally no default: case here, so that the diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index b8447771bd9..752cf1b315f 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -2526,6 +2526,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location) case EXPR_KIND_COPY_WHERE: err = _("set-returning functions are not allowed in COPY FROM WHERE conditions"); break; + case EXPR_KIND_GENERATED_COLUMN: + err = _("set-returning functions are not allowed in column generation expressions"); + break; /* * There is intentionally no default: case here, so that the diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index f3b6d193aa7..0640d11fac7 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -731,6 +731,17 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, const char *colname, colname), parser_errposition(pstate, location))); + /* + * In generated column, no system column is allowed except tableOid. + */ + if (pstate->p_expr_kind == EXPR_KIND_GENERATED_COLUMN && + attnum < InvalidAttrNumber && attnum != TableOidAttributeNumber) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("cannot use system column \"%s\" in column generation expression", + colname), + parser_errposition(pstate, location))); + if (attnum != InvalidAttrNumber) { /* now check to see if column actually is defined */ @@ -1257,6 +1268,7 @@ addRangeTableEntry(ParseState *pstate, rte->selectedCols = NULL; rte->insertedCols = NULL; rte->updatedCols = NULL; + rte->extraUpdatedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -1328,6 +1340,7 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->selectedCols = NULL; rte->insertedCols = NULL; rte->updatedCols = NULL; + rte->extraUpdatedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -1407,6 +1420,7 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->selectedCols = NULL; rte->insertedCols = NULL; rte->updatedCols = NULL; + rte->extraUpdatedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -1670,6 +1684,7 @@ addRangeTableEntryForFunction(ParseState *pstate, rte->selectedCols = NULL; rte->insertedCols = NULL; rte->updatedCols = NULL; + rte->extraUpdatedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -1733,6 +1748,7 @@ addRangeTableEntryForTableFunc(ParseState *pstate, rte->selectedCols = NULL; rte->insertedCols = NULL; rte->updatedCols = NULL; + rte->extraUpdatedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -1811,6 +1827,7 @@ addRangeTableEntryForValues(ParseState *pstate, rte->selectedCols = NULL; rte->insertedCols = NULL; rte->updatedCols = NULL; + rte->extraUpdatedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -1881,6 +1898,7 @@ addRangeTableEntryForJoin(ParseState *pstate, rte->selectedCols = NULL; rte->insertedCols = NULL; rte->updatedCols = NULL; + rte->extraUpdatedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -1983,6 +2001,7 @@ addRangeTableEntryForCTE(ParseState *pstate, rte->selectedCols = NULL; rte->insertedCols = NULL; rte->updatedCols = NULL; + rte->extraUpdatedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 8fdd3d785c7..674f4b98f40 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -502,6 +502,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) bool saw_nullable; bool saw_default; bool saw_identity; + bool saw_generated; ListCell *clist; cxt->columns = lappend(cxt->columns, column); @@ -609,6 +610,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) saw_nullable = false; saw_default = false; saw_identity = false; + saw_generated = false; foreach(clist, column->constraints) { @@ -689,6 +691,29 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) break; } + case CONSTR_GENERATED: + if (cxt->ofType) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("generated columns are not supported on typed tables"))); + if (cxt->partbound) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("generated columns are not supported on partitions"))); + + if (saw_generated) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("multiple generation clauses specified for column \"%s\" of table \"%s\"", + column->colname, cxt->relation->relname), + parser_errposition(cxt->pstate, + constraint->location))); + column->generated = ATTRIBUTE_GENERATED_STORED; + column->raw_default = constraint->raw_expr; + Assert(constraint->cooked_expr == NULL); + saw_generated = true; + break; + case CONSTR_CHECK: cxt->ckconstraints = lappend(cxt->ckconstraints, constraint); break; @@ -755,6 +780,22 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) column->colname, cxt->relation->relname), parser_errposition(cxt->pstate, constraint->location))); + + if (saw_default && saw_generated) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("both default and generation expression specified for column \"%s\" of table \"%s\"", + column->colname, cxt->relation->relname), + parser_errposition(cxt->pstate, + constraint->location))); + + if (saw_identity && saw_generated) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("both identity and generation expression specified for column \"%s\" of table \"%s\"", + column->colname, cxt->relation->relname), + parser_errposition(cxt->pstate, + constraint->location))); } /* @@ -983,11 +1024,13 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla * Copy default, if present and the default has been requested */ if (attribute->atthasdef && - (table_like_clause->options & CREATE_TABLE_LIKE_DEFAULTS)) + (table_like_clause->options & CREATE_TABLE_LIKE_DEFAULTS || + table_like_clause->options & CREATE_TABLE_LIKE_GENERATED)) { Node *this_default = NULL; AttrDefault *attrdef; int i; + bool found_whole_row; /* Find default in constraint structure */ Assert(constr != NULL); @@ -1002,12 +1045,27 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla } Assert(this_default != NULL); + def->cooked_default = map_variable_attnos(this_default, + 1, 0, + attmap, tupleDesc->natts, + InvalidOid, &found_whole_row); + /* - * If default expr could contain any vars, we'd need to fix 'em, - * but it can't; so default is ready to apply to child. + * Prevent this for the same reason as for constraints below. + * Note that defaults cannot contain any vars, so it's OK that the + * error message refers to generated columns. */ + if (found_whole_row) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert whole-row table reference"), + errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".", + attributeName, + RelationGetRelationName(relation)))); - def->cooked_default = this_default; + if (attribute->attgenerated && + (table_like_clause->options & CREATE_TABLE_LIKE_GENERATED)) + def->generated = attribute->attgenerated; } /* |