Compare commits

..

No commits in common. "9391f71523b6e57f1194d9f6543bc7948c16411b" and "007693f2a3ac2ac19affcb03ad43cdb36ccff5b5" have entirely different histories.

26 changed files with 157 additions and 876 deletions

View File

@ -675,14 +675,12 @@ DECLARE
<title>Copying Types</title>
<synopsis>
<replaceable>name</replaceable> <replaceable>table</replaceable>.<replaceable>column</replaceable>%TYPE
<replaceable>name</replaceable> <replaceable>variable</replaceable>%TYPE
<replaceable>variable</replaceable>%TYPE
</synopsis>
<para>
<literal>%TYPE</literal> provides the data type of a table column
or a previously-declared <application>PL/pgSQL</application>
variable. You can use this to declare variables that will hold
<literal>%TYPE</literal> provides the data type of a variable or
table column. You can use this to declare variables that will hold
database values. For example, let's say you have a column named
<literal>user_id</literal> in your <literal>users</literal>
table. To declare a variable with the same data type as
@ -692,21 +690,6 @@ user_id users.user_id%TYPE;
</programlisting>
</para>
<para>
It is also possible to write array decoration
after <literal>%TYPE</literal>, thereby creating a variable that holds
an array of the referenced type:
<programlisting>
user_ids users.user_id%TYPE[];
user_ids users.user_id%TYPE ARRAY[4]; -- equivalent to the above
</programlisting>
Just as when declaring table columns that are arrays, it doesn't
matter whether you write multiple bracket pairs or specific array
dimensions: <productname>PostgreSQL</productname> treats all arrays of
a given element type as the same type, regardless of dimensionality.
(See <xref linkend="arrays-declaration"/>.)
</para>
<para>
By using <literal>%TYPE</literal> you don't need to know the data
type of the structure you are referencing, and most importantly,
@ -756,12 +739,6 @@ user_ids users.user_id%TYPE ARRAY[4]; -- equivalent to the above
<literal>%ROWTYPE</literal> is more portable.)
</para>
<para>
As with <literal>%TYPE</literal>, <literal>%ROWTYPE</literal> can be
followed by array decoration to declare a variable that holds an array
of the referenced composite type.
</para>
<para>
Parameters to a function can be
composite types (complete table rows). In that case, the

View File

@ -46,7 +46,6 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET DEFAULT <replaceable class="parameter">expression</replaceable>
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> DROP DEFAULT
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> { SET | DROP } NOT NULL
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET EXPRESSION AS ( <replaceable class="parameter">expression</replaceable> )
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> DROP EXPRESSION [ IF EXISTS ]
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ]
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> { SET GENERATED { ALWAYS | BY DEFAULT } | SET <replaceable>sequence_option</replaceable> | RESTART [ [ WITH ] <replaceable class="parameter">restart</replaceable> ] } [...]
@ -257,17 +256,6 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
<varlistentry id="sql-altertable-desc-set-expression">
<term><literal>SET EXPRESSION AS</literal></term>
<listitem>
<para>
This form replaces the expression of a generated column. Existing data
in the column is rewritten and all the future changes will apply the new
generation expression.
</para>
</listitem>
</varlistentry>
<varlistentry id="sql-altertable-desc-drop-expression">
<term><literal>DROP EXPRESSION [ IF EXISTS ]</literal></term>
<listitem>

View File

