mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 00:03:00 -04:00
Compare commits
No commits in common. "9391f71523b6e57f1194d9f6543bc7948c16411b" and "007693f2a3ac2ac19affcb03ad43cdb36ccff5b5" have entirely different histories.
9391f71523
...
007693f2a3
@ -675,14 +675,12 @@ DECLARE
|
|||||||
<title>Copying Types</title>
|
<title>Copying Types</title>
|
||||||
|
|
||||||
<synopsis>
|
<synopsis>
|
||||||
<replaceable>name</replaceable> <replaceable>table</replaceable>.<replaceable>column</replaceable>%TYPE
|
<replaceable>variable</replaceable>%TYPE
|
||||||
<replaceable>name</replaceable> <replaceable>variable</replaceable>%TYPE
|
|
||||||
</synopsis>
|
</synopsis>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
<literal>%TYPE</literal> provides the data type of a table column
|
<literal>%TYPE</literal> provides the data type of a variable or
|
||||||
or a previously-declared <application>PL/pgSQL</application>
|
table column. You can use this to declare variables that will hold
|
||||||
variable. You can use this to declare variables that will hold
|
|
||||||
database values. For example, let's say you have a column named
|
database values. For example, let's say you have a column named
|
||||||
<literal>user_id</literal> in your <literal>users</literal>
|
<literal>user_id</literal> in your <literal>users</literal>
|
||||||
table. To declare a variable with the same data type as
|
table. To declare a variable with the same data type as
|
||||||
@ -692,21 +690,6 @@ user_id users.user_id%TYPE;
|
|||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</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>
|
<para>
|
||||||
By using <literal>%TYPE</literal> you don't need to know the data
|
By using <literal>%TYPE</literal> you don't need to know the data
|
||||||
type of the structure you are referencing, and most importantly,
|
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.)
|
<literal>%ROWTYPE</literal> is more portable.)
|
||||||
</para>
|
</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>
|
<para>
|
||||||
Parameters to a function can be
|
Parameters to a function can be
|
||||||
composite types (complete table rows). In that case, the
|
composite types (complete table rows). In that case, the
|
||||||
|
@ -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> 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> DROP DEFAULT
|
||||||
ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> { SET | DROP } NOT NULL
|
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> 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> 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> ] } [...]
|
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>
|
</listitem>
|
||||||
</varlistentry>
|
</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">
|
<varlistentry id="sql-altertable-desc-drop-expression">
|
||||||
<term><literal>DROP EXPRESSION [ IF EXISTS ]</literal></term>
|
<term><literal>DROP EXPRESSION [ IF EXISTS ]</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -147,11 +147,10 @@ typedef enum AlterTablePass
|
|||||||
AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
|
AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
|
||||||
AT_PASS_DROP, /* DROP (all flavors) */
|
AT_PASS_DROP, /* DROP (all flavors) */
|
||||||
AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
|
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_INDEX, /* re-add existing indexes */
|
||||||
AT_PASS_OLD_CONSTR, /* re-add existing constraints */
|
AT_PASS_OLD_CONSTR, /* re-add existing constraints */
|
||||||
/* We could support a RENAME COLUMN pass here, but not currently used */
|
/* 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_ADD_CONSTR, /* ADD constraints (initial examination) */
|
||||||
AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
|
AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
|
||||||
AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
|
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,
|
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
|
||||||
Node *def, LOCKMODE lockmode);
|
Node *def, LOCKMODE lockmode);
|
||||||
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, 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 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 ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
|
||||||
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
|
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 bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
|
||||||
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
|
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
|
||||||
AlterTableCmd *cmd, LOCKMODE lockmode);
|
AlterTableCmd *cmd, LOCKMODE lockmode);
|
||||||
static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
|
static void RememberAllDependentForRebuilding(AlteredTableInfo *tab,
|
||||||
Relation rel, AttrNumber attnum, const char *colName);
|
Relation rel, AttrNumber attnum, const char *colName);
|
||||||
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
|
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
|
||||||
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
|
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
|
||||||
@ -4554,7 +4551,6 @@ AlterTableGetLockLevel(List *cmds)
|
|||||||
case AT_AddIdentity:
|
case AT_AddIdentity:
|
||||||
case AT_DropIdentity:
|
case AT_DropIdentity:
|
||||||
case AT_SetIdentity:
|
case AT_SetIdentity:
|
||||||
case AT_SetExpression:
|
|
||||||
case AT_DropExpression:
|
case AT_DropExpression:
|
||||||
case AT_SetCompression:
|
case AT_SetCompression:
|
||||||
cmd_lockmode = AccessExclusiveLock;
|
cmd_lockmode = AccessExclusiveLock;
|
||||||
@ -4856,11 +4852,6 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
|||||||
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
|
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
|
||||||
pass = AT_PASS_COL_ATTRS;
|
pass = AT_PASS_COL_ATTRS;
|
||||||
break;
|
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 */
|
case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
|
||||||
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
|
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
|
||||||
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
|
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
|
||||||
@ -5162,11 +5153,11 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
|
|||||||
lockmode, pass, context);
|
lockmode, pass, context);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
|
* After the ALTER TYPE pass, do cleanup work (this is not done in
|
||||||
* (this is not done in ATExecAlterColumnType since it should be
|
* ATExecAlterColumnType since it should be done only once if
|
||||||
* done only once if multiple columns of a table are altered).
|
* 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);
|
ATPostAlterTypeCleanup(wqueue, tab, lockmode);
|
||||||
|
|
||||||
if (tab->rel)
|
if (tab->rel)
|
||||||
@ -5245,9 +5236,6 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
|
|||||||
case AT_SetAttNotNull: /* set pg_attribute.attnotnull */
|
case AT_SetAttNotNull: /* set pg_attribute.attnotnull */
|
||||||
address = ATExecSetAttNotNull(wqueue, rel, cmd->name, lockmode);
|
address = ATExecSetAttNotNull(wqueue, rel, cmd->name, lockmode);
|
||||||
break;
|
break;
|
||||||
case AT_SetExpression:
|
|
||||||
address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
|
|
||||||
break;
|
|
||||||
case AT_DropExpression:
|
case AT_DropExpression:
|
||||||
address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
|
address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
|
||||||
break;
|
break;
|
||||||
@ -6375,8 +6363,6 @@ alter_table_type_to_string(AlterTableType cmdtype)
|
|||||||
return "ALTER COLUMN ... SET NOT NULL";
|
return "ALTER COLUMN ... SET NOT NULL";
|
||||||
case AT_SetAttNotNull:
|
case AT_SetAttNotNull:
|
||||||
return NULL; /* not real grammar */
|
return NULL; /* not real grammar */
|
||||||
case AT_SetExpression:
|
|
||||||
return "ALTER COLUMN ... SET EXPRESSION";
|
|
||||||
case AT_DropExpression:
|
case AT_DropExpression:
|
||||||
return "ALTER COLUMN ... DROP EXPRESSION";
|
return "ALTER COLUMN ... DROP EXPRESSION";
|
||||||
case AT_SetStatistics:
|
case AT_SetStatistics:
|
||||||
@ -8027,11 +8013,10 @@ ATExecColumnDefault(Relation rel, const char *colName,
|
|||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("column \"%s\" of relation \"%s\" is a generated column",
|
errmsg("column \"%s\" of relation \"%s\" is a generated column",
|
||||||
colName, RelationGetRelationName(rel)),
|
colName, RelationGetRelationName(rel)),
|
||||||
newDefault ?
|
newDefault || TupleDescAttr(tupdesc, attnum - 1)->attgenerated != ATTRIBUTE_GENERATED_STORED ? 0 :
|
||||||
/* translator: %s is an SQL ALTER command */
|
/* translator: %s is an SQL ALTER command */
|
||||||
errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
|
errhint("Use %s instead.",
|
||||||
(TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
|
"ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION")));
|
||||||
errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove any old default for the column. We use RESTRICT here for
|
* 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;
|
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
|
* 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
|
* the info before executing ALTER TYPE, though, else the deparser will
|
||||||
* get confused.
|
* 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
|
* 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
|
* Subroutine for ATExecAlterColumnType: Find everything that depends on the
|
||||||
* that depends on the column (constraints, indexes, etc), and record enough
|
* column (constraints, indexes, etc), and record enough information to let us
|
||||||
* information to let us recreate the objects.
|
* recreate the objects.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
|
RememberAllDependentForRebuilding(AlteredTableInfo *tab, Relation rel, AttrNumber attnum, const char *colName)
|
||||||
Relation rel, AttrNumber attnum, const char *colName)
|
|
||||||
{
|
{
|
||||||
Relation depRel;
|
Relation depRel;
|
||||||
ScanKeyData key[3];
|
ScanKeyData key[3];
|
||||||
SysScanDesc scan;
|
SysScanDesc scan;
|
||||||
HeapTuple depTup;
|
HeapTuple depTup;
|
||||||
|
|
||||||
Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
|
|
||||||
|
|
||||||
depRel = table_open(DependRelationId, RowExclusiveLock);
|
depRel = table_open(DependRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
ScanKeyInit(&key[0],
|
ScanKeyInit(&key[0],
|
||||||
@ -13705,13 +13572,12 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
|
|||||||
|
|
||||||
case OCLASS_REWRITE:
|
case OCLASS_REWRITE:
|
||||||
/* XXX someday see if we can cope with revising views */
|
/* XXX someday see if we can cope with revising views */
|
||||||
if (subtype == AT_AlterColumnType)
|
ereport(ERROR,
|
||||||
ereport(ERROR,
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
errmsg("cannot alter type of a column used by a view or rule"),
|
||||||
errmsg("cannot alter type of a column used by a view or rule"),
|
errdetail("%s depends on column \"%s\"",
|
||||||
errdetail("%s depends on column \"%s\"",
|
getObjectDescription(&foundObject, false),
|
||||||
getObjectDescription(&foundObject, false),
|
colName)));
|
||||||
colName)));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCLASS_TRIGGER:
|
case OCLASS_TRIGGER:
|
||||||
@ -13725,13 +13591,12 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
|
|||||||
* significant amount of new code. Since we can't easily tell
|
* significant amount of new code. Since we can't easily tell
|
||||||
* which case applies, we punt for both. FIXME someday.
|
* which case applies, we punt for both. FIXME someday.
|
||||||
*/
|
*/
|
||||||
if (subtype == AT_AlterColumnType)
|
ereport(ERROR,
|
||||||
ereport(ERROR,
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
errmsg("cannot alter type of a column used in a trigger definition"),
|
||||||
errmsg("cannot alter type of a column used in a trigger definition"),
|
errdetail("%s depends on column \"%s\"",
|
||||||
errdetail("%s depends on column \"%s\"",
|
getObjectDescription(&foundObject, false),
|
||||||
getObjectDescription(&foundObject, false),
|
colName)));
|
||||||
colName)));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCLASS_POLICY:
|
case OCLASS_POLICY:
|
||||||
@ -13744,13 +13609,12 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
|
|||||||
* easy enough to remove and recreate the policy; still, FIXME
|
* easy enough to remove and recreate the policy; still, FIXME
|
||||||
* someday.
|
* someday.
|
||||||
*/
|
*/
|
||||||
if (subtype == AT_AlterColumnType)
|
ereport(ERROR,
|
||||||
ereport(ERROR,
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
errmsg("cannot alter type of a column used in a policy definition"),
|
||||||
errmsg("cannot alter type of a column used in a policy definition"),
|
errdetail("%s depends on column \"%s\"",
|
||||||
errdetail("%s depends on column \"%s\"",
|
getObjectDescription(&foundObject, false),
|
||||||
getObjectDescription(&foundObject, false),
|
colName)));
|
||||||
colName)));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCLASS_DEFAULT:
|
case OCLASS_DEFAULT:
|
||||||
@ -13770,20 +13634,19 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
|
|||||||
/*
|
/*
|
||||||
* This must be a reference from the expression of a
|
* This must be a reference from the expression of a
|
||||||
* generated column elsewhere in the same table.
|
* generated column elsewhere in the same table.
|
||||||
* Changing the type/generated expression of a column
|
* Changing the type of a column that is used by a
|
||||||
* that is used by a generated column is not allowed
|
* generated column is not allowed by SQL standard, so
|
||||||
* by SQL standard, so just punt for now. It might be
|
* just punt for now. It might be doable with some
|
||||||
* doable with some thinking and effort.
|
* thinking and effort.
|
||||||
*/
|
*/
|
||||||
if (subtype == AT_AlterColumnType)
|
ereport(ERROR,
|
||||||
ereport(ERROR,
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
errmsg("cannot alter type of a column used by a generated column"),
|
||||||
errmsg("cannot alter type of a column used by a generated column"),
|
errdetail("Column \"%s\" is used by generated column \"%s\".",
|
||||||
errdetail("Column \"%s\" is used by generated column \"%s\".",
|
colName,
|
||||||
colName,
|
get_attname(col.objectId,
|
||||||
get_attname(col.objectId,
|
col.objectSubId,
|
||||||
col.objectSubId,
|
false))));
|
||||||
false))));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -14000,11 +13863,11 @@ RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
|
* Cleanup after we've finished all the ALTER TYPE operations for a
|
||||||
* operations for a particular relation. We have to drop and recreate all the
|
* particular relation. We have to drop and recreate all the indexes
|
||||||
* indexes and constraints that depend on the altered columns. We do the
|
* and constraints that depend on the altered columns. We do the
|
||||||
* actual dropping here, but re-creation is managed by adding work queue
|
* actual dropping here, but re-creation is managed by adding work
|
||||||
* entries to do those steps later.
|
* queue entries to do those steps later.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
|
ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
|
||||||
|
@ -216,6 +216,7 @@ ExecInitQual(List *qual, PlanState *parent)
|
|||||||
ExprState *state;
|
ExprState *state;
|
||||||
ExprEvalStep scratch = {0};
|
ExprEvalStep scratch = {0};
|
||||||
List *adjust_jumps = NIL;
|
List *adjust_jumps = NIL;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
/* short-circuit (here and in ExecQual) for empty restriction list */
|
/* short-circuit (here and in ExecQual) for empty restriction list */
|
||||||
if (qual == NIL)
|
if (qual == NIL)
|
||||||
@ -249,8 +250,10 @@ ExecInitQual(List *qual, PlanState *parent)
|
|||||||
scratch.resvalue = &state->resvalue;
|
scratch.resvalue = &state->resvalue;
|
||||||
scratch.resnull = &state->resnull;
|
scratch.resnull = &state->resnull;
|
||||||
|
|
||||||
foreach_ptr(Expr, node, qual)
|
foreach(lc, qual)
|
||||||
{
|
{
|
||||||
|
Expr *node = (Expr *) lfirst(lc);
|
||||||
|
|
||||||
/* first evaluate expression */
|
/* first evaluate expression */
|
||||||
ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
|
ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
|
||||||
|
|
||||||
@ -262,9 +265,9 @@ ExecInitQual(List *qual, PlanState *parent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* adjust jump targets */
|
/* 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->opcode == EEOP_QUAL);
|
||||||
Assert(as->d.qualexpr.jumpdone == -1);
|
Assert(as->d.qualexpr.jumpdone == -1);
|
||||||
|
@ -4579,16 +4579,12 @@ ExecEvalPreOrderedDistinctSingle(AggState *aggstate, AggStatePerTrans pertrans)
|
|||||||
/*
|
/*
|
||||||
* ExecEvalPreOrderedDistinctMulti
|
* ExecEvalPreOrderedDistinctMulti
|
||||||
* Returns true when the aggregate input is distinct from the previous
|
* Returns true when the aggregate input is distinct from the previous
|
||||||
* input and returns false when the input matches the previous input, or
|
* input and returns false when the input matches the previous input.
|
||||||
* when there was no previous input.
|
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
ExecEvalPreOrderedDistinctMulti(AggState *aggstate, AggStatePerTrans pertrans)
|
ExecEvalPreOrderedDistinctMulti(AggState *aggstate, AggStatePerTrans pertrans)
|
||||||
{
|
{
|
||||||
ExprContext *tmpcontext = aggstate->tmpcontext;
|
ExprContext *tmpcontext = aggstate->tmpcontext;
|
||||||
bool isdistinct = false; /* for now */
|
|
||||||
TupleTableSlot *save_outer;
|
|
||||||
TupleTableSlot *save_inner;
|
|
||||||
|
|
||||||
for (int i = 0; i < pertrans->numTransInputs; i++)
|
for (int i = 0; i < pertrans->numTransInputs; i++)
|
||||||
{
|
{
|
||||||
@ -4600,10 +4596,6 @@ ExecEvalPreOrderedDistinctMulti(AggState *aggstate, AggStatePerTrans pertrans)
|
|||||||
pertrans->sortslot->tts_nvalid = pertrans->numInputs;
|
pertrans->sortslot->tts_nvalid = pertrans->numInputs;
|
||||||
ExecStoreVirtualTuple(pertrans->sortslot);
|
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_outertuple = pertrans->sortslot;
|
||||||
tmpcontext->ecxt_innertuple = pertrans->uniqslot;
|
tmpcontext->ecxt_innertuple = pertrans->uniqslot;
|
||||||
|
|
||||||
@ -4615,15 +4607,9 @@ ExecEvalPreOrderedDistinctMulti(AggState *aggstate, AggStatePerTrans pertrans)
|
|||||||
|
|
||||||
pertrans->haslast = true;
|
pertrans->haslast = true;
|
||||||
ExecCopySlot(pertrans->uniqslot, pertrans->sortslot);
|
ExecCopySlot(pertrans->uniqslot, pertrans->sortslot);
|
||||||
|
return true;
|
||||||
isdistinct = true;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
/* restore the original slots */
|
|
||||||
tmpcontext->ecxt_outertuple = save_outer;
|
|
||||||
tmpcontext->ecxt_innertuple = save_inner;
|
|
||||||
|
|
||||||
return isdistinct;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1256,7 +1256,7 @@ cost_tidscan(Path *path, PlannerInfo *root,
|
|||||||
QualCost qpqual_cost;
|
QualCost qpqual_cost;
|
||||||
Cost cpu_per_tuple;
|
Cost cpu_per_tuple;
|
||||||
QualCost tid_qual_cost;
|
QualCost tid_qual_cost;
|
||||||
double ntuples;
|
int ntuples;
|
||||||
ListCell *l;
|
ListCell *l;
|
||||||
double spc_random_page_cost;
|
double spc_random_page_cost;
|
||||||
|
|
||||||
@ -1283,7 +1283,7 @@ cost_tidscan(Path *path, PlannerInfo *root,
|
|||||||
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) qual;
|
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) qual;
|
||||||
Node *arraynode = (Node *) lsecond(saop->args);
|
Node *arraynode = (Node *) lsecond(saop->args);
|
||||||
|
|
||||||
ntuples += estimate_array_length(root, arraynode);
|
ntuples += estimate_array_length(arraynode);
|
||||||
}
|
}
|
||||||
else if (IsA(qual, CurrentOfExpr))
|
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);
|
Node *arraynode = (Node *) lsecond(saop->args);
|
||||||
QualCost sacosts;
|
QualCost sacosts;
|
||||||
QualCost hcosts;
|
QualCost hcosts;
|
||||||
double estarraylen = estimate_array_length(context->root, arraynode);
|
int estarraylen = estimate_array_length(arraynode);
|
||||||
|
|
||||||
set_sa_opfuncid(saop);
|
set_sa_opfuncid(saop);
|
||||||
sacosts.startup = sacosts.per_tuple = 0;
|
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.startup += sacosts.startup;
|
||||||
context->total.per_tuple += sacosts.per_tuple *
|
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) ||
|
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;
|
context->total.startup += perelemcost.startup;
|
||||||
if (perelemcost.per_tuple > 0)
|
if (perelemcost.per_tuple > 0)
|
||||||
context->total.per_tuple += perelemcost.per_tuple *
|
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))
|
else if (IsA(node, RowCompareExpr))
|
||||||
{
|
{
|
||||||
|
@ -2417,16 +2417,6 @@ alter_table_cmd:
|
|||||||
n->name = $3;
|
n->name = $3;
|
||||||
$$ = (Node *) n;
|
$$ = (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 TABLE <name> ALTER [COLUMN] <colname> DROP EXPRESSION */
|
||||||
| ALTER opt_column ColId DROP EXPRESSION
|
| ALTER opt_column ColId DROP EXPRESSION
|
||||||
{
|
{
|
||||||
|
@ -746,9 +746,11 @@ static Oid
|
|||||||
FindUsableIndexForReplicaIdentityFull(Relation localrel, AttrMap *attrmap)
|
FindUsableIndexForReplicaIdentityFull(Relation localrel, AttrMap *attrmap)
|
||||||
{
|
{
|
||||||
List *idxlist = RelationGetIndexList(localrel);
|
List *idxlist = RelationGetIndexList(localrel);
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
foreach_oid(idxoid, idxlist)
|
foreach(lc, idxlist)
|
||||||
{
|
{
|
||||||
|
Oid idxoid = lfirst_oid(lc);
|
||||||
bool isUsableIdx;
|
bool isUsableIdx;
|
||||||
Relation idxRel;
|
Relation idxRel;
|
||||||
IndexInfo *idxInfo;
|
IndexInfo *idxInfo;
|
||||||
|
@ -1036,11 +1036,11 @@ fetch_remote_table_info(char *nspname, char *relname,
|
|||||||
|
|
||||||
/* Build the pubname list. */
|
/* Build the pubname list. */
|
||||||
initStringInfo(&pub_names);
|
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, ", ");
|
||||||
|
|
||||||
appendStringInfoString(&pub_names, quote_literal_cstr(pubname));
|
appendStringInfoString(&pub_names, quote_literal_cstr(pubname));
|
||||||
|
@ -2234,6 +2234,7 @@ cleanup_rel_sync_cache(TransactionId xid, bool is_commit)
|
|||||||
{
|
{
|
||||||
HASH_SEQ_STATUS hash_seq;
|
HASH_SEQ_STATUS hash_seq;
|
||||||
RelationSyncEntry *entry;
|
RelationSyncEntry *entry;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
Assert(RelationSyncCache != NULL);
|
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
|
* corresponding schema and we don't need to send it unless there is
|
||||||
* any invalidation for that relation.
|
* 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)
|
if (is_commit)
|
||||||
entry->schema_sent = true;
|
entry->schema_sent = true;
|
||||||
|
|
||||||
entry->streamed_txns =
|
entry->streamed_txns =
|
||||||
foreach_delete_current(entry->streamed_txns, streamed_txn);
|
foreach_delete_current(entry->streamed_txns, lc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6340,7 +6340,7 @@ array_unnest_support(PG_FUNCTION_ARGS)
|
|||||||
/* We can use estimated argument values here */
|
/* We can use estimated argument values here */
|
||||||
arg1 = estimate_expression_value(req->root, linitial(args));
|
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;
|
ret = (Node *) req;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2128,11 +2128,10 @@ scalararraysel(PlannerInfo *root,
|
|||||||
/*
|
/*
|
||||||
* Estimate number of elements in the array yielded by an expression.
|
* Estimate number of elements in the array yielded by an expression.
|
||||||
*
|
*
|
||||||
* Note: the result is integral, but we use "double" to avoid overflow
|
* It's important that this agree with scalararraysel.
|
||||||
* concerns. Most callers will use it in double-type expressions anyway.
|
|
||||||
*/
|
*/
|
||||||
double
|
int
|
||||||
estimate_array_length(PlannerInfo *root, Node *arrayexpr)
|
estimate_array_length(Node *arrayexpr)
|
||||||
{
|
{
|
||||||
/* look through any binary-compatible relabeling of arrayexpr */
|
/* look through any binary-compatible relabeling of arrayexpr */
|
||||||
arrayexpr = strip_array_coercion(arrayexpr);
|
arrayexpr = strip_array_coercion(arrayexpr);
|
||||||
@ -2153,39 +2152,11 @@ estimate_array_length(PlannerInfo *root, Node *arrayexpr)
|
|||||||
{
|
{
|
||||||
return list_length(((ArrayExpr *) arrayexpr)->elements);
|
return list_length(((ArrayExpr *) arrayexpr)->elements);
|
||||||
}
|
}
|
||||||
else if (arrayexpr)
|
else
|
||||||
{
|
{
|
||||||
/* See if we can find any statistics about it */
|
/* default guess --- see also scalararraysel */
|
||||||
VariableStatData vardata;
|
return 10;
|
||||||
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 */
|
|
||||||
return 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -6569,7 +6540,7 @@ genericcostestimate(PlannerInfo *root,
|
|||||||
if (IsA(rinfo->clause, ScalarArrayOpExpr))
|
if (IsA(rinfo->clause, ScalarArrayOpExpr))
|
||||||
{
|
{
|
||||||
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) rinfo->clause;
|
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)
|
if (alength > 1)
|
||||||
num_sa_scans *= alength;
|
num_sa_scans *= alength;
|
||||||
@ -6849,7 +6820,7 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
|||||||
{
|
{
|
||||||
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
|
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
|
||||||
Node *other_operand = (Node *) lsecond(saop->args);
|
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;
|
clause_op = saop->opno;
|
||||||
found_saop = true;
|
found_saop = true;
|
||||||
@ -7443,7 +7414,7 @@ gincost_scalararrayopexpr(PlannerInfo *root,
|
|||||||
{
|
{
|
||||||
counts->exactEntries++;
|
counts->exactEntries++;
|
||||||
counts->searchEntries++;
|
counts->searchEntries++;
|
||||||
counts->arrayScans *= estimate_array_length(root, rightop);
|
counts->arrayScans *= estimate_array_length(rightop);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2504,7 +2504,7 @@ psql_completion(const char *text, int start, int end)
|
|||||||
/* ALTER TABLE ALTER [COLUMN] <foo> SET */
|
/* ALTER TABLE ALTER [COLUMN] <foo> SET */
|
||||||
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") ||
|
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") ||
|
||||||
Matches("ALTER", "TABLE", MatchAny, "ALTER", 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 */
|
/* a subset of ALTER SEQUENCE options */
|
||||||
"INCREMENT", "MINVALUE", "MAXVALUE", "START", "NO", "CACHE", "CYCLE");
|
"INCREMENT", "MINVALUE", "MAXVALUE", "START", "NO", "CACHE", "CYCLE");
|
||||||
/* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
|
/* 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") ||
|
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSION") ||
|
||||||
Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSION"))
|
Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSION"))
|
||||||
COMPLETE_WITH("DEFAULT", "PGLZ", "LZ4");
|
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 */
|
/* ALTER TABLE ALTER [COLUMN] <foo> SET GENERATED */
|
||||||
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "GENERATED") ||
|
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "GENERATED") ||
|
||||||
Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "GENERATED"))
|
Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "GENERATED"))
|
||||||
|
@ -2192,7 +2192,6 @@ typedef enum AlterTableType
|
|||||||
AT_DropNotNull, /* alter column drop not null */
|
AT_DropNotNull, /* alter column drop not null */
|
||||||
AT_SetNotNull, /* alter column set not null */
|
AT_SetNotNull, /* alter column set not null */
|
||||||
AT_SetAttNotNull, /* set attnotnull w/o a constraint */
|
AT_SetAttNotNull, /* set attnotnull w/o a constraint */
|
||||||
AT_SetExpression, /* alter column set expression */
|
|
||||||
AT_DropExpression, /* alter column drop expression */
|
AT_DropExpression, /* alter column drop expression */
|
||||||
AT_SetStatistics, /* alter column set statistics */
|
AT_SetStatistics, /* alter column set statistics */
|
||||||
AT_SetOptions, /* alter column set ( options ) */
|
AT_SetOptions, /* alter column set ( options ) */
|
||||||
|
@ -381,26 +381,26 @@ lnext(const List *l, const ListCell *c)
|
|||||||
/*
|
/*
|
||||||
* foreach_delete_current -
|
* foreach_delete_current -
|
||||||
* delete the current list element from the List associated with a
|
* delete the current list element from the List associated with a
|
||||||
* surrounding foreach() or foreach_*() loop, returning the new List
|
* surrounding foreach() loop, returning the new List pointer.
|
||||||
* pointer; pass the name of the iterator variable.
|
|
||||||
*
|
*
|
||||||
* This is similar to list_delete_cell(), but it also adjusts the loop's state
|
* This is equivalent to list_delete_cell(), but it also adjusts the foreach
|
||||||
* so that no list elements will be missed. Do not delete elements from an
|
* loop's state so that no list elements will be missed. Do not delete
|
||||||
* active foreach or foreach_* loop's list in any other way!
|
* elements from an active foreach loop's list in any other way!
|
||||||
*/
|
*/
|
||||||
#define foreach_delete_current(lst, var_or_cell) \
|
#define foreach_delete_current(lst, cell) \
|
||||||
((List *) (var_or_cell##__state.l = list_delete_nth_cell(lst, var_or_cell##__state.i--)))
|
(cell##__state.i--, \
|
||||||
|
(List *) (cell##__state.l = list_delete_cell(lst, cell)))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* foreach_current_index -
|
* foreach_current_index -
|
||||||
* get the zero-based list index of a surrounding foreach() or foreach_*()
|
* get the zero-based list index of a surrounding foreach() loop's
|
||||||
* loop's current element; pass the name of the iterator variable.
|
* current element; pass the name of the "ListCell *" iterator variable.
|
||||||
*
|
*
|
||||||
* Beware of using this after foreach_delete_current(); the value will be
|
* 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
|
* out of sync for the rest of the current loop iteration. Anyway, since
|
||||||
* you just deleted the current element, the value is pretty meaningless.
|
* 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 -
|
* for_each_from -
|
||||||
@ -452,57 +452,6 @@ for_each_cell_setup(const List *lst, const ListCell *initcell)
|
|||||||
return r;
|
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 -
|
* forboth -
|
||||||
* a convenience macro for advancing through two linked lists
|
* a convenience macro for advancing through two linked lists
|
||||||
|
@ -200,7 +200,7 @@ extern Selectivity scalararraysel(PlannerInfo *root,
|
|||||||
ScalarArrayOpExpr *clause,
|
ScalarArrayOpExpr *clause,
|
||||||
bool is_join_clause,
|
bool is_join_clause,
|
||||||
int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo);
|
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,
|
extern Selectivity rowcomparesel(PlannerInfo *root,
|
||||||
RowCompareExpr *clause,
|
RowCompareExpr *clause,
|
||||||
int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo);
|
int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo);
|
||||||
|
@ -93,82 +93,3 @@ LINE 1: a.r[1] := 2
|
|||||||
^
|
^
|
||||||
QUERY: a.r[1] := 2
|
QUERY: a.r[1] := 2
|
||||||
CONTEXT: PL/pgSQL function inline_code_block line 2 at assignment
|
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)"}
|
|
||||||
|
@ -2208,33 +2208,6 @@ build_datatype(HeapTuple typeTup, int32 typmod,
|
|||||||
return typ;
|
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
|
* plpgsql_recognize_err_condition
|
||||||
* Check condition name and translate it to SQLSTATE.
|
* Check condition name and translate it to SQLSTATE.
|
||||||
|
@ -757,9 +757,8 @@ decl_const :
|
|||||||
decl_datatype :
|
decl_datatype :
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* If there's a lookahead token, read_datatype() will
|
* If there's a lookahead token, read_datatype
|
||||||
* consume it, and then we must tell bison to forget
|
* should consume it.
|
||||||
* it.
|
|
||||||
*/
|
*/
|
||||||
$$ = read_datatype(yychar);
|
$$ = read_datatype(yychar);
|
||||||
yyclearin;
|
yyclearin;
|
||||||
@ -2784,17 +2783,13 @@ read_sql_construct(int until,
|
|||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Read a datatype declaration, consuming the current lookahead token if any.
|
|
||||||
* Returns a PLpgSQL_type struct.
|
|
||||||
*/
|
|
||||||
static PLpgSQL_type *
|
static PLpgSQL_type *
|
||||||
read_datatype(int tok)
|
read_datatype(int tok)
|
||||||
{
|
{
|
||||||
StringInfoData ds;
|
StringInfoData ds;
|
||||||
char *type_name;
|
char *type_name;
|
||||||
int startlocation;
|
int startlocation;
|
||||||
PLpgSQL_type *result = NULL;
|
PLpgSQL_type *result;
|
||||||
int parenlevel = 0;
|
int parenlevel = 0;
|
||||||
|
|
||||||
/* Should only be called while parsing DECLARE sections */
|
/* Should only be called while parsing DECLARE sections */
|
||||||
@ -2804,15 +2799,11 @@ read_datatype(int tok)
|
|||||||
if (tok == YYEMPTY)
|
if (tok == YYEMPTY)
|
||||||
tok = yylex();
|
tok = yylex();
|
||||||
|
|
||||||
/* The current token is the start of what we'll pass to parse_datatype */
|
|
||||||
startlocation = yylloc;
|
startlocation = yylloc;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we have a simple or composite identifier, check for %TYPE and
|
* If we have a simple or composite identifier, check for %TYPE
|
||||||
* %ROWTYPE constructs. (Note that if plpgsql_parse_wordtype et al fail
|
* and %ROWTYPE constructs.
|
||||||
* 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 (tok == T_WORD)
|
if (tok == T_WORD)
|
||||||
{
|
{
|
||||||
@ -2824,10 +2815,18 @@ read_datatype(int tok)
|
|||||||
tok = yylex();
|
tok = yylex();
|
||||||
if (tok_is_keyword(tok, &yylval,
|
if (tok_is_keyword(tok, &yylval,
|
||||||
K_TYPE, "type"))
|
K_TYPE, "type"))
|
||||||
|
{
|
||||||
result = plpgsql_parse_wordtype(dtname);
|
result = plpgsql_parse_wordtype(dtname);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
else if (tok_is_keyword(tok, &yylval,
|
else if (tok_is_keyword(tok, &yylval,
|
||||||
K_ROWTYPE, "rowtype"))
|
K_ROWTYPE, "rowtype"))
|
||||||
|
{
|
||||||
result = plpgsql_parse_wordrowtype(dtname);
|
result = plpgsql_parse_wordrowtype(dtname);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (plpgsql_token_is_unreserved_keyword(tok))
|
else if (plpgsql_token_is_unreserved_keyword(tok))
|
||||||
@ -2840,10 +2839,18 @@ read_datatype(int tok)
|
|||||||
tok = yylex();
|
tok = yylex();
|
||||||
if (tok_is_keyword(tok, &yylval,
|
if (tok_is_keyword(tok, &yylval,
|
||||||
K_TYPE, "type"))
|
K_TYPE, "type"))
|
||||||
|
{
|
||||||
result = plpgsql_parse_wordtype(dtname);
|
result = plpgsql_parse_wordtype(dtname);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
else if (tok_is_keyword(tok, &yylval,
|
else if (tok_is_keyword(tok, &yylval,
|
||||||
K_ROWTYPE, "rowtype"))
|
K_ROWTYPE, "rowtype"))
|
||||||
|
{
|
||||||
result = plpgsql_parse_wordrowtype(dtname);
|
result = plpgsql_parse_wordrowtype(dtname);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (tok == T_CWORD)
|
else if (tok == T_CWORD)
|
||||||
@ -2856,56 +2863,21 @@ read_datatype(int tok)
|
|||||||
tok = yylex();
|
tok = yylex();
|
||||||
if (tok_is_keyword(tok, &yylval,
|
if (tok_is_keyword(tok, &yylval,
|
||||||
K_TYPE, "type"))
|
K_TYPE, "type"))
|
||||||
|
{
|
||||||
result = plpgsql_parse_cwordtype(dtnames);
|
result = plpgsql_parse_cwordtype(dtnames);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
else if (tok_is_keyword(tok, &yylval,
|
else if (tok_is_keyword(tok, &yylval,
|
||||||
K_ROWTYPE, "rowtype"))
|
K_ROWTYPE, "rowtype"))
|
||||||
|
{
|
||||||
result = plpgsql_parse_cwordrowtype(dtnames);
|
result = plpgsql_parse_cwordrowtype(dtnames);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 != ';')
|
while (tok != ';')
|
||||||
{
|
{
|
||||||
if (tok == 0)
|
if (tok == 0)
|
||||||
|
@ -1249,7 +1249,6 @@ extern PLpgSQL_type *plpgsql_parse_cwordrowtype(List *idents);
|
|||||||
extern PGDLLEXPORT PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod,
|
extern PGDLLEXPORT PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod,
|
||||||
Oid collation,
|
Oid collation,
|
||||||
TypeName *origtypname);
|
TypeName *origtypname);
|
||||||
extern PLpgSQL_type *plpgsql_build_datatype_arrayof(PLpgSQL_type *dtype);
|
|
||||||
extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int lineno,
|
extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int lineno,
|
||||||
PLpgSQL_type *dtype,
|
PLpgSQL_type *dtype,
|
||||||
bool add2namespace);
|
bool add2namespace);
|
||||||
|
@ -77,74 +77,3 @@ begin a[1] := 2; raise notice 'a = %', a; end$$;
|
|||||||
|
|
||||||
do $$ declare a complex;
|
do $$ declare a complex;
|
||||||
begin a.r[1] := 2; raise notice 'a = %', a; end$$;
|
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;
|
|
||||||
$$;
|
|
||||||
|
@ -132,9 +132,6 @@ get_altertable_subcmdinfo(PG_FUNCTION_ARGS)
|
|||||||
case AT_SetAttNotNull:
|
case AT_SetAttNotNull:
|
||||||
strtype = "SET ATTNOTNULL";
|
strtype = "SET ATTNOTNULL";
|
||||||
break;
|
break;
|
||||||
case AT_SetExpression:
|
|
||||||
strtype = "SET EXPRESSION";
|
|
||||||
break;
|
|
||||||
case AT_DropExpression:
|
case AT_DropExpression:
|
||||||
strtype = "DROP EXPRESSION";
|
strtype = "DROP EXPRESSION";
|
||||||
break;
|
break;
|
||||||
|
@ -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)"}
|
{"(0,,)","(1,3,foo)","(2,2,bar)","(3,1,baz)"}
|
||||||
(1 row)
|
(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
|
-- check node I/O via view creation and usage, also deparsing logic
|
||||||
create view agg_view1 as
|
create view agg_view1 as
|
||||||
select aggfns(a,b,c)
|
select aggfns(a,b,c)
|
||||||
|
@ -578,9 +578,6 @@ INSERT INTO gtest20 (a) VALUES (10); -- ok
|
|||||||
INSERT INTO gtest20 (a) VALUES (30); -- violates constraint
|
INSERT INTO gtest20 (a) VALUES (30); -- violates constraint
|
||||||
ERROR: new row for relation "gtest20" violates check constraint "gtest20_b_check"
|
ERROR: new row for relation "gtest20" violates check constraint "gtest20_b_check"
|
||||||
DETAIL: Failing row contains (30, 60).
|
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);
|
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 (10);
|
||||||
INSERT INTO gtest20a (a) VALUES (30);
|
INSERT INTO gtest20a (a) VALUES (30);
|
||||||
@ -676,47 +673,6 @@ SELECT * FROM gtest22c WHERE a = 1 AND b > 0;
|
|||||||
1 | 2
|
1 | 2
|
||||||
(1 row)
|
(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_seqscan;
|
||||||
RESET enable_bitmapscan;
|
RESET enable_bitmapscan;
|
||||||
-- foreign keys
|
-- foreign keys
|
||||||
@ -742,10 +698,6 @@ INSERT INTO gtest23b VALUES (1); -- ok
|
|||||||
INSERT INTO gtest23b VALUES (5); -- error
|
INSERT INTO gtest23b VALUES (5); -- error
|
||||||
ERROR: insert or update on table "gtest23b" violates foreign key constraint "gtest23b_b_fkey"
|
ERROR: insert or update on table "gtest23b" violates foreign key constraint "gtest23b_b_fkey"
|
||||||
DETAIL: Key (b)=(10) is not present in table "gtest23a".
|
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 gtest23b;
|
||||||
DROP TABLE gtest23a;
|
DROP TABLE gtest23a;
|
||||||
CREATE TABLE gtest23p (x int, y int GENERATED ALWAYS AS (x * 2) STORED, PRIMARY KEY (y));
|
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')
|
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', 1);
|
||||||
INSERT INTO gtest_parent (f1, f2) VALUES ('2016-07-15', 2);
|
SELECT * FROM gtest_parent;
|
||||||
INSERT INTO gtest_parent (f1, f2) VALUES ('2016-08-15', 3);
|
f1 | f2 | f3
|
||||||
SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
|
------------+----+----
|
||||||
tableoid | f1 | f2 | f3
|
07-15-2016 | 1 | 2
|
||||||
--------------+------------+----+----
|
(1 row)
|
||||||
gtest_child | 07-15-2016 | 1 | 2
|
|
||||||
gtest_child | 07-15-2016 | 2 | 4
|
|
||||||
gtest_child2 | 08-15-2016 | 3 | 66
|
|
||||||
(3 rows)
|
|
||||||
|
|
||||||
UPDATE gtest_parent SET f1 = f1 + 60 WHERE f2 = 1;
|
SELECT * FROM gtest_child;
|
||||||
SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
|
f1 | f2 | f3
|
||||||
tableoid | f1 | f2 | f3
|
------------+----+----
|
||||||
--------------+------------+----+----
|
07-15-2016 | 1 | 2
|
||||||
gtest_child | 07-15-2016 | 2 | 4
|
(1 row)
|
||||||
gtest_child2 | 08-15-2016 | 3 | 66
|
|
||||||
gtest_child3 | 09-13-2016 | 1 | 33
|
|
||||||
(3 rows)
|
|
||||||
|
|
||||||
-- alter only parent's and one child's generation expression
|
UPDATE gtest_parent SET f1 = f1 + 60;
|
||||||
ALTER TABLE ONLY gtest_parent ALTER COLUMN f3 SET EXPRESSION AS (f2 * 4);
|
SELECT * FROM gtest_parent;
|
||||||
ALTER TABLE gtest_child ALTER COLUMN f3 SET EXPRESSION AS (f2 * 10);
|
f1 | f2 | f3
|
||||||
\d gtest_parent
|
------------+----+----
|
||||||
Partitioned table "public.gtest_parent"
|
09-13-2016 | 1 | 33
|
||||||
Column | Type | Collation | Nullable | Default
|
(1 row)
|
||||||
--------+--------+-----------+----------+-------------------------------------
|
|
||||||
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.)
|
|
||||||
|
|
||||||
\d gtest_child
|
SELECT * FROM gtest_child3;
|
||||||
Table "public.gtest_child"
|
f1 | f2 | f3
|
||||||
Column | Type | Collation | Nullable | Default
|
------------+----+----
|
||||||
--------+--------+-----------+----------+--------------------------------------
|
09-13-2016 | 1 | 33
|
||||||
f1 | date | | not null |
|
(1 row)
|
||||||
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)
|
|
||||||
|
|
||||||
-- we leave these tables around for purposes of testing dump/reload/upgrade
|
-- we leave these tables around for purposes of testing dump/reload/upgrade
|
||||||
-- generated columns in partition key (not allowed)
|
-- generated columns in partition key (not allowed)
|
||||||
@ -962,7 +825,7 @@ DETAIL: Column "f3" is a generated column.
|
|||||||
-- ALTER TABLE ... ADD COLUMN
|
-- ALTER TABLE ... ADD COLUMN
|
||||||
CREATE TABLE gtest25 (a int PRIMARY KEY);
|
CREATE TABLE gtest25 (a int PRIMARY KEY);
|
||||||
INSERT INTO gtest25 VALUES (3), (4);
|
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;
|
SELECT * FROM gtest25 ORDER BY a;
|
||||||
a | b
|
a | b
|
||||||
---+----
|
---+----
|
||||||
@ -1072,50 +935,18 @@ CREATE TABLE gtest29 (
|
|||||||
b int GENERATED ALWAYS AS (a * 2) STORED
|
b int GENERATED ALWAYS AS (a * 2) STORED
|
||||||
);
|
);
|
||||||
INSERT INTO gtest29 (a) VALUES (3), (4);
|
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
|
ALTER TABLE gtest29 ALTER COLUMN a DROP EXPRESSION; -- error
|
||||||
ERROR: column "a" of relation "gtest29" is not a stored generated column
|
ERROR: column "a" of relation "gtest29" is not a stored generated column
|
||||||
ALTER TABLE gtest29 ALTER COLUMN a DROP EXPRESSION IF EXISTS; -- notice
|
ALTER TABLE gtest29 ALTER COLUMN a DROP EXPRESSION IF EXISTS; -- notice
|
||||||
NOTICE: column "a" of relation "gtest29" is not a stored generated column, skipping
|
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;
|
ALTER TABLE gtest29 ALTER COLUMN b DROP EXPRESSION;
|
||||||
INSERT INTO gtest29 (a) VALUES (5);
|
INSERT INTO gtest29 (a) VALUES (5);
|
||||||
INSERT INTO gtest29 (a, b) VALUES (6, 66);
|
INSERT INTO gtest29 (a, b) VALUES (6, 66);
|
||||||
SELECT * FROM gtest29;
|
SELECT * FROM gtest29;
|
||||||
a | b
|
a | b
|
||||||
---+----
|
---+----
|
||||||
3 | 9
|
3 | 6
|
||||||
4 | 12
|
4 | 8
|
||||||
5 |
|
5 |
|
||||||
6 | 66
|
6 | 66
|
||||||
(4 rows)
|
(4 rows)
|
||||||
|
@ -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),
|
from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c),
|
||||||
generate_series(1,2) i;
|
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
|
-- check node I/O via view creation and usage, also deparsing logic
|
||||||
|
|
||||||
create view agg_view1 as
|
create view agg_view1 as
|
||||||
|
@ -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 (10); -- ok
|
||||||
INSERT INTO gtest20 (a) VALUES (30); -- violates constraint
|
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);
|
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 (10);
|
||||||
INSERT INTO gtest20a (a) VALUES (30);
|
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;
|
SELECT * FROM gtest22c WHERE b * 3 = 6;
|
||||||
EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE a = 1 AND b > 0;
|
EXPLAIN (COSTS OFF) SELECT * FROM gtest22c WHERE a = 1 AND b > 0;
|
||||||
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_seqscan;
|
||||||
RESET enable_bitmapscan;
|
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 (1); -- ok
|
||||||
INSERT INTO gtest23b VALUES (5); -- error
|
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 gtest23b;
|
||||||
DROP TABLE gtest23a;
|
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_child2
|
||||||
\d gtest_child3
|
\d gtest_child3
|
||||||
INSERT INTO gtest_parent (f1, f2) VALUES ('2016-07-15', 1);
|
INSERT INTO gtest_parent (f1, f2) VALUES ('2016-07-15', 1);
|
||||||
INSERT INTO gtest_parent (f1, f2) VALUES ('2016-07-15', 2);
|
SELECT * FROM gtest_parent;
|
||||||
INSERT INTO gtest_parent (f1, f2) VALUES ('2016-08-15', 3);
|
SELECT * FROM gtest_child;
|
||||||
SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
|
UPDATE gtest_parent SET f1 = f1 + 60;
|
||||||
UPDATE gtest_parent SET f1 = f1 + 60 WHERE f2 = 1;
|
SELECT * FROM gtest_parent;
|
||||||
SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
|
SELECT * FROM gtest_child3;
|
||||||
|
|
||||||
-- 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;
|
|
||||||
-- we leave these tables around for purposes of testing dump/reload/upgrade
|
-- we leave these tables around for purposes of testing dump/reload/upgrade
|
||||||
|
|
||||||
-- generated columns in partition key (not allowed)
|
-- 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
|
-- ALTER TABLE ... ADD COLUMN
|
||||||
CREATE TABLE gtest25 (a int PRIMARY KEY);
|
CREATE TABLE gtest25 (a int PRIMARY KEY);
|
||||||
INSERT INTO gtest25 VALUES (3), (4);
|
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;
|
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 (b * 4) STORED; -- error
|
||||||
ALTER TABLE gtest25 ADD COLUMN x int GENERATED ALWAYS AS (z * 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
|
b int GENERATED ALWAYS AS (a * 2) STORED
|
||||||
);
|
);
|
||||||
INSERT INTO gtest29 (a) VALUES (3), (4);
|
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; -- error
|
||||||
ALTER TABLE gtest29 ALTER COLUMN a DROP EXPRESSION IF EXISTS; -- notice
|
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;
|
ALTER TABLE gtest29 ALTER COLUMN b DROP EXPRESSION;
|
||||||
INSERT INTO gtest29 (a) VALUES (5);
|
INSERT INTO gtest29 (a) VALUES (5);
|
||||||
INSERT INTO gtest29 (a, b) VALUES (6, 66);
|
INSERT INTO gtest29 (a, b) VALUES (6, 66);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user