@ -147,11 +147,10 @@ typedef enum AlterTablePass
AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
AT_PASS_DROP, /* DROP (all flavors) */
AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
AT_PASS_ADD_COL, /* ADD COLUMN */
AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
AT_PASS_OLD_INDEX, /* re-add existing indexes */
AT_PASS_OLD_CONSTR, /* re-add existing constraints */
/* We could support a RENAME COLUMN pass here, but not currently used */
AT_PASS_ADD_COL, /* ADD COLUMN */
AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
@ -460,8 +459,6 @@ static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
Node *def, LOCKMODE lockmode);
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
Node *newExpr, LOCKMODE lockmode);
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
@ -564,7 +561,7 @@ static void ATPrepAlterColumnType(List **wqueue,
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
AlterTableCmd *cmd, LOCKMODE lockmode);
static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
static void RememberAllDependentForRebuilding(AlteredTableInfo *tab,
Relation rel, AttrNumber attnum, const char *colName);
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
@ -4554,7 +4551,6 @@ AlterTableGetLockLevel(List *cmds)
case AT_AddIdentity:
case AT_DropIdentity:
case AT_SetIdentity:
case AT_SetExpression:
case AT_DropExpression:
case AT_SetCompression:
cmd_lockmode = AccessExclusiveLock;
@ -4856,11 +4852,6 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
pass = AT_PASS_COL_ATTRS;
break;
case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
pass = AT_PASS_SET_EXPRESSION;
break;
case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
@ -5162,11 +5153,11 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
lockmode, pass, context);
/*
* After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
* (this is not done in ATExecAlterColumnType since it should be
* done only once if multiple columns of a table are altered).
* After the ALTER TYPE pass, do cleanup work (this is not done in
* ATExecAlterColumnType since it should be done only once if
* multiple columns of a table are altered).
*/
if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
if (pass == AT_PASS_ALTER_TYPE)
ATPostAlterTypeCleanup(wqueue, tab, lockmode);
if (tab->rel)
@ -5245,9 +5236,6 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
case AT_SetAttNotNull: /* set pg_attribute.attnotnull */
address = ATExecSetAttNotNull(wqueue, rel, cmd->name, lockmode);
break;
case AT_SetExpression:
address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
break;
case AT_DropExpression:
address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
break;
@ -6375,8 +6363,6 @@ alter_table_type_to_string(AlterTableType cmdtype)
return "ALTER COLUMN ... SET NOT NULL";
case AT_SetAttNotNull:
return NULL; /* not real grammar */
case AT_SetExpression:
return "ALTER COLUMN ... SET EXPRESSION";
case AT_DropExpression:
return "ALTER COLUMN ... DROP EXPRESSION";
case AT_SetStatistics:
@ -8027,11 +8013,10 @@ ATExecColumnDefault(Relation rel, const char *colName,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("column \"%s\" of relation \"%s\" is a generated column",
colName, RelationGetRelationName(rel)),
newDefault ?
newDefault || TupleDescAttr(tupdesc, attnum - 1)->attgenerated != ATTRIBUTE_GENERATED_STORED ? 0 :
/* translator: %s is an SQL ALTER command */
errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
(TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
errhint("Use %s instead.",
"ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION")));
/*
* Remove any old default for the column. We use RESTRICT here for
@ -8328,121 +8313,6 @@ ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE
return address;
}
/*
* ALTER TABLE ALTER COLUMN SET EXPRESSION
*
* Return the address of the affected column.
*/
static ObjectAddress
ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
Node *newExpr, LOCKMODE lockmode)
{
HeapTuple tuple;
Form_pg_attribute attTup;
AttrNumber attnum;
Oid attrdefoid;
ObjectAddress address;
Expr *defval;
NewColumnValue *newval;
RawColumnDefault *rawEnt;
tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" of relation \"%s\" does not exist",
colName, RelationGetRelationName(rel))));
attTup = (Form_pg_attribute) GETSTRUCT(tuple);
attnum = attTup->attnum;
if (attnum <= 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot alter system column \"%s\"",
colName)));
if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("column \"%s\" of relation \"%s\" is not a generated column",
colName, RelationGetRelationName(rel))));
ReleaseSysCache(tuple);
/*
* Clear all the missing values if we're rewriting the table, since this
* renders them pointless.
*/
RelationClearMissing(rel);
/* make sure we don't conflict with later attribute modifications */
CommandCounterIncrement();
/*
* Find everything that depends on the column (constraints, indexes, etc),
* and record enough information to let us recreate the objects after
* rewrite.
*/
RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
/*
* Drop the dependency records of the GENERATED expression, in particular
* its INTERNAL dependency on the column, which would otherwise cause
* dependency.c to refuse to perform the deletion.
*/
attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
if (!OidIsValid(attrdefoid))
elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
RelationGetRelid(rel), attnum);
(void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
/* Make above changes visible */
CommandCounterIncrement();
/*
* Get rid of the GENERATED expression itself. We use RESTRICT here for
* safety, but at present we do not expect anything to depend on the
* expression.
*/
RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
false, false);
/* Prepare to store the new expression, in the catalogs */
rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
rawEnt->attnum = attnum;
rawEnt->raw_default = newExpr;
rawEnt->missingMode = false;
rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
/* Store the generated expression */
AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
false, true, false, NULL);
/* Make above new expression visible */
CommandCounterIncrement();
/* Prepare for table rewrite */
defval = (Expr *) build_column_default(rel, attnum);
newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
newval->attnum = attnum;
newval->expr = expression_planner(defval);
newval->is_generated = true;
tab->newvals = lappend(tab->newvals, newval);
tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
/* Drop any pg_statistic entry for the column */
RemoveStatistics(RelationGetRelid(rel), attnum);
InvokeObjectPostAlterHook(RelationRelationId,
RelationGetRelid(rel), attnum);
ObjectAddressSubSet(address, RelationRelationId,
RelationGetRelid(rel), attnum);
return address;
}
/*
* ALTER TABLE ALTER COLUMN DROP EXPRESSION
*/
@ -13430,7 +13300,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
* the info before executing ALTER TYPE, though, else the deparser will
* get confused.
*/
RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
RememberAllDependentForRebuilding(tab, rel, attnum, colName);
/*
* Now scan for dependencies of this column on other things. The only
@ -13627,21 +13497,18 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
}
/*
* Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
* that depends on the column (constraints, indexes, etc), and record enough
* information to let us recreate the objects.
* Subroutine for ATExecAlterColumnType: Find everything that depends on the
* column (constraints, indexes, etc), and record enough information to let us
* recreate the objects.
*/
static void
RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
Relation rel, AttrNumber attnum, const char *colName)
RememberAllDependentForRebuilding(AlteredTableInfo *tab, Relation rel, AttrNumber attnum, const char *colName)
{
Relation depRel;
ScanKeyData key[3];
SysScanDesc scan;
HeapTuple depTup;
Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
depRel = table_open(DependRelationId, RowExclusiveLock);
ScanKeyInit(&key[0],
@ -13705,7 +13572,6 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
case OCLASS_REWRITE:
/* XXX someday see if we can cope with revising views */
if (subtype == AT_AlterColumnType)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot alter type of a column used by a view or rule"),
@ -13725,7 +13591,6 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
* significant amount of new code. Since we can't easily tell
* which case applies, we punt for both. FIXME someday.
*/
if (subtype == AT_AlterColumnType)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot alter type of a column used in a trigger definition"),
@ -13744,7 +13609,6 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
* easy enough to remove and recreate the policy; still, FIXME
* someday.
*/
if (subtype == AT_AlterColumnType)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot alter type of a column used in a policy definition"),
@ -13770,12 +13634,11 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
/*
* This must be a reference from the expression of a
* generated column elsewhere in the same table.
* Changing the type/generated expression of a column
* that is used by a generated column is not allowed
* by SQL standard, so just punt for now. It might be
* doable with some thinking and effort.
* Changing the type of a column that is used by a
* generated column is not allowed by SQL standard, so
* just punt for now. It might be doable with some
* thinking and effort.
*/
if (subtype == AT_AlterColumnType)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot alter type of a column used by a generated column"),
@ -14000,11 +13863,11 @@ RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
}
/*
* Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
* operations for a particular relation. We have to drop and recreate all the
* indexes and constraints that depend on the altered columns. We do the
* actual dropping here, but re-creation is managed by adding work queue
* entries to do those steps later.
* Cleanup after we've finished all the ALTER TYPE operations for a
* particular relation. We have to drop and recreate all the indexes
* and constraints that depend on the altered columns. We do the
* actual dropping here, but re-creation is managed by adding work
* queue entries to do those steps later.
*/
static void
ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)

View File

@ -216,6 +216,7 @@ ExecInitQual(List *qual, PlanState *parent)
ExprState *state;
ExprEvalStep scratch = {0};
List *adjust_jumps = NIL;
ListCell *lc;
/* short-circuit (here and in ExecQual) for empty restriction list */
if (qual == NIL)
@ -249,8 +250,10 @@ ExecInitQual(List *qual, PlanState *parent)
scratch.resvalue = &state->resvalue;
scratch.resnull = &state->resnull;
foreach_ptr(Expr, node, qual)
foreach(lc, qual)
{
Expr *node = (Expr *) lfirst(lc);
/* first evaluate expression */
ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
@ -262,9 +265,9 @@ ExecInitQual(List *qual, PlanState *parent)
}
/* adjust jump targets */
foreach_int(jump, adjust_jumps)
foreach(lc, adjust_jumps)
{
ExprEvalStep *as = &state->steps[jump];
ExprEvalStep *as = &state->steps[lfirst_int(lc)];
Assert(as->opcode == EEOP_QUAL);
Assert(as->d.qualexpr.jumpdone == -1);

View File

@ -4579,16 +4579,12 @@ ExecEvalPreOrderedDistinctSingle(AggState *aggstate, AggStatePerTrans pertrans)
/*
* ExecEvalPreOrderedDistinctMulti
* Returns true when the aggregate input is distinct from the previous
* input and returns false when the input matches the previous input, or
* when there was no previous input.
* input and returns false when the input matches the previous input.
*/
bool
ExecEvalPreOrderedDistinctMulti(AggState *aggstate, AggStatePerTrans pertrans)
{
ExprContext *tmpcontext = aggstate->tmpcontext;
bool isdistinct = false; /* for now */
TupleTableSlot *save_outer;
TupleTableSlot *save_inner;
for (int i = 0; i < pertrans->numTransInputs; i++)
{
@ -4600,10 +4596,6 @@ ExecEvalPreOrderedDistinctMulti(AggState *aggstate, AggStatePerTrans pertrans)
pertrans->sortslot->tts_nvalid = pertrans->numInputs;
ExecStoreVirtualTuple(pertrans->sortslot);
/* save the previous slots before we overwrite them */
save_outer = tmpcontext->ecxt_outertuple;
save_inner = tmpcontext->ecxt_innertuple;
tmpcontext->ecxt_outertuple = pertrans->sortslot;
tmpcontext->ecxt_innertuple = pertrans->uniqslot;
@ -4615,15 +4607,9 @@ ExecEvalPreOrderedDistinctMulti(AggState *aggstate, AggStatePerTrans pertrans)
pertrans->haslast = true;
ExecCopySlot(pertrans->uniqslot, pertrans->sortslot);
isdistinct = true;
return true;
}
/* restore the original slots */
tmpcontext->ecxt_outertuple = save_outer;
tmpcontext->ecxt_innertuple = save_inner;
return isdistinct;
return false;
}
/*

View File

@ -1256,7 +1256,7 @@ cost_tidscan(Path *path, PlannerInfo *root,
QualCost qpqual_cost;
Cost cpu_per_tuple;
QualCost tid_qual_cost;
double ntuples;
int ntuples;
ListCell *l;
double spc_random_page_cost;
@ -1283,7 +1283,7 @@ cost_tidscan(Path *path, PlannerInfo *root,
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) qual;
Node *arraynode = (Node *) lsecond(saop->args);
ntuples += estimate_array_length(root, arraynode);
ntuples += estimate_array_length(arraynode);
}
else if (IsA(qual, CurrentOfExpr))
{
@ -4770,7 +4770,7 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
Node *arraynode = (Node *) lsecond(saop->args);
QualCost sacosts;
QualCost hcosts;
double estarraylen = estimate_array_length(context->root, arraynode);
int estarraylen = estimate_array_length(arraynode);
set_sa_opfuncid(saop);
sacosts.startup = sacosts.per_tuple = 0;
@ -4808,7 +4808,7 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
*/
context->total.startup += sacosts.startup;
context->total.per_tuple += sacosts.per_tuple *
estimate_array_length(context->root, arraynode) * 0.5;
estimate_array_length(arraynode) * 0.5;
}
}
else if (IsA(node, Aggref) ||
@ -4859,7 +4859,7 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
context->total.startup += perelemcost.startup;
if (perelemcost.per_tuple > 0)
context->total.per_tuple += perelemcost.per_tuple *
estimate_array_length(context->root, (Node *) acoerce->arg);
estimate_array_length((Node *) acoerce->arg);
}
else if (IsA(node, RowCompareExpr))
{

View File

@ -2417,16 +2417,6 @@ alter_table_cmd:
n->name = $3;
$$ = (Node *) n;
}
/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET EXPRESSION AS <expr> */
| ALTER opt_column ColId SET EXPRESSION AS '(' a_expr ')'
{
AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = AT_SetExpression;
n->name = $3;
n->def = $8;
$$ = (Node *) n;
}
/* ALTER TABLE <name> ALTER [COLUMN] <colname> DROP EXPRESSION */
| ALTER opt_column ColId DROP EXPRESSION
{

View File

@ -746,9 +746,11 @@ static Oid
FindUsableIndexForReplicaIdentityFull(Relation localrel, AttrMap *attrmap)
{
List *idxlist = RelationGetIndexList(localrel);
ListCell *lc;
foreach_oid(idxoid, idxlist)
foreach(lc, idxlist)
{
Oid idxoid = lfirst_oid(lc);
bool isUsableIdx;
Relation idxRel;
IndexInfo *idxInfo;

View File

@ -1036,11 +1036,11 @@ fetch_remote_table_info(char *nspname, char *relname,
/* Build the pubname list. */
initStringInfo(&pub_names);
foreach_node(String, pubstr, MySubscription->publications)
foreach(lc, MySubscription->publications)
{
char *pubname = strVal(pubstr);
char *pubname = strVal(lfirst(lc));
if (foreach_current_index(pubstr) > 0)
if (foreach_current_index(lc) > 0)
appendStringInfoString(&pub_names, ", ");
appendStringInfoString(&pub_names, quote_literal_cstr(pubname));

View File

@ -2234,6 +2234,7 @@ cleanup_rel_sync_cache(TransactionId xid, bool is_commit)
{
HASH_SEQ_STATUS hash_seq;
RelationSyncEntry *entry;
ListCell *lc;
Assert(RelationSyncCache != NULL);
@ -2246,15 +2247,15 @@ cleanup_rel_sync_cache(TransactionId xid, bool is_commit)
* corresponding schema and we don't need to send it unless there is
* any invalidation for that relation.
*/
foreach_xid(streamed_txn, entry->streamed_txns)
foreach(lc, entry->streamed_txns)
{
if (xid == streamed_txn)
if (xid == lfirst_xid(lc))
{
if (is_commit)
entry->schema_sent = true;
entry->streamed_txns =
foreach_delete_current(entry->streamed_txns, streamed_txn);
foreach_delete_current(entry->streamed_txns, lc);
break;
}
}

View File

@ -6340,7 +6340,7 @@ array_unnest_support(PG_FUNCTION_ARGS)
/* We can use estimated argument values here */
arg1 = estimate_expression_value(req->root, linitial(args));
req->rows = estimate_array_length(req->root, arg1);
req->rows = estimate_array_length(arg1);
ret = (Node *) req;
}
}

View File

@ -2128,11 +2128,10 @@ scalararraysel(PlannerInfo *root,
/*
* Estimate number of elements in the array yielded by an expression.
*
* Note: the result is integral, but we use "double" to avoid overflow
* concerns. Most callers will use it in double-type expressions anyway.
* It's important that this agree with scalararraysel.
*/
double
estimate_array_length(PlannerInfo *root, Node *arrayexpr)
int
estimate_array_length(Node *arrayexpr)
{
/* look through any binary-compatible relabeling of arrayexpr */
arrayexpr = strip_array_coercion(arrayexpr);
@ -2153,39 +2152,11 @@ estimate_array_length(PlannerInfo *root, Node *arrayexpr)
{
return list_length(((ArrayExpr *) arrayexpr)->elements);
}
else if (arrayexpr)
else
{
/* See if we can find any statistics about it */
VariableStatData vardata;
AttStatsSlot sslot;
double nelem = 0;
examine_variable(root, arrayexpr, 0, &vardata);
if (HeapTupleIsValid(vardata.statsTuple))
{
/*
* Found stats, so use the average element count, which is stored
* in the last stanumbers element of the DECHIST statistics.
* Actually that is the average count of *distinct* elements;
* perhaps we should scale it up somewhat?
*/
if (get_attstatsslot(&sslot, vardata.statsTuple,
STATISTIC_KIND_DECHIST, InvalidOid,
ATTSTATSSLOT_NUMBERS))
{
if (sslot.nnumbers > 0)
nelem = clamp_row_est(sslot.numbers[sslot.nnumbers - 1]);
free_attstatsslot(&sslot);
}
}
ReleaseVariableStats(vardata);
if (nelem > 0)
return nelem;
}
/* Else use a default guess --- this should match scalararraysel */
/* default guess --- see also scalararraysel */
return 10;
}
}
/*
@ -6569,7 +6540,7 @@ genericcostestimate(PlannerInfo *root,
if (IsA(rinfo->clause, ScalarArrayOpExpr))
{
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) rinfo->clause;
double alength = estimate_array_length(root, lsecond(saop->args));
int alength = estimate_array_length(lsecond(saop->args));
if (alength > 1)
num_sa_scans *= alength;
@ -6849,7 +6820,7 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
{
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
Node *other_operand = (Node *) lsecond(saop->args);
double alength = estimate_array_length(root, other_operand);
int alength = estimate_array_length(other_operand);
clause_op = saop->opno;
found_saop = true;
@ -7443,7 +7414,7 @@ gincost_scalararrayopexpr(PlannerInfo *root,
{
counts->exactEntries++;
counts->searchEntries++;
counts->arrayScans *= estimate_array_length(root, rightop);
counts->arrayScans *= estimate_array_length(rightop);
return true;
}

View File

@ -2504,7 +2504,7 @@ psql_completion(const char *text, int start, int end)
/* ALTER TABLE ALTER [COLUMN] <foo> SET */
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") ||
Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET"))
COMPLETE_WITH("(", "COMPRESSION", "DEFAULT", "EXPRESSION", "GENERATED", "NOT NULL", "STATISTICS", "STORAGE",
COMPLETE_WITH("(", "COMPRESSION", "DEFAULT", "GENERATED", "NOT NULL", "STATISTICS", "STORAGE",
/* a subset of ALTER SEQUENCE options */
"INCREMENT", "MINVALUE", "MAXVALUE", "START", "NO", "CACHE", "CYCLE");
/* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
@ -2515,14 +2515,6 @@ psql_completion(const char *text, int start, int end)
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSION") ||
Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSION"))
COMPLETE_WITH("DEFAULT", "PGLZ", "LZ4");
/* ALTER TABLE ALTER [COLUMN] <foo> SET EXPRESSION */
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "EXPRESSION") ||
Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "EXPRESSION"))
COMPLETE_WITH("AS");
/* ALTER TABLE ALTER [COLUMN] <foo> SET EXPRESSION AS */
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "EXPRESSION", "AS") ||
Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "EXPRESSION", "AS"))
COMPLETE_WITH("(");
/* ALTER TABLE ALTER [COLUMN] <foo> SET GENERATED */
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "GENERATED") ||
Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "GENERATED"))

View File

@ -2192,7 +2192,6 @@ typedef enum AlterTableType
AT_DropNotNull, /* alter column drop not null */
AT_SetNotNull, /* alter column set not null */
AT_SetAttNotNull, /* set attnotnull w/o a constraint */
AT_SetExpression, /* alter column set expression */
AT_DropExpression, /* alter column drop expression */
AT_SetStatistics, /* alter column set statistics */
AT_SetOptions, /* alter column set ( options ) */

View File

@ -381,26 +381,26 @@ lnext(const List *l, const ListCell *c)
/*
* foreach_delete_current -
* delete the current list element from the List associated with a
* surrounding foreach() or foreach_*() loop, returning the new List
* pointer; pass the name of the iterator variable.
* surrounding foreach() loop, returning the new List pointer.
*
* This is similar to list_delete_cell(), but it also adjusts the loop's state
* so that no list elements will be missed. Do not delete elements from an
* active foreach or foreach_* loop's list in any other way!
* This is equivalent to list_delete_cell(), but it also adjusts the foreach
* loop's state so that no list elements will be missed. Do not delete
* elements from an active foreach loop's list in any other way!
*/
#define foreach_delete_current(lst, var_or_cell) \
((List *) (var_or_cell##__state.l = list_delete_nth_cell(lst, var_or_cell##__state.i--)))
#define foreach_delete_current(lst, cell) \
(cell##__state.i--, \
(List *) (cell##__state.l = list_delete_cell(lst, cell)))
/*
* foreach_current_index -
* get the zero-based list index of a surrounding foreach() or foreach_*()
* loop's current element; pass the name of the iterator variable.
* get the zero-based list index of a surrounding foreach() loop's
* current element; pass the name of the "ListCell *" iterator variable.
*
* Beware of using this after foreach_delete_current(); the value will be
* out of sync for the rest of the current loop iteration. Anyway, since
* you just deleted the current element, the value is pretty meaningless.
*/
#define foreach_current_index(var_or_cell) (var_or_cell##__state.i)
#define foreach_current_index(cell) (cell##__state.i)
/*
* for_each_from -
@ -452,57 +452,6 @@ for_each_cell_setup(const List *lst, const ListCell *initcell)
return r;
}
/*
* Convenience macros that loop through a list without needing a separate
* "ListCell *" variable. Instead, the macros declare a locally-scoped loop
* variable with the provided name and the appropriate type.
*
* Since the variable is scoped to the loop, it's not possible to detect an
* early break by checking its value after the loop completes, as is common
* practice. If you need to do this, you can either use foreach() instead or
* manually track early breaks with a separate variable declared outside of the
* loop.
*
* Note that the caveats described in the comment above the foreach() macro
* also apply to these convenience macros.
*/
#define foreach_ptr(type, var, lst) foreach_internal(type, *, var, lst, lfirst)
#define foreach_int(var, lst) foreach_internal(int, , var, lst, lfirst_int)
#define foreach_oid(var, lst) foreach_internal(Oid, , var, lst, lfirst_oid)
#define foreach_xid(var, lst) foreach_internal(TransactionId, , var, lst, lfirst_xid)
/*
* The internal implementation of the above macros. Do not use directly.
*
* This macro actually generates two loops in order to declare two variables of
* different types. The outer loop only iterates once, so we expect optimizing
* compilers will unroll it, thereby optimizing it away.
*/
#define foreach_internal(type, pointer, var, lst, func) \
for (type pointer var = 0, pointer var##__outerloop = (type pointer) 1; \
var##__outerloop; \
var##__outerloop = 0) \
for (ForEachState var##__state = {(lst), 0}; \
(var##__state.l != NIL && \
var##__state.i < var##__state.l->length && \
(var = func(&var##__state.l->elements[var##__state.i]), true)); \
var##__state.i++)
/*
* foreach_node -
* The same as foreach_ptr, but asserts that the element is of the specified
* node type.
*/
#define foreach_node(type, var, lst) \
for (type * var = 0, *var##__outerloop = (type *) 1; \
var##__outerloop; \
var##__outerloop = 0) \
for (ForEachState var##__state = {(lst), 0}; \
(var##__state.l != NIL && \
var##__state.i < var##__state.l->length && \
(var = lfirst_node(type, &var##__state.l->elements[var##__state.i]), true)); \
var##__state.i++)
/*
* forboth -
* a convenience macro for advancing through two linked lists

View File

@ -200,7 +200,7 @@ extern Selectivity scalararraysel(PlannerInfo *root,
ScalarArrayOpExpr *clause,
bool is_join_clause,
int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo);
extern double estimate_array_length(PlannerInfo *root, Node *arrayexpr);
extern int estimate_array_length(Node *arrayexpr);
extern Selectivity rowcomparesel(PlannerInfo *root,
RowCompareExpr *clause,
int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo);

View File

@ -93,82 +93,3 @@ LINE 1: a.r[1] := 2
^
QUERY: a.r[1] := 2
CONTEXT: PL/pgSQL function inline_code_block line 2 at assignment
--
-- test of %type[] and %rowtype[] syntax
--
-- check supported syntax
do $$
declare
v int;
v1 v%type;
v2 v%type[];
v3 v%type[1];
v4 v%type[][];
v5 v%type[1][3];
v6 v%type array;
v7 v%type array[];
v8 v%type array[1];
v9 v%type array[1][1];
v10 pg_catalog.pg_class%rowtype[];
begin
raise notice '%', pg_typeof(v1);
raise notice '%', pg_typeof(v2);
raise notice '%', pg_typeof(v3);
raise notice '%', pg_typeof(v4);
raise notice '%', pg_typeof(v5);
raise notice '%', pg_typeof(v6);
raise notice '%', pg_typeof(v7);
raise notice '%', pg_typeof(v8);
raise notice '%', pg_typeof(v9);
raise notice '%', pg_typeof(v10);
end;
$$;
NOTICE: integer
NOTICE: integer[]
NOTICE: integer[]
NOTICE: integer[]
NOTICE: integer[]
NOTICE: integer[]
NOTICE: integer[]
NOTICE: integer[]
NOTICE: integer[]
NOTICE: pg_class[]
-- some types don't support arrays
do $$
declare
v pg_node_tree;
v1 v%type[];
begin
end;
$$;
ERROR: could not find array type for data type pg_node_tree
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 4
-- check functionality
do $$
declare
v1 int;
v2 varchar;
a1 v1%type[];
a2 v2%type[];
begin
v1 := 10;
v2 := 'Hi';
a1 := array[v1,v1];
a2 := array[v2,v2];
raise notice '% %', a1, a2;
end;
$$;
NOTICE: {10,10} {Hi,Hi}
create table array_test_table(a int, b varchar);
insert into array_test_table values(1, 'first'), (2, 'second');
do $$
declare tg array_test_table%rowtype[];
begin
tg := array(select array_test_table from array_test_table);
raise notice '%', tg;
tg := array(select row(a,b) from array_test_table);
raise notice '%', tg;
end;
$$;
NOTICE: {"(1,first)","(2,second)"}
NOTICE: {"(1,first)","(2,second)"}

View File

@ -2208,33 +2208,6 @@ build_datatype(HeapTuple typeTup, int32 typmod,
return typ;
}
/*
* Build an array type for the element type specified as argument.
*/
PLpgSQL_type *
plpgsql_build_datatype_arrayof(PLpgSQL_type *dtype)
{
Oid array_typeid;
/*
* If it's already an array type, use it as-is: Postgres doesn't do nested
* arrays.
*/
if (dtype->typisarray)
return dtype;
array_typeid = get_array_type(dtype->typoid);
if (!OidIsValid(array_typeid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(dtype->typoid))));
/* Note we inherit typmod and collation, if any, from the element type */
return plpgsql_build_datatype(array_typeid, dtype->atttypmod,
dtype->collation, NULL);
}
/*
* plpgsql_recognize_err_condition
* Check condition name and translate it to SQLSTATE.

View File

@ -757,9 +757,8 @@ decl_const :
decl_datatype :
{
/*
* If there's a lookahead token, read_datatype() will
* consume it, and then we must tell bison to forget
* it.
* If there's a lookahead token, read_datatype
* should consume it.
*/
$$ = read_datatype(yychar);
yyclearin;
@ -2784,17 +2783,13 @@ read_sql_construct(int until,
return expr;
}
/*
* Read a datatype declaration, consuming the current lookahead token if any.
* Returns a PLpgSQL_type struct.
*/
static PLpgSQL_type *
read_datatype(int tok)
{
StringInfoData ds;
char *type_name;
int startlocation;
PLpgSQL_type *result = NULL;
PLpgSQL_type *result;
int parenlevel = 0;
/* Should only be called while parsing DECLARE sections */
@ -2804,15 +2799,11 @@ read_datatype(int tok)
if (tok == YYEMPTY)
tok = yylex();
/* The current token is the start of what we'll pass to parse_datatype */
startlocation = yylloc;
/*
* If we have a simple or composite identifier, check for %TYPE and
* %ROWTYPE constructs. (Note that if plpgsql_parse_wordtype et al fail
* to recognize the identifier, we'll fall through and pass the whole
* string to parse_datatype, which will assuredly give an unhelpful
* "syntax error". Should we try to give a more specific error?)
* If we have a simple or composite identifier, check for %TYPE
* and %ROWTYPE constructs.
*/
if (tok == T_WORD)
{
@ -2824,10 +2815,18 @@ read_datatype(int tok)
tok = yylex();
if (tok_is_keyword(tok, &yylval,
K_TYPE, "type"))
{
result = plpgsql_parse_wordtype(dtname);
if (result)
return result;
}
else if (tok_is_keyword(tok, &yylval,
K_ROWTYPE, "rowtype"))
{
result = plpgsql_parse_wordrowtype(dtname);
if (result)
return result;
}
}
}
else if (plpgsql_token_is_unreserved_keyword(tok))
@ -2840,10 +2839,18 @@ read_datatype(int tok)
tok = yylex();
if (tok_is_keyword(tok, &yylval,
K_TYPE, "type"))
{
result = plpgsql_parse_wordtype(dtname);
if (result)
return result;
}
else if (tok_is_keyword(tok, &yylval,
K_ROWTYPE, "rowtype"))
{
result = plpgsql_parse_wordrowtype(dtname);
if (result)
return result;
}
}
}
else if (tok == T_CWORD)
@ -2856,56 +2863,21 @@ read_datatype(int tok)
tok = yylex();
if (tok_is_keyword(tok, &yylval,
K_TYPE, "type"))
{
result = plpgsql_parse_cwordtype(dtnames);
else if (tok_is_keyword(tok, &yylval,
K_ROWTYPE, "rowtype"))
result = plpgsql_parse_cwordrowtype(dtnames);
}
}
/*
* If we recognized a %TYPE or %ROWTYPE construct, see if it is followed
* by array decoration: [ ARRAY ] [ '[' [ iconst ] ']' [ ... ] ]
*
* Like the core parser, we ignore the specific numbers and sizes of
* dimensions; arrays of different dimensionality are still the same type
* in Postgres.
*/
if (result)
{
bool is_array = false;
tok = yylex();
if (tok_is_keyword(tok, &yylval,
K_ARRAY, "array"))
{
is_array = true;
tok = yylex();
}
while (tok == '[')
{
is_array = true;
tok = yylex();
if (tok == ICONST)
tok = yylex();
if (tok != ']')
yyerror("syntax error, expected \"]\"");
tok = yylex();
}
plpgsql_push_back_token(tok);
if (is_array)
result = plpgsql_build_datatype_arrayof(result);
return result;
}
else if (tok_is_keyword(tok, &yylval,
K_ROWTYPE, "rowtype"))
{
result = plpgsql_parse_cwordrowtype(dtnames);
if (result)
return result;
}
}
}
/*
* Not %TYPE or %ROWTYPE, so scan to the end of the datatype declaration,
* which could include typmod or array decoration. We are not very picky
* here, instead relying on parse_datatype to complain about garbage. But
* we must count parens to handle typmods within cursor_arg correctly.
*/
while (tok != ';')
{
if (tok == 0)

View File

@ -1249,7 +1249,6 @@ extern PLpgSQL_type *plpgsql_parse_cwordrowtype(List *idents);
extern PGDLLEXPORT PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod,
Oid collation,
TypeName *origtypname);
extern PLpgSQL_type *plpgsql_build_datatype_arrayof(PLpgSQL_type *dtype);
extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int lineno,
PLpgSQL_type *dtype,
bool add2namespace);

View File

@ -77,74 +77,3 @@ begin a[1] := 2; raise notice 'a = %', a; end$$;
do $$ declare a complex;
begin a.r[1] := 2; raise notice 'a = %', a; end$$;
--
-- test of %type[] and %rowtype[] syntax
--
-- check supported syntax
do $$
declare
v int;
v1 v%type;
v2 v%type[];
v3 v%type[1];
v4 v%type[][];
v5 v%type[1][3];
v6 v%type array;
v7 v%type array[];
v8 v%type array[1];
v9 v%type array[1][1];
v10 pg_catalog.pg_class%rowtype[];
begin
raise notice '%', pg_typeof(v1);
raise notice '%', pg_typeof(v2);
raise notice '%', pg_typeof(v3);
raise notice '%', pg_typeof(v4);
raise notice '%', pg_typeof(v5);
raise notice '%', pg_typeof(v6);
raise notice '%', pg_typeof(v7);
raise notice '%', pg_typeof(v8);
raise notice '%', pg_typeof(v9);
raise notice '%', pg_typeof(v10);
end;
$$;
-- some types don't support arrays
do $$
declare
v pg_node_tree;
v1 v%type[];
begin
end;
$$;
-- check functionality
do $$
declare
v1 int;
v2 varchar;
a1 v1%type[];
a2 v2%type[];
begin
v1 := 10;
v2 := 'Hi';
a1 := array[v1,v1];
a2 := array[v2,v2];
raise notice '% %', a1, a2;
end;
$$;
create table array_test_table(a int, b varchar);
insert into array_test_table values(1, 'first'), (2, 'second');
do $$
declare tg array_test_table%rowtype[];
begin
tg := array(select array_test_table from array_test_table);
raise notice '%', tg;
tg := array(select row(a,b) from array_test_table);
raise notice '%', tg;
end;
$$;

View File

@ -132,9 +132,6 @@ get_altertable_subcmdinfo(PG_FUNCTION_ARGS)
case AT_SetAttNotNull:
strtype = "SET ATTNOTNULL";
break;
case AT_SetExpression:
strtype = "SET EXPRESSION";
break;
case AT_DropExpression:
strtype = "DROP EXPRESSION";
break;

View File

@ -1694,19 +1694,6 @@ select aggfns(distinct a,b,c order by a,c using ~<~,b)
{"(0,,)","(1,3,foo)","(2,2,bar)","(3,1,baz)"}
(1 row)
-- test a more complex permutation that has previous caused issues
select
string_agg(distinct 'a', ','),
sum((
select sum(1)
from (values(1)) b(id)
where a.id = b.id
)) from unnest(array[1]) a(id);
string_agg | sum
------------+-----
a | 1
(1 row)
-- check node I/O via view creation and usage, also deparsing logic
create view agg_view1 as
select aggfns(a,b,c)

View File

@ -578,9 +578,6 @@ INSERT INTO gtest20 (a) VALUES (10); -- ok
INSERT INTO gtest20 (a) VALUES (30); -- violates constraint
ERROR: new row for relation "gtest20" violates check constraint "gtest20_b_check"
DETAIL: Failing row contains (30, 60).
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 100); -- violates constraint
ERROR: check constraint "gtest20_b_check" of relation "gtest20" is violated by some row
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 3); -- ok
CREATE TABLE gtest20a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED);
INSERT INTO gtest20a (a) VALUES (10);
INSERT INTO gtest20a (a) VALUES (30);
@ -676,47 +673,6 @@ SELECT * FROM gtest22c WHERE a = 1 AND b > 0;
1 | 2
(1 row)
ALTER TABLE gtest22c ALTER COLUMN b SET EXPRESSION AS (a * 4);
ANALYZE gtest22c;
EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE b = 8;
QUERY PLAN
---------------------------------------------
Index Scan using gtest22c_b_idx on gtest22c
Index Cond: (b = 8)
(2 rows)
SELECT * FROM gtest22c WHERE b = 8;
a | b
---+---
2 | 8
(1 row)
EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE b * 3 = 12;
QUERY PLAN
------------------------------------------------
Index Scan using gtest22c_expr_idx on gtest22c
Index Cond: ((b * 3) = 12)
(2 rows)
SELECT * FROM gtest22c WHERE b * 3 = 12;
a | b
---+---
1 | 4
(1 row)
EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE a = 1 AND b > 0;
QUERY PLAN
------------------------------------------------
Index Scan using gtest22c_pred_idx on gtest22c
Index Cond: (a = 1)
(2 rows)
SELECT * FROM gtest22c WHERE a = 1 AND b > 0;
a | b
---+---
1 | 4
(1 row)
RESET enable_seqscan;
RESET enable_bitmapscan;
-- foreign keys
@ -742,10 +698,6 @@ INSERT INTO gtest23b VALUES (1); -- ok
INSERT INTO gtest23b VALUES (5); -- error
ERROR: insert or update on table "gtest23b" violates foreign key constraint "gtest23b_b_fkey"
DETAIL: Key (b)=(10) is not present in table "gtest23a".
ALTER TABLE gtest23b ALTER COLUMN b SET EXPRESSION AS (a * 5); -- error
ERROR: insert or update on table "gtest23b" violates foreign key constraint "gtest23b_b_fkey"
DETAIL: Key (b)=(5) is not present in table "gtest23a".
ALTER TABLE gtest23b ALTER COLUMN b SET EXPRESSION AS (a * 1); -- ok
DROP TABLE gtest23b;
DROP TABLE gtest23a;
CREATE TABLE gtest23p (x int, y int GENERATED ALWAYS AS (x * 2) STORED, PRIMARY KEY (y));
@ -833,119 +785,30 @@ Partition of: gtest_parent FOR VALUES FROM ('08-01-2016') TO ('09-01-2016')
Partition of: gtest_parent FOR VALUES FROM ('09-01-2016') TO ('10-01-2016')
INSERT INTO gtest_parent (f1, f2) VALUES ('2016-07-15', 1);
INSERT INTO gtest_parent (f1, f2) VALUES ('2016-07-15', 2);
INSERT INTO gtest_parent (f1, f2) VALUES ('2016-08-15', 3);
SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
tableoid | f1 | f2 | f3
--------------+------------+----+----
gtest_child | 07-15-2016 | 1 | 2
gtest_child | 07-15-2016 | 2 | 4
gtest_child2 | 08-15-2016 | 3 | 66
(3 rows)
SELECT * FROM gtest_parent;
f1 | f2 | f3
------------+----+----
07-15-2016 | 1 | 2
(1 row)
UPDATE gtest_parent SET f1 = f1 + 60 WHERE f2 = 1;
SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
tableoid | f1 | f2 | f3
--------------+------------+----+----
gtest_child | 07-15-2016 | 2 | 4
gtest_child2 | 08-15-2016 | 3 | 66
gtest_child3 | 09-13-2016 | 1 | 33
(3 rows)
SELECT * FROM gtest_child;
f1 | f2 | f3
------------+----+----
07-15-2016 | 1 | 2
(1 row)
-- alter only parent's and one child's generation expression
ALTER TABLE ONLY gtest_parent ALTER COLUMN f3 SET EXPRESSION AS (f2 * 4);
ALTER TABLE gtest_child ALTER COLUMN f3 SET EXPRESSION AS (f2 * 10);
\d gtest_parent
Partitioned table "public.gtest_parent"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+-------------------------------------
f1 | date | | not null |
f2 | bigint | | |
f3 | bigint | | | generated always as (f2 * 4) stored
Partition key: RANGE (f1)
Number of partitions: 3 (Use \d+ to list them.)
UPDATE gtest_parent SET f1 = f1 + 60;
SELECT * FROM gtest_parent;
f1 | f2 | f3
------------+----+----
09-13-2016 | 1 | 33
(1 row)
\d gtest_child
Table "public.gtest_child"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+--------------------------------------
f1 | date | | not null |
f2 | bigint | | |
f3 | bigint | | | generated always as (f2 * 10) stored
Partition of: gtest_parent FOR VALUES FROM ('07-01-2016') TO ('08-01-2016')
\d gtest_child2
Table "public.gtest_child2"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+--------------------------------------
f1 | date | | not null |
f2 | bigint | | |
f3 | bigint | | | generated always as (f2 * 22) stored
Partition of: gtest_parent FOR VALUES FROM ('08-01-2016') TO ('09-01-2016')
\d gtest_child3
Table "public.gtest_child3"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+--------------------------------------
f1 | date | | not null |
f2 | bigint | | |
f3 | bigint | | | generated always as (f2 * 33) stored
Partition of: gtest_parent FOR VALUES FROM ('09-01-2016') TO ('10-01-2016')
SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
tableoid | f1 | f2 | f3
--------------+------------+----+----
gtest_child | 07-15-2016 | 2 | 20
gtest_child2 | 08-15-2016 | 3 | 66
gtest_child3 | 09-13-2016 | 1 | 33
(3 rows)
-- alter generation expression of parent and all its children altogether
ALTER TABLE gtest_parent ALTER COLUMN f3 SET EXPRESSION AS (f2 * 2);
\d gtest_parent
Partitioned table "public.gtest_parent"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+-------------------------------------
f1 | date | | not null |
f2 | bigint | | |
f3 | bigint | | | generated always as (f2 * 2) stored
Partition key: RANGE (f1)
Number of partitions: 3 (Use \d+ to list them.)
\d gtest_child
Table "public.gtest_child"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+-------------------------------------
f1 | date | | not null |
f2 | bigint | | |
f3 | bigint | | | generated always as (f2 * 2) stored
Partition of: gtest_parent FOR VALUES FROM ('07-01-2016') TO ('08-01-2016')
\d gtest_child2
Table "public.gtest_child2"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+-------------------------------------
f1 | date | | not null |
f2 | bigint | | |
f3 | bigint | | | generated always as (f2 * 2) stored
Partition of: gtest_parent FOR VALUES FROM ('08-01-2016') TO ('09-01-2016')
\d gtest_child3
Table "public.gtest_child3"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+-------------------------------------
f1 | date | | not null |
f2 | bigint | | |
f3 | bigint | | | generated always as (f2 * 2) stored
Partition of: gtest_parent FOR VALUES FROM ('09-01-2016') TO ('10-01-2016')
SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
tableoid | f1 | f2 | f3
--------------+------------+----+----
gtest_child | 07-15-2016 | 2 | 4
gtest_child2 | 08-15-2016 | 3 | 6
gtest_child3 | 09-13-2016 | 1 | 2
(3 rows)
SELECT * FROM gtest_child3;
f1 | f2 | f3
------------+----+----
09-13-2016 | 1 | 33
(1 row)
-- we leave these tables around for purposes of testing dump/reload/upgrade
-- generated columns in partition key (not allowed)
@ -962,7 +825,7 @@ DETAIL: Column "f3" is a generated column.
-- ALTER TABLE ... ADD COLUMN
CREATE TABLE gtest25 (a int PRIMARY KEY);
INSERT INTO gtest25 VALUES (3), (4);
ALTER TABLE gtest25 ADD COLUMN b int GENERATED ALWAYS AS (a * 2) STORED, ALTER COLUMN b SET EXPRESSION AS (a * 3);
ALTER TABLE gtest25 ADD COLUMN b int GENERATED ALWAYS AS (a * 3) STORED;
SELECT * FROM gtest25 ORDER BY a;
a | b
---+----
@ -1072,50 +935,18 @@ CREATE TABLE gtest29 (
b int GENERATED ALWAYS AS (a * 2) STORED
);
INSERT INTO gtest29 (a) VALUES (3), (4);
SELECT * FROM gtest29;
a | b
---+---
3 | 6
4 | 8
(2 rows)
\d gtest29
Table "public.gtest29"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+------------------------------------
a | integer | | |
b | integer | | | generated always as (a * 2) stored
ALTER TABLE gtest29 ALTER COLUMN a SET EXPRESSION AS (a * 3); -- error
ERROR: column "a" of relation "gtest29" is not a generated column
ALTER TABLE gtest29 ALTER COLUMN a DROP EXPRESSION; -- error
ERROR: column "a" of relation "gtest29" is not a stored generated column
ALTER TABLE gtest29 ALTER COLUMN a DROP EXPRESSION IF EXISTS; -- notice
NOTICE: column "a" of relation "gtest29" is not a stored generated column, skipping
-- Change the expression
ALTER TABLE gtest29 ALTER COLUMN b SET EXPRESSION AS (a * 3);
SELECT * FROM gtest29;
a | b
---+----
3 | 9
4 | 12
(2 rows)
\d gtest29
Table "public.gtest29"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+------------------------------------
a | integer | | |
b | integer | | | generated always as (a * 3) stored
ALTER TABLE gtest29 ALTER COLUMN b DROP EXPRESSION;
INSERT INTO gtest29 (a) VALUES (5);
INSERT INTO gtest29 (a, b) VALUES (6, 66);
SELECT * FROM gtest29;
a | b
---+----
3 | 9
4 | 12
3 | 6
4 | 8
5 |
6 | 66
(4 rows)

View File

@ -643,15 +643,6 @@ select aggfns(distinct a,b,c order by a,c using ~<~,b)
from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c),
generate_series(1,2) i;
-- test a more complex permutation that has previous caused issues
select
string_agg(distinct 'a', ','),
sum((
select sum(1)
from (values(1)) b(id)
where a.id = b.id
)) from unnest(array[1]) a(id);
-- check node I/O via view creation and usage, also deparsing logic
create view agg_view1 as

View File

@ -293,9 +293,6 @@ CREATE TABLE gtest20 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORE
INSERT INTO gtest20 (a) VALUES (10); -- ok
INSERT INTO gtest20 (a) VALUES (30); -- violates constraint
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 100); -- violates constraint
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 3); -- ok
CREATE TABLE gtest20a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STORED);
INSERT INTO gtest20a (a) VALUES (10);
INSERT INTO gtest20a (a) VALUES (30);
@ -344,15 +341,6 @@ EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE b * 3 = 6;
SELECT * FROM gtest22c WHERE b * 3 = 6;
EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE a = 1 AND b > 0;
SELECT * FROM gtest22c WHERE a = 1 AND b > 0;
ALTER TABLE gtest22c ALTER COLUMN b SET EXPRESSION AS (a * 4);
ANALYZE gtest22c;
EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE b = 8;
SELECT * FROM gtest22c WHERE b = 8;
EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE b * 3 = 12;
SELECT * FROM gtest22c WHERE b * 3 = 12;
EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE a = 1 AND b > 0;
SELECT * FROM gtest22c WHERE a = 1 AND b > 0;
RESET enable_seqscan;
RESET enable_bitmapscan;
@ -368,8 +356,6 @@ CREATE TABLE gtest23b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) STOR
INSERT INTO gtest23b VALUES (1); -- ok
INSERT INTO gtest23b VALUES (5); -- error
ALTER TABLE gtest23b ALTER COLUMN b SET EXPRESSION AS (a * 5); -- error
ALTER TABLE gtest23b ALTER COLUMN b SET EXPRESSION AS (a * 1); -- ok
DROP TABLE gtest23b;
DROP TABLE gtest23a;
@ -428,28 +414,11 @@ ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09
\d gtest_child2
\d gtest_child3
INSERT INTO gtest_parent (f1, f2) VALUES ('2016-07-15', 1);
INSERT INTO gtest_parent (f1, f2) VALUES ('2016-07-15', 2);
INSERT INTO gtest_parent (f1, f2) VALUES ('2016-08-15', 3);
SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
UPDATE gtest_parent SET f1 = f1 + 60 WHERE f2 = 1;
SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
-- alter only parent's and one child's generation expression
ALTER TABLE ONLY gtest_parent ALTER COLUMN f3 SET EXPRESSION AS (f2 * 4);
ALTER TABLE gtest_child ALTER COLUMN f3 SET EXPRESSION AS (f2 * 10);
\d gtest_parent
\d gtest_child
\d gtest_child2
\d gtest_child3
SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
-- alter generation expression of parent and all its children altogether
ALTER TABLE gtest_parent ALTER COLUMN f3 SET EXPRESSION AS (f2 * 2);
\d gtest_parent
\d gtest_child
\d gtest_child2
\d gtest_child3
SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
SELECT * FROM gtest_parent;
SELECT * FROM gtest_child;
UPDATE gtest_parent SET f1 = f1 + 60;
SELECT * FROM gtest_parent;
SELECT * FROM gtest_child3;
-- we leave these tables around for purposes of testing dump/reload/upgrade
-- generated columns in partition key (not allowed)
@ -459,7 +428,7 @@ CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED AL
-- ALTER TABLE ... ADD COLUMN
CREATE TABLE gtest25 (a int PRIMARY KEY);
INSERT INTO gtest25 VALUES (3), (4);
ALTER TABLE gtest25 ADD COLUMN b int GENERATED ALWAYS AS (a * 2) STORED, ALTER COLUMN b SET EXPRESSION AS (a * 3);
ALTER TABLE gtest25 ADD COLUMN b int GENERATED ALWAYS AS (a * 3) STORED;
SELECT * FROM gtest25 ORDER BY a;
ALTER TABLE gtest25 ADD COLUMN x int GENERATED ALWAYS AS (b * 4) STORED; -- error
ALTER TABLE gtest25 ADD COLUMN x int GENERATED ALWAYS AS (z * 4) STORED; -- error
@ -504,17 +473,8 @@ CREATE TABLE gtest29 (
b int GENERATED ALWAYS AS (a * 2) STORED
);
INSERT INTO gtest29 (a) VALUES (3), (4);
SELECT * FROM gtest29;
\d gtest29
ALTER TABLE gtest29 ALTER COLUMN a SET EXPRESSION AS (a * 3); -- error
ALTER TABLE gtest29 ALTER COLUMN a DROP EXPRESSION; -- error
ALTER TABLE gtest29 ALTER COLUMN a DROP EXPRESSION IF EXISTS; -- notice
-- Change the expression
ALTER TABLE gtest29 ALTER COLUMN b SET EXPRESSION AS (a * 3);
SELECT * FROM gtest29;
\d gtest29
ALTER TABLE gtest29 ALTER COLUMN b DROP EXPRESSION;
INSERT INTO gtest29 (a) VALUES (5);
INSERT INTO gtest29 (a, b) VALUES (6, 66);