mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Support assignment to subfields of composite columns in UPDATE and INSERT.
As a side effect, cause subscripts in INSERT targetlists to do something more or less sensible; previously we evaluated such subscripts and then effectively ignored them. Another side effect is that UPDATE-ing an element or slice of an array value that is NULL now produces a non-null result, namely an array containing just the assigned-to positions.
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$PostgreSQL: pgsql/doc/src/sgml/ref/insert.sgml,v 1.26 2003/11/29 19:51:39 pgsql Exp $
|
$PostgreSQL: pgsql/doc/src/sgml/ref/insert.sgml,v 1.27 2004/06/09 19:08:13 tgl Exp $
|
||||||
PostgreSQL documentation
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -73,6 +73,9 @@ INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable
|
|||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
The name of a column in <replaceable class="PARAMETER">table</replaceable>.
|
The name of a column in <replaceable class="PARAMETER">table</replaceable>.
|
||||||
|
The column name can be qualified with a subfield name or array
|
||||||
|
subscript, if needed. (Inserting into only some fields of a
|
||||||
|
composite column leaves the other fields null.)
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -184,13 +187,11 @@ INSERT INTO films SELECT * FROM tmp;
|
|||||||
|
|
||||||
<programlisting>
|
<programlisting>
|
||||||
-- Create an empty 3x3 gameboard for noughts-and-crosses
|
-- Create an empty 3x3 gameboard for noughts-and-crosses
|
||||||
-- (all of these commands create the same board)
|
-- (these commands create the same board)
|
||||||
INSERT INTO tictactoe (game, board[1:3][1:3])
|
INSERT INTO tictactoe (game, board[1:3][1:3])
|
||||||
VALUES (1,'{{"","",""},{},{"",""}}');
|
VALUES (1,'{{"","",""},{"","",""},{"","",""}}');
|
||||||
INSERT INTO tictactoe (game, board[3][3])
|
|
||||||
VALUES (2,'{}');
|
|
||||||
INSERT INTO tictactoe (game, board)
|
INSERT INTO tictactoe (game, board)
|
||||||
VALUES (3,'{{,,},{,,},{,,}}');
|
VALUES (2,'{{,,},{,,},{,,}}');
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$PostgreSQL: pgsql/doc/src/sgml/ref/update.sgml,v 1.28 2004/03/03 22:22:24 neilc Exp $
|
$PostgreSQL: pgsql/doc/src/sgml/ref/update.sgml,v 1.29 2004/06/09 19:08:13 tgl Exp $
|
||||||
PostgreSQL documentation
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -77,7 +77,10 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> SET <replacea
|
|||||||
<term><replaceable class="PARAMETER">column</replaceable></term>
|
<term><replaceable class="PARAMETER">column</replaceable></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
The name of a column in <replaceable class="PARAMETER">table</replaceable>.
|
The name of a column in <replaceable
|
||||||
|
class="PARAMETER">table</replaceable>.
|
||||||
|
The column name can be qualified with a subfield name or array
|
||||||
|
subscript, if needed.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/rowtypes.sgml,v 2.1 2004/06/07 04:04:47 tgl Exp $ -->
|
<!-- $PostgreSQL: pgsql/doc/src/sgml/rowtypes.sgml,v 2.2 2004/06/09 19:08:14 tgl Exp $ -->
|
||||||
|
|
||||||
<sect1 id="rowtypes">
|
<sect1 id="rowtypes">
|
||||||
<title>Composite Types</title>
|
<title>Composite Types</title>
|
||||||
@ -66,6 +66,27 @@ SELECT price_extension(item, 10) FROM on_hand;
|
|||||||
</programlisting>
|
</programlisting>
|
||||||
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Whenever you create a table, a composite type is also automatically
|
||||||
|
created, with the same name as the table, to represent the table's
|
||||||
|
row type. For example, had we said
|
||||||
|
<programlisting>
|
||||||
|
CREATE TABLE inventory_item (
|
||||||
|
name text,
|
||||||
|
supplier_id integer REFERENCES suppliers,
|
||||||
|
price numeric CHECK (price > 0)
|
||||||
|
);
|
||||||
|
</programlisting>
|
||||||
|
then the same <literal>inventory_item</> composite type shown above would
|
||||||
|
come into being as a
|
||||||
|
byproduct, and could be used just as above. Note however an important
|
||||||
|
restriction of the current implementation: since no constraints are
|
||||||
|
associated with a composite type, the constraints shown in the table
|
||||||
|
definition <emphasis>do not apply</> to values of the composite type
|
||||||
|
outside the table. (A partial workaround is to use domain
|
||||||
|
types as members of composite types.)
|
||||||
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
<sect2>
|
<sect2>
|
||||||
@ -178,6 +199,49 @@ SELECT (my_func(...)).field FROM ...
|
|||||||
</para>
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
|
<sect2>
|
||||||
|
<title>Modifying Composite Types</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Here are some examples of the proper syntax for inserting and updating
|
||||||
|
composite columns.
|
||||||
|
First, inserting or updating a whole column:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
INSERT INTO mytab (complex_col) VALUES((1.1,2.2));
|
||||||
|
|
||||||
|
UPDATE mytab SET complex_col = ROW(1.1,2.2) WHERE ...;
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
The first example omits <literal>ROW</>, the second uses it; we
|
||||||
|
could have done it either way.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
We can update an individual subfield of a composite column:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
UPDATE mytab SET complex_col.r = (complex_col).r + 1 WHERE ...;
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
Notice here that we don't need to (and indeed cannot)
|
||||||
|
put parentheses around the column name appearing just after
|
||||||
|
<literal>SET</>, but we do need parentheses when referencing the same
|
||||||
|
column in the expression to the right of the equal sign.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
And we can specify subfields as targets for <command>INSERT</>, too:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
INSERT INTO mytab (complex_col.r, complex_col.i) VALUES(1.1, 2.2);
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
Had we not supplied values for all the subfields of the column, the
|
||||||
|
remaining subfields would have been filled with NULLs.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
<sect2>
|
<sect2>
|
||||||
<title>Composite Type Input and Output Syntax</title>
|
<title>Composite Type Input and Output Syntax</title>
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.163 2004/06/05 19:48:08 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.164 2004/06/09 19:08:14 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -118,6 +118,9 @@ static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate,
|
|||||||
static Datum ExecEvalFieldSelect(FieldSelectState *fstate,
|
static Datum ExecEvalFieldSelect(FieldSelectState *fstate,
|
||||||
ExprContext *econtext,
|
ExprContext *econtext,
|
||||||
bool *isNull, ExprDoneCond *isDone);
|
bool *isNull, ExprDoneCond *isDone);
|
||||||
|
static Datum ExecEvalFieldStore(FieldStoreState *fstate,
|
||||||
|
ExprContext *econtext,
|
||||||
|
bool *isNull, ExprDoneCond *isDone);
|
||||||
static Datum ExecEvalRelabelType(GenericExprState *exprstate,
|
static Datum ExecEvalRelabelType(GenericExprState *exprstate,
|
||||||
ExprContext *econtext,
|
ExprContext *econtext,
|
||||||
bool *isNull, ExprDoneCond *isDone);
|
bool *isNull, ExprDoneCond *isDone);
|
||||||
@ -217,6 +220,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
|
|||||||
ArrayType *array_source;
|
ArrayType *array_source;
|
||||||
ArrayType *resultArray;
|
ArrayType *resultArray;
|
||||||
bool isAssignment = (arrayRef->refassgnexpr != NULL);
|
bool isAssignment = (arrayRef->refassgnexpr != NULL);
|
||||||
|
bool eisnull;
|
||||||
ListCell *l;
|
ListCell *l;
|
||||||
int i = 0,
|
int i = 0,
|
||||||
j = 0;
|
j = 0;
|
||||||
@ -224,39 +228,23 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
|
|||||||
lower;
|
lower;
|
||||||
int *lIndex;
|
int *lIndex;
|
||||||
|
|
||||||
/* Set default values for result flags: non-null, not a set result */
|
array_source = (ArrayType *)
|
||||||
*isNull = false;
|
DatumGetPointer(ExecEvalExpr(astate->refexpr,
|
||||||
if (isDone)
|
econtext,
|
||||||
*isDone = ExprSingleResult;
|
isNull,
|
||||||
|
isDone));
|
||||||
|
|
||||||
if (arrayRef->refexpr != NULL)
|
/*
|
||||||
|
* If refexpr yields NULL, and it's a fetch, then result is NULL.
|
||||||
|
* In the assignment case, we'll cons up something below.
|
||||||
|
*/
|
||||||
|
if (*isNull)
|
||||||
{
|
{
|
||||||
array_source = (ArrayType *)
|
if (isDone && *isDone == ExprEndResult)
|
||||||
DatumGetPointer(ExecEvalExpr(astate->refexpr,
|
return (Datum) NULL; /* end of set result */
|
||||||
econtext,
|
if (!isAssignment)
|
||||||
isNull,
|
|
||||||
isDone));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If refexpr yields NULL, result is always NULL, for now anyway.
|
|
||||||
* (This means you cannot assign to an element or slice of an
|
|
||||||
* array that's NULL; it'll just stay NULL.)
|
|
||||||
*/
|
|
||||||
if (*isNull)
|
|
||||||
return (Datum) NULL;
|
return (Datum) NULL;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Empty refexpr indicates we are doing an INSERT into an array
|
|
||||||
* column. For now, we just take the refassgnexpr (which the
|
|
||||||
* parser will have ensured is an array value) and return it
|
|
||||||
* as-is, ignoring any subscripts that may have been supplied in
|
|
||||||
* the INSERT column list. This is a kluge, but it's not real
|
|
||||||
* clear what the semantics ought to be...
|
|
||||||
*/
|
|
||||||
array_source = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach(l, astate->refupperindexpr)
|
foreach(l, astate->refupperindexpr)
|
||||||
{
|
{
|
||||||
@ -270,14 +258,16 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
|
|||||||
|
|
||||||
upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
|
upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
|
||||||
econtext,
|
econtext,
|
||||||
isNull,
|
&eisnull,
|
||||||
NULL));
|
NULL));
|
||||||
/* If any index expr yields NULL, result is NULL or source array */
|
/* If any index expr yields NULL, result is NULL or source array */
|
||||||
if (*isNull)
|
if (eisnull)
|
||||||
{
|
{
|
||||||
if (!isAssignment || array_source == NULL)
|
if (!isAssignment)
|
||||||
|
{
|
||||||
|
*isNull = true;
|
||||||
return (Datum) NULL;
|
return (Datum) NULL;
|
||||||
*isNull = false;
|
}
|
||||||
return PointerGetDatum(array_source);
|
return PointerGetDatum(array_source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -296,18 +286,20 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
|
|||||||
|
|
||||||
lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
|
lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
|
||||||
econtext,
|
econtext,
|
||||||
isNull,
|
&eisnull,
|
||||||
NULL));
|
NULL));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If any index expr yields NULL, result is NULL or source
|
* If any index expr yields NULL, result is NULL or source
|
||||||
* array
|
* array
|
||||||
*/
|
*/
|
||||||
if (*isNull)
|
if (eisnull)
|
||||||
{
|
{
|
||||||
if (!isAssignment || array_source == NULL)
|
if (!isAssignment)
|
||||||
|
{
|
||||||
|
*isNull = true;
|
||||||
return (Datum) NULL;
|
return (Datum) NULL;
|
||||||
*isNull = false;
|
}
|
||||||
return PointerGetDatum(array_source);
|
return PointerGetDatum(array_source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -321,25 +313,49 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
|
|||||||
|
|
||||||
if (isAssignment)
|
if (isAssignment)
|
||||||
{
|
{
|
||||||
Datum sourceData = ExecEvalExpr(astate->refassgnexpr,
|
Datum sourceData;
|
||||||
econtext,
|
|
||||||
isNull,
|
/*
|
||||||
NULL);
|
* Evaluate the value to be assigned into the array.
|
||||||
|
*
|
||||||
|
* XXX At some point we'll need to look into making the old value of
|
||||||
|
* the array element available via CaseTestExpr, as is done by
|
||||||
|
* ExecEvalFieldStore. This is not needed now but will be needed
|
||||||
|
* to support arrays of composite types; in an assignment to a field
|
||||||
|
* of an array member, the parser would generate a FieldStore that
|
||||||
|
* expects to fetch its input tuple via CaseTestExpr.
|
||||||
|
*/
|
||||||
|
sourceData = ExecEvalExpr(astate->refassgnexpr,
|
||||||
|
econtext,
|
||||||
|
&eisnull,
|
||||||
|
NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For now, can't cope with inserting NULL into an array, so make
|
* For now, can't cope with inserting NULL into an array, so make
|
||||||
* it a no-op per discussion above...
|
* it a no-op per discussion above...
|
||||||
*/
|
*/
|
||||||
|
if (eisnull)
|
||||||
|
return PointerGetDatum(array_source);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For an assignment, if all the subscripts and the input expression
|
||||||
|
* are non-null but the original array is null, then substitute an
|
||||||
|
* empty (zero-dimensional) array and proceed with the assignment.
|
||||||
|
* This only works for varlena arrays, though; for fixed-length
|
||||||
|
* array types we punt and return the null input array.
|
||||||
|
*/
|
||||||
if (*isNull)
|
if (*isNull)
|
||||||
{
|
{
|
||||||
if (array_source == NULL)
|
if (astate->refattrlength > 0) /* fixed-length array? */
|
||||||
return (Datum) NULL;
|
return PointerGetDatum(array_source);
|
||||||
*isNull = false;
|
|
||||||
return PointerGetDatum(array_source);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_source == NULL)
|
array_source = construct_md_array(NULL, 0, NULL, NULL,
|
||||||
return sourceData; /* XXX do something else? */
|
arrayRef->refelemtype,
|
||||||
|
astate->refelemlength,
|
||||||
|
astate->refelembyval,
|
||||||
|
astate->refelemalign);
|
||||||
|
*isNull = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (lIndex == NULL)
|
if (lIndex == NULL)
|
||||||
resultArray = array_set(array_source, i,
|
resultArray = array_set(array_source, i,
|
||||||
@ -2538,6 +2554,120 @@ ExecEvalFieldSelect(FieldSelectState *fstate,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------
|
||||||
|
* ExecEvalFieldStore
|
||||||
|
*
|
||||||
|
* Evaluate a FieldStore node.
|
||||||
|
* ----------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
static Datum
|
||||||
|
ExecEvalFieldStore(FieldStoreState *fstate,
|
||||||
|
ExprContext *econtext,
|
||||||
|
bool *isNull,
|
||||||
|
ExprDoneCond *isDone)
|
||||||
|
{
|
||||||
|
FieldStore *fstore = (FieldStore *) fstate->xprstate.expr;
|
||||||
|
HeapTuple tuple;
|
||||||
|
Datum tupDatum;
|
||||||
|
TupleDesc tupDesc;
|
||||||
|
Datum *values;
|
||||||
|
char *nulls;
|
||||||
|
Datum save_datum;
|
||||||
|
bool save_isNull;
|
||||||
|
ListCell *l1,
|
||||||
|
*l2;
|
||||||
|
|
||||||
|
tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
|
||||||
|
|
||||||
|
if (isDone && *isDone == ExprEndResult)
|
||||||
|
return tupDatum;
|
||||||
|
|
||||||
|
/* Lookup tupdesc if first time through or if type changes */
|
||||||
|
tupDesc = fstate->argdesc;
|
||||||
|
if (tupDesc == NULL ||
|
||||||
|
fstore->resulttype != tupDesc->tdtypeid)
|
||||||
|
{
|
||||||
|
MemoryContext oldcontext;
|
||||||
|
|
||||||
|
tupDesc = lookup_rowtype_tupdesc(fstore->resulttype, -1);
|
||||||
|
/* Copy the tupdesc into query storage for safety */
|
||||||
|
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
|
||||||
|
tupDesc = CreateTupleDescCopy(tupDesc);
|
||||||
|
if (fstate->argdesc)
|
||||||
|
FreeTupleDesc(fstate->argdesc);
|
||||||
|
fstate->argdesc = tupDesc;
|
||||||
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate workspace */
|
||||||
|
values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
|
||||||
|
nulls = (char *) palloc(tupDesc->natts * sizeof(char));
|
||||||
|
|
||||||
|
if (!*isNull)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* heap_deformtuple needs a HeapTuple not a bare HeapTupleHeader.
|
||||||
|
* We set all the fields in the struct just in case.
|
||||||
|
*/
|
||||||
|
HeapTupleHeader tuphdr;
|
||||||
|
HeapTupleData tmptup;
|
||||||
|
|
||||||
|
tuphdr = DatumGetHeapTupleHeader(tupDatum);
|
||||||
|
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr);
|
||||||
|
ItemPointerSetInvalid(&(tmptup.t_self));
|
||||||
|
tmptup.t_tableOid = InvalidOid;
|
||||||
|
tmptup.t_data = tuphdr;
|
||||||
|
|
||||||
|
heap_deformtuple(&tmptup, tupDesc, values, nulls);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Convert null input tuple into an all-nulls row */
|
||||||
|
memset(nulls, 'n', tupDesc->natts * sizeof(char));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Result is never null */
|
||||||
|
*isNull = false;
|
||||||
|
|
||||||
|
save_datum = econtext->caseValue_datum;
|
||||||
|
save_isNull = econtext->caseValue_isNull;
|
||||||
|
|
||||||
|
forboth(l1, fstate->newvals, l2, fstore->fieldnums)
|
||||||
|
{
|
||||||
|
ExprState *newval = (ExprState *) lfirst(l1);
|
||||||
|
AttrNumber fieldnum = lfirst_int(l2);
|
||||||
|
bool eisnull;
|
||||||
|
|
||||||
|
Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use the CaseTestExpr mechanism to pass down the old value of the
|
||||||
|
* field being replaced; this is useful in case we have a nested field
|
||||||
|
* update situation. It's safe to reuse the CASE mechanism because
|
||||||
|
* there cannot be a CASE between here and where the value would be
|
||||||
|
* needed.
|
||||||
|
*/
|
||||||
|
econtext->caseValue_datum = values[fieldnum - 1];
|
||||||
|
econtext->caseValue_isNull = (nulls[fieldnum - 1] == 'n');
|
||||||
|
|
||||||
|
values[fieldnum - 1] = ExecEvalExpr(newval,
|
||||||
|
econtext,
|
||||||
|
&eisnull,
|
||||||
|
NULL);
|
||||||
|
nulls[fieldnum - 1] = eisnull ? 'n' : ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
econtext->caseValue_datum = save_datum;
|
||||||
|
econtext->caseValue_isNull = save_isNull;
|
||||||
|
|
||||||
|
tuple = heap_formtuple(tupDesc, values, nulls);
|
||||||
|
|
||||||
|
pfree(values);
|
||||||
|
pfree(nulls);
|
||||||
|
|
||||||
|
return HeapTupleGetDatum(tuple);
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* ExecEvalRelabelType
|
* ExecEvalRelabelType
|
||||||
*
|
*
|
||||||
@ -2810,6 +2940,18 @@ ExecInitExpr(Expr *node, PlanState *parent)
|
|||||||
state = (ExprState *) fstate;
|
state = (ExprState *) fstate;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case T_FieldStore:
|
||||||
|
{
|
||||||
|
FieldStore *fstore = (FieldStore *) node;
|
||||||
|
FieldStoreState *fstate = makeNode(FieldStoreState);
|
||||||
|
|
||||||
|
fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldStore;
|
||||||
|
fstate->arg = ExecInitExpr(fstore->arg, parent);
|
||||||
|
fstate->newvals = (List *) ExecInitExpr((Expr *) fstore->newvals, parent);
|
||||||
|
fstate->argdesc = NULL;
|
||||||
|
state = (ExprState *) fstate;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case T_RelabelType:
|
case T_RelabelType:
|
||||||
{
|
{
|
||||||
RelabelType *relabel = (RelabelType *) node;
|
RelabelType *relabel = (RelabelType *) node;
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.284 2004/05/30 23:40:27 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.285 2004/06/09 19:08:15 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -844,6 +844,22 @@ _copyFieldSelect(FieldSelect *from)
|
|||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _copyFieldStore
|
||||||
|
*/
|
||||||
|
static FieldStore *
|
||||||
|
_copyFieldStore(FieldStore *from)
|
||||||
|
{
|
||||||
|
FieldStore *newnode = makeNode(FieldStore);
|
||||||
|
|
||||||
|
COPY_NODE_FIELD(arg);
|
||||||
|
COPY_NODE_FIELD(newvals);
|
||||||
|
COPY_NODE_FIELD(fieldnums);
|
||||||
|
COPY_SCALAR_FIELD(resulttype);
|
||||||
|
|
||||||
|
return newnode;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* _copyRelabelType
|
* _copyRelabelType
|
||||||
*/
|
*/
|
||||||
@ -1275,7 +1291,6 @@ _copyColumnRef(ColumnRef *from)
|
|||||||
ColumnRef *newnode = makeNode(ColumnRef);
|
ColumnRef *newnode = makeNode(ColumnRef);
|
||||||
|
|
||||||
COPY_NODE_FIELD(fields);
|
COPY_NODE_FIELD(fields);
|
||||||
COPY_NODE_FIELD(indirection);
|
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
@ -1286,8 +1301,6 @@ _copyParamRef(ParamRef *from)
|
|||||||
ParamRef *newnode = makeNode(ParamRef);
|
ParamRef *newnode = makeNode(ParamRef);
|
||||||
|
|
||||||
COPY_SCALAR_FIELD(number);
|
COPY_SCALAR_FIELD(number);
|
||||||
COPY_NODE_FIELD(fields);
|
|
||||||
COPY_NODE_FIELD(indirection);
|
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
@ -1347,13 +1360,12 @@ _copyAIndices(A_Indices *from)
|
|||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ExprFieldSelect *
|
static A_Indirection *
|
||||||
_copyExprFieldSelect(ExprFieldSelect *from)
|
_copyA_Indirection(A_Indirection *from)
|
||||||
{
|
{
|
||||||
ExprFieldSelect *newnode = makeNode(ExprFieldSelect);
|
A_Indirection *newnode = makeNode(A_Indirection);
|
||||||
|
|
||||||
COPY_NODE_FIELD(arg);
|
COPY_NODE_FIELD(arg);
|
||||||
COPY_NODE_FIELD(fields);
|
|
||||||
COPY_NODE_FIELD(indirection);
|
COPY_NODE_FIELD(indirection);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
@ -2648,6 +2660,9 @@ copyObject(void *from)
|
|||||||
case T_FieldSelect:
|
case T_FieldSelect:
|
||||||
retval = _copyFieldSelect(from);
|
retval = _copyFieldSelect(from);
|
||||||
break;
|
break;
|
||||||
|
case T_FieldStore:
|
||||||
|
retval = _copyFieldStore(from);
|
||||||
|
break;
|
||||||
case T_RelabelType:
|
case T_RelabelType:
|
||||||
retval = _copyRelabelType(from);
|
retval = _copyRelabelType(from);
|
||||||
break;
|
break;
|
||||||
@ -2984,8 +2999,8 @@ copyObject(void *from)
|
|||||||
case T_A_Indices:
|
case T_A_Indices:
|
||||||
retval = _copyAIndices(from);
|
retval = _copyAIndices(from);
|
||||||
break;
|
break;
|
||||||
case T_ExprFieldSelect:
|
case T_A_Indirection:
|
||||||
retval = _copyExprFieldSelect(from);
|
retval = _copyA_Indirection(from);
|
||||||
break;
|
break;
|
||||||
case T_ResTarget:
|
case T_ResTarget:
|
||||||
retval = _copyResTarget(from);
|
retval = _copyResTarget(from);
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.223 2004/05/30 23:40:27 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.224 2004/06/09 19:08:15 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -350,6 +350,17 @@ _equalFieldSelect(FieldSelect *a, FieldSelect *b)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
_equalFieldStore(FieldStore *a, FieldStore *b)
|
||||||
|
{
|
||||||
|
COMPARE_NODE_FIELD(arg);
|
||||||
|
COMPARE_NODE_FIELD(newvals);
|
||||||
|
COMPARE_NODE_FIELD(fieldnums);
|
||||||
|
COMPARE_SCALAR_FIELD(resulttype);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
_equalRelabelType(RelabelType *a, RelabelType *b)
|
_equalRelabelType(RelabelType *a, RelabelType *b)
|
||||||
{
|
{
|
||||||
@ -1428,7 +1439,6 @@ static bool
|
|||||||
_equalColumnRef(ColumnRef *a, ColumnRef *b)
|
_equalColumnRef(ColumnRef *a, ColumnRef *b)
|
||||||
{
|
{
|
||||||
COMPARE_NODE_FIELD(fields);
|
COMPARE_NODE_FIELD(fields);
|
||||||
COMPARE_NODE_FIELD(indirection);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1437,8 +1447,6 @@ static bool
|
|||||||
_equalParamRef(ParamRef *a, ParamRef *b)
|
_equalParamRef(ParamRef *a, ParamRef *b)
|
||||||
{
|
{
|
||||||
COMPARE_SCALAR_FIELD(number);
|
COMPARE_SCALAR_FIELD(number);
|
||||||
COMPARE_NODE_FIELD(fields);
|
|
||||||
COMPARE_NODE_FIELD(indirection);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1474,10 +1482,9 @@ _equalAIndices(A_Indices *a, A_Indices *b)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
_equalExprFieldSelect(ExprFieldSelect *a, ExprFieldSelect *b)
|
_equalA_Indirection(A_Indirection *a, A_Indirection *b)
|
||||||
{
|
{
|
||||||
COMPARE_NODE_FIELD(arg);
|
COMPARE_NODE_FIELD(arg);
|
||||||
COMPARE_NODE_FIELD(fields);
|
|
||||||
COMPARE_NODE_FIELD(indirection);
|
COMPARE_NODE_FIELD(indirection);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1805,6 +1812,9 @@ equal(void *a, void *b)
|
|||||||
case T_FieldSelect:
|
case T_FieldSelect:
|
||||||
retval = _equalFieldSelect(a, b);
|
retval = _equalFieldSelect(a, b);
|
||||||
break;
|
break;
|
||||||
|
case T_FieldStore:
|
||||||
|
retval = _equalFieldStore(a, b);
|
||||||
|
break;
|
||||||
case T_RelabelType:
|
case T_RelabelType:
|
||||||
retval = _equalRelabelType(a, b);
|
retval = _equalRelabelType(a, b);
|
||||||
break;
|
break;
|
||||||
@ -2127,8 +2137,8 @@ equal(void *a, void *b)
|
|||||||
case T_A_Indices:
|
case T_A_Indices:
|
||||||
retval = _equalAIndices(a, b);
|
retval = _equalAIndices(a, b);
|
||||||
break;
|
break;
|
||||||
case T_ExprFieldSelect:
|
case T_A_Indirection:
|
||||||
retval = _equalExprFieldSelect(a, b);
|
retval = _equalA_Indirection(a, b);
|
||||||
break;
|
break;
|
||||||
case T_ResTarget:
|
case T_ResTarget:
|
||||||
retval = _equalResTarget(a, b);
|
retval = _equalResTarget(a, b);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.238 2004/05/30 23:40:27 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.239 2004/06/09 19:08:15 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Every node type that can appear in stored rules' parsetrees *must*
|
* Every node type that can appear in stored rules' parsetrees *must*
|
||||||
@ -745,6 +745,17 @@ _outFieldSelect(StringInfo str, FieldSelect *node)
|
|||||||
WRITE_INT_FIELD(resulttypmod);
|
WRITE_INT_FIELD(resulttypmod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_outFieldStore(StringInfo str, FieldStore *node)
|
||||||
|
{
|
||||||
|
WRITE_NODE_TYPE("FIELDSTORE");
|
||||||
|
|
||||||
|
WRITE_NODE_FIELD(arg);
|
||||||
|
WRITE_NODE_FIELD(newvals);
|
||||||
|
WRITE_NODE_FIELD(fieldnums);
|
||||||
|
WRITE_OID_FIELD(resulttype);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_outRelabelType(StringInfo str, RelabelType *node)
|
_outRelabelType(StringInfo str, RelabelType *node)
|
||||||
{
|
{
|
||||||
@ -1166,8 +1177,24 @@ _outSelectStmt(StringInfo str, SelectStmt *node)
|
|||||||
{
|
{
|
||||||
WRITE_NODE_TYPE("SELECT");
|
WRITE_NODE_TYPE("SELECT");
|
||||||
|
|
||||||
/* XXX this is pretty durn incomplete */
|
|
||||||
WRITE_NODE_FIELD(whereClause);
|
WRITE_NODE_FIELD(whereClause);
|
||||||
|
WRITE_NODE_FIELD(distinctClause);
|
||||||
|
WRITE_NODE_FIELD(into);
|
||||||
|
WRITE_NODE_FIELD(intoColNames);
|
||||||
|
WRITE_ENUM_FIELD(intoHasOids, ContainsOids);
|
||||||
|
WRITE_NODE_FIELD(targetList);
|
||||||
|
WRITE_NODE_FIELD(fromClause);
|
||||||
|
WRITE_NODE_FIELD(whereClause);
|
||||||
|
WRITE_NODE_FIELD(groupClause);
|
||||||
|
WRITE_NODE_FIELD(havingClause);
|
||||||
|
WRITE_NODE_FIELD(sortClause);
|
||||||
|
WRITE_NODE_FIELD(limitOffset);
|
||||||
|
WRITE_NODE_FIELD(limitCount);
|
||||||
|
WRITE_NODE_FIELD(forUpdate);
|
||||||
|
WRITE_ENUM_FIELD(op, SetOperation);
|
||||||
|
WRITE_BOOL_FIELD(all);
|
||||||
|
WRITE_NODE_FIELD(larg);
|
||||||
|
WRITE_NODE_FIELD(rarg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1181,6 +1208,15 @@ _outFuncCall(StringInfo str, FuncCall *node)
|
|||||||
WRITE_BOOL_FIELD(agg_distinct);
|
WRITE_BOOL_FIELD(agg_distinct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_outDefElem(StringInfo str, DefElem *node)
|
||||||
|
{
|
||||||
|
WRITE_NODE_TYPE("DEFELEM");
|
||||||
|
|
||||||
|
WRITE_STRING_FIELD(defname);
|
||||||
|
WRITE_NODE_FIELD(arg);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_outColumnDef(StringInfo str, ColumnDef *node)
|
_outColumnDef(StringInfo str, ColumnDef *node)
|
||||||
{
|
{
|
||||||
@ -1439,7 +1475,6 @@ _outColumnRef(StringInfo str, ColumnRef *node)
|
|||||||
WRITE_NODE_TYPE("COLUMNREF");
|
WRITE_NODE_TYPE("COLUMNREF");
|
||||||
|
|
||||||
WRITE_NODE_FIELD(fields);
|
WRITE_NODE_FIELD(fields);
|
||||||
WRITE_NODE_FIELD(indirection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1448,29 +1483,45 @@ _outParamRef(StringInfo str, ParamRef *node)
|
|||||||
WRITE_NODE_TYPE("PARAMREF");
|
WRITE_NODE_TYPE("PARAMREF");
|
||||||
|
|
||||||
WRITE_INT_FIELD(number);
|
WRITE_INT_FIELD(number);
|
||||||
WRITE_NODE_FIELD(fields);
|
|
||||||
WRITE_NODE_FIELD(indirection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_outAConst(StringInfo str, A_Const *node)
|
_outAConst(StringInfo str, A_Const *node)
|
||||||
{
|
{
|
||||||
WRITE_NODE_TYPE("CONST ");
|
WRITE_NODE_TYPE("A_CONST");
|
||||||
|
|
||||||
_outValue(str, &(node->val));
|
_outValue(str, &(node->val));
|
||||||
WRITE_NODE_FIELD(typename);
|
WRITE_NODE_FIELD(typename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_outExprFieldSelect(StringInfo str, ExprFieldSelect *node)
|
_outA_Indices(StringInfo str, A_Indices *node)
|
||||||
{
|
{
|
||||||
WRITE_NODE_TYPE("EXPRFIELDSELECT");
|
WRITE_NODE_TYPE("A_INDICES");
|
||||||
|
|
||||||
|
WRITE_NODE_FIELD(lidx);
|
||||||
|
WRITE_NODE_FIELD(uidx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_outA_Indirection(StringInfo str, A_Indirection *node)
|
||||||
|
{
|
||||||
|
WRITE_NODE_TYPE("A_INDIRECTION");
|
||||||
|
|
||||||
WRITE_NODE_FIELD(arg);
|
WRITE_NODE_FIELD(arg);
|
||||||
WRITE_NODE_FIELD(fields);
|
|
||||||
WRITE_NODE_FIELD(indirection);
|
WRITE_NODE_FIELD(indirection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_outResTarget(StringInfo str, ResTarget *node)
|
||||||
|
{
|
||||||
|
WRITE_NODE_TYPE("RESTARGET");
|
||||||
|
|
||||||
|
WRITE_STRING_FIELD(name);
|
||||||
|
WRITE_NODE_FIELD(indirection);
|
||||||
|
WRITE_NODE_FIELD(val);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_outConstraint(StringInfo str, Constraint *node)
|
_outConstraint(StringInfo str, Constraint *node)
|
||||||
{
|
{
|
||||||
@ -1666,6 +1717,9 @@ _outNode(StringInfo str, void *obj)
|
|||||||
case T_FieldSelect:
|
case T_FieldSelect:
|
||||||
_outFieldSelect(str, obj);
|
_outFieldSelect(str, obj);
|
||||||
break;
|
break;
|
||||||
|
case T_FieldStore:
|
||||||
|
_outFieldStore(str, obj);
|
||||||
|
break;
|
||||||
case T_RelabelType:
|
case T_RelabelType:
|
||||||
_outRelabelType(str, obj);
|
_outRelabelType(str, obj);
|
||||||
break;
|
break;
|
||||||
@ -1815,8 +1869,14 @@ _outNode(StringInfo str, void *obj)
|
|||||||
case T_A_Const:
|
case T_A_Const:
|
||||||
_outAConst(str, obj);
|
_outAConst(str, obj);
|
||||||
break;
|
break;
|
||||||
case T_ExprFieldSelect:
|
case T_A_Indices:
|
||||||
_outExprFieldSelect(str, obj);
|
_outA_Indices(str, obj);
|
||||||
|
break;
|
||||||
|
case T_A_Indirection:
|
||||||
|
_outA_Indirection(str, obj);
|
||||||
|
break;
|
||||||
|
case T_ResTarget:
|
||||||
|
_outResTarget(str, obj);
|
||||||
break;
|
break;
|
||||||
case T_Constraint:
|
case T_Constraint:
|
||||||
_outConstraint(str, obj);
|
_outConstraint(str, obj);
|
||||||
@ -1827,6 +1887,9 @@ _outNode(StringInfo str, void *obj)
|
|||||||
case T_FuncCall:
|
case T_FuncCall:
|
||||||
_outFuncCall(str, obj);
|
_outFuncCall(str, obj);
|
||||||
break;
|
break;
|
||||||
|
case T_DefElem:
|
||||||
|
_outDefElem(str, obj);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.171 2004/05/30 23:40:27 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.172 2004/06/09 19:08:15 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Path and Plan nodes do not have any readfuncs support, because we
|
* Path and Plan nodes do not have any readfuncs support, because we
|
||||||
@ -543,6 +543,22 @@ _readFieldSelect(void)
|
|||||||
READ_DONE();
|
READ_DONE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _readFieldStore
|
||||||
|
*/
|
||||||
|
static FieldStore *
|
||||||
|
_readFieldStore(void)
|
||||||
|
{
|
||||||
|
READ_LOCALS(FieldStore);
|
||||||
|
|
||||||
|
READ_NODE_FIELD(arg);
|
||||||
|
READ_NODE_FIELD(newvals);
|
||||||
|
READ_NODE_FIELD(fieldnums);
|
||||||
|
READ_OID_FIELD(resulttype);
|
||||||
|
|
||||||
|
READ_DONE();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* _readRelabelType
|
* _readRelabelType
|
||||||
*/
|
*/
|
||||||
@ -814,17 +830,6 @@ _readFromExpr(void)
|
|||||||
* Stuff from parsenodes.h.
|
* Stuff from parsenodes.h.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static ColumnRef *
|
|
||||||
_readColumnRef(void)
|
|
||||||
{
|
|
||||||
READ_LOCALS(ColumnRef);
|
|
||||||
|
|
||||||
READ_NODE_FIELD(fields);
|
|
||||||
READ_NODE_FIELD(indirection);
|
|
||||||
|
|
||||||
READ_DONE();
|
|
||||||
}
|
|
||||||
|
|
||||||
static ColumnDef *
|
static ColumnDef *
|
||||||
_readColumnDef(void)
|
_readColumnDef(void)
|
||||||
{
|
{
|
||||||
@ -859,18 +864,6 @@ _readTypeName(void)
|
|||||||
READ_DONE();
|
READ_DONE();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ExprFieldSelect *
|
|
||||||
_readExprFieldSelect(void)
|
|
||||||
{
|
|
||||||
READ_LOCALS(ExprFieldSelect);
|
|
||||||
|
|
||||||
READ_NODE_FIELD(arg);
|
|
||||||
READ_NODE_FIELD(fields);
|
|
||||||
READ_NODE_FIELD(indirection);
|
|
||||||
|
|
||||||
READ_DONE();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* _readRangeTblEntry
|
* _readRangeTblEntry
|
||||||
*/
|
*/
|
||||||
@ -974,6 +967,8 @@ parseNodeString(void)
|
|||||||
return_value = _readSubLink();
|
return_value = _readSubLink();
|
||||||
else if (MATCH("FIELDSELECT", 11))
|
else if (MATCH("FIELDSELECT", 11))
|
||||||
return_value = _readFieldSelect();
|
return_value = _readFieldSelect();
|
||||||
|
else if (MATCH("FIELDSTORE", 10))
|
||||||
|
return_value = _readFieldStore();
|
||||||
else if (MATCH("RELABELTYPE", 11))
|
else if (MATCH("RELABELTYPE", 11))
|
||||||
return_value = _readRelabelType();
|
return_value = _readRelabelType();
|
||||||
else if (MATCH("CASE", 4))
|
else if (MATCH("CASE", 4))
|
||||||
@ -1008,14 +1003,10 @@ parseNodeString(void)
|
|||||||
return_value = _readJoinExpr();
|
return_value = _readJoinExpr();
|
||||||
else if (MATCH("FROMEXPR", 8))
|
else if (MATCH("FROMEXPR", 8))
|
||||||
return_value = _readFromExpr();
|
return_value = _readFromExpr();
|
||||||
else if (MATCH("COLUMNREF", 9))
|
|
||||||
return_value = _readColumnRef();
|
|
||||||
else if (MATCH("COLUMNDEF", 9))
|
else if (MATCH("COLUMNDEF", 9))
|
||||||
return_value = _readColumnDef();
|
return_value = _readColumnDef();
|
||||||
else if (MATCH("TYPENAME", 8))
|
else if (MATCH("TYPENAME", 8))
|
||||||
return_value = _readTypeName();
|
return_value = _readTypeName();
|
||||||
else if (MATCH("EXPRFIELDSELECT", 15))
|
|
||||||
return_value = _readExprFieldSelect();
|
|
||||||
else if (MATCH("RTE", 3))
|
else if (MATCH("RTE", 3))
|
||||||
return_value = _readRangeTblEntry();
|
return_value = _readRangeTblEntry();
|
||||||
else if (MATCH("NOTIFY", 6))
|
else if (MATCH("NOTIFY", 6))
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.174 2004/06/05 19:48:08 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.175 2004/06/09 19:08:16 tgl Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -724,6 +724,13 @@ contain_nonstrict_functions_walker(Node *node, void *context)
|
|||||||
/* an aggregate could return non-null with null input */
|
/* an aggregate could return non-null with null input */
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (IsA(node, ArrayRef))
|
||||||
|
{
|
||||||
|
/* array assignment is nonstrict */
|
||||||
|
if (((ArrayRef *) node)->refassgnexpr != NULL)
|
||||||
|
return true;
|
||||||
|
/* else fall through to check args */
|
||||||
|
}
|
||||||
if (IsA(node, FuncExpr))
|
if (IsA(node, FuncExpr))
|
||||||
{
|
{
|
||||||
FuncExpr *expr = (FuncExpr *) node;
|
FuncExpr *expr = (FuncExpr *) node;
|
||||||
@ -771,6 +778,8 @@ contain_nonstrict_functions_walker(Node *node, void *context)
|
|||||||
}
|
}
|
||||||
if (IsA(node, SubPlan))
|
if (IsA(node, SubPlan))
|
||||||
return true;
|
return true;
|
||||||
|
if (IsA(node, FieldStore))
|
||||||
|
return true;
|
||||||
if (IsA(node, CaseExpr))
|
if (IsA(node, CaseExpr))
|
||||||
return true;
|
return true;
|
||||||
if (IsA(node, CaseWhen))
|
if (IsA(node, CaseWhen))
|
||||||
@ -2450,6 +2459,16 @@ expression_tree_walker(Node *node,
|
|||||||
break;
|
break;
|
||||||
case T_FieldSelect:
|
case T_FieldSelect:
|
||||||
return walker(((FieldSelect *) node)->arg, context);
|
return walker(((FieldSelect *) node)->arg, context);
|
||||||
|
case T_FieldStore:
|
||||||
|
{
|
||||||
|
FieldStore *fstore = (FieldStore *) node;
|
||||||
|
|
||||||
|
if (walker(fstore->arg, context))
|
||||||
|
return true;
|
||||||
|
if (walker(fstore->newvals, context))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case T_RelabelType:
|
case T_RelabelType:
|
||||||
return walker(((RelabelType *) node)->arg, context);
|
return walker(((RelabelType *) node)->arg, context);
|
||||||
case T_CaseExpr:
|
case T_CaseExpr:
|
||||||
@ -2840,6 +2859,18 @@ expression_tree_mutator(Node *node,
|
|||||||
return (Node *) newnode;
|
return (Node *) newnode;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case T_FieldStore:
|
||||||
|
{
|
||||||
|
FieldStore *fstore = (FieldStore *) node;
|
||||||
|
FieldStore *newnode;
|
||||||
|
|
||||||
|
FLATCOPY(newnode, fstore, FieldStore);
|
||||||
|
MUTATE(newnode->arg, fstore->arg, Expr *);
|
||||||
|
MUTATE(newnode->newvals, fstore->newvals, List *);
|
||||||
|
newnode->fieldnums = list_copy(fstore->fieldnums);
|
||||||
|
return (Node *) newnode;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case T_RelabelType:
|
case T_RelabelType:
|
||||||
{
|
{
|
||||||
RelabelType *relabel = (RelabelType *) node;
|
RelabelType *relabel = (RelabelType *) node;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.303 2004/06/04 03:24:04 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.304 2004/06/09 19:08:16 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -2461,6 +2461,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
|
|||||||
if (origTargetList == NULL)
|
if (origTargetList == NULL)
|
||||||
elog(ERROR, "UPDATE target count mismatch --- internal error");
|
elog(ERROR, "UPDATE target count mismatch --- internal error");
|
||||||
origTarget = (ResTarget *) lfirst(origTargetList);
|
origTarget = (ResTarget *) lfirst(origTargetList);
|
||||||
|
Assert(IsA(origTarget, ResTarget));
|
||||||
|
|
||||||
updateTargetListEntry(pstate, tle, origTarget->name,
|
updateTargetListEntry(pstate, tle, origTarget->name,
|
||||||
attnameAttNum(pstate->p_target_relation,
|
attnameAttNum(pstate->p_target_relation,
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.460 2004/06/02 21:01:09 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.461 2004/06/09 19:08:17 tgl Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -75,6 +75,7 @@ static bool QueryIsRule = FALSE;
|
|||||||
*/
|
*/
|
||||||
/*#define __YYSCLASS*/
|
/*#define __YYSCLASS*/
|
||||||
|
|
||||||
|
static Node *makeColumnRef(char *relname, List *indirection);
|
||||||
static Node *makeTypeCast(Node *arg, TypeName *typename);
|
static Node *makeTypeCast(Node *arg, TypeName *typename);
|
||||||
static Node *makeStringConst(char *str, TypeName *typename);
|
static Node *makeStringConst(char *str, TypeName *typename);
|
||||||
static Node *makeIntConst(int val);
|
static Node *makeIntConst(int val);
|
||||||
@ -84,6 +85,7 @@ static Node *makeRowNullTest(NullTestType test, RowExpr *row);
|
|||||||
static DefElem *makeDefElem(char *name, Node *arg);
|
static DefElem *makeDefElem(char *name, Node *arg);
|
||||||
static A_Const *makeBoolAConst(bool state);
|
static A_Const *makeBoolAConst(bool state);
|
||||||
static FuncCall *makeOverlaps(List *largs, List *rargs);
|
static FuncCall *makeOverlaps(List *largs, List *rargs);
|
||||||
|
static List *check_func_name(List *names);
|
||||||
static List *extractArgTypes(List *parameters);
|
static List *extractArgTypes(List *parameters);
|
||||||
static SelectStmt *findLeftmostSelect(SelectStmt *node);
|
static SelectStmt *findLeftmostSelect(SelectStmt *node);
|
||||||
static void insertSelectOptions(SelectStmt *stmt,
|
static void insertSelectOptions(SelectStmt *stmt,
|
||||||
@ -110,7 +112,6 @@ static void doNegateFloat(Value *v);
|
|||||||
List *list;
|
List *list;
|
||||||
Node *node;
|
Node *node;
|
||||||
Value *value;
|
Value *value;
|
||||||
ColumnRef *columnref;
|
|
||||||
ObjectType objtype;
|
ObjectType objtype;
|
||||||
|
|
||||||
TypeName *typnam;
|
TypeName *typnam;
|
||||||
@ -221,9 +222,9 @@ static void doNegateFloat(Value *v);
|
|||||||
sort_clause opt_sort_clause sortby_list index_params
|
sort_clause opt_sort_clause sortby_list index_params
|
||||||
name_list from_clause from_list opt_array_bounds
|
name_list from_clause from_list opt_array_bounds
|
||||||
qualified_name_list any_name any_name_list
|
qualified_name_list any_name any_name_list
|
||||||
any_operator expr_list dotted_name attrs
|
any_operator expr_list attrs
|
||||||
target_list update_target_list insert_column_list
|
target_list update_target_list insert_column_list
|
||||||
insert_target_list def_list opt_indirection
|
insert_target_list def_list indirection opt_indirection
|
||||||
group_clause TriggerFuncArgs select_limit
|
group_clause TriggerFuncArgs select_limit
|
||||||
opt_select_limit opclass_item_list transaction_mode_list
|
opt_select_limit opclass_item_list transaction_mode_list
|
||||||
transaction_mode_list_or_empty
|
transaction_mode_list_or_empty
|
||||||
@ -274,8 +275,8 @@ static void doNegateFloat(Value *v);
|
|||||||
%type <node> TableElement ConstraintElem TableFuncElement
|
%type <node> TableElement ConstraintElem TableFuncElement
|
||||||
%type <node> columnDef
|
%type <node> columnDef
|
||||||
%type <defelt> def_elem
|
%type <defelt> def_elem
|
||||||
%type <node> def_arg columnElem where_clause insert_column_item
|
%type <node> def_arg columnElem where_clause
|
||||||
a_expr b_expr c_expr AexprConst
|
a_expr b_expr c_expr AexprConst indirection_el columnref
|
||||||
in_expr having_clause func_table array_expr
|
in_expr having_clause func_table array_expr
|
||||||
%type <list> row type_list array_expr_list
|
%type <list> row type_list array_expr_list
|
||||||
%type <node> case_expr case_arg when_clause case_default
|
%type <node> case_expr case_arg when_clause case_default
|
||||||
@ -284,14 +285,13 @@ static void doNegateFloat(Value *v);
|
|||||||
%type <list> OptCreateAs CreateAsList
|
%type <list> OptCreateAs CreateAsList
|
||||||
%type <node> CreateAsElement
|
%type <node> CreateAsElement
|
||||||
%type <value> NumericOnly FloatOnly IntegerOnly
|
%type <value> NumericOnly FloatOnly IntegerOnly
|
||||||
%type <columnref> columnref
|
|
||||||
%type <alias> alias_clause
|
%type <alias> alias_clause
|
||||||
%type <sortby> sortby
|
%type <sortby> sortby
|
||||||
%type <ielem> index_elem
|
%type <ielem> index_elem
|
||||||
%type <node> table_ref
|
%type <node> table_ref
|
||||||
%type <jexpr> joined_table
|
%type <jexpr> joined_table
|
||||||
%type <range> relation_expr
|
%type <range> relation_expr
|
||||||
%type <target> target_el insert_target_el update_target_el
|
%type <target> target_el insert_target_el update_target_el insert_column_item
|
||||||
|
|
||||||
%type <typnam> Typename SimpleTypename ConstTypename
|
%type <typnam> Typename SimpleTypename ConstTypename
|
||||||
GenericType Numeric opt_float
|
GenericType Numeric opt_float
|
||||||
@ -2102,12 +2102,11 @@ opt_trusted:
|
|||||||
|
|
||||||
/* This ought to be just func_name, but that causes reduce/reduce conflicts
|
/* This ought to be just func_name, but that causes reduce/reduce conflicts
|
||||||
* (CREATE LANGUAGE is the only place where func_name isn't followed by '(').
|
* (CREATE LANGUAGE is the only place where func_name isn't followed by '(').
|
||||||
* Work around by using name and dotted_name separately.
|
* Work around by using simple names, instead.
|
||||||
*/
|
*/
|
||||||
handler_name:
|
handler_name:
|
||||||
name
|
name { $$ = list_make1(makeString($1)); }
|
||||||
{ $$ = list_make1(makeString($1)); }
|
| name attrs { $$ = lcons(makeString($1), $2); }
|
||||||
| dotted_name { $$ = $1; }
|
|
||||||
;
|
;
|
||||||
|
|
||||||
opt_lancompiler:
|
opt_lancompiler:
|
||||||
@ -2578,9 +2577,20 @@ any_name_list:
|
|||||||
;
|
;
|
||||||
|
|
||||||
any_name: ColId { $$ = list_make1(makeString($1)); }
|
any_name: ColId { $$ = list_make1(makeString($1)); }
|
||||||
| dotted_name { $$ = $1; }
|
| ColId attrs { $$ = lcons(makeString($1), $2); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The slightly convoluted way of writing this production avoids reduce/reduce
|
||||||
|
* errors against indirection_el.
|
||||||
|
*/
|
||||||
|
attrs: '.' attr_name
|
||||||
|
{ $$ = list_make1(makeString($2)); }
|
||||||
|
| '.' attr_name attrs
|
||||||
|
{ $$ = lcons(makeString($2), $3); }
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* QUERY:
|
* QUERY:
|
||||||
@ -4387,7 +4397,8 @@ insert_rest:
|
|||||||
;
|
;
|
||||||
|
|
||||||
insert_column_list:
|
insert_column_list:
|
||||||
insert_column_item { $$ = list_make1($1); }
|
insert_column_item
|
||||||
|
{ $$ = list_make1($1); }
|
||||||
| insert_column_list ',' insert_column_item
|
| insert_column_list ',' insert_column_item
|
||||||
{ $$ = lappend($1, $3); }
|
{ $$ = lappend($1, $3); }
|
||||||
;
|
;
|
||||||
@ -4395,11 +4406,10 @@ insert_column_list:
|
|||||||
insert_column_item:
|
insert_column_item:
|
||||||
ColId opt_indirection
|
ColId opt_indirection
|
||||||
{
|
{
|
||||||
ResTarget *n = makeNode(ResTarget);
|
$$ = makeNode(ResTarget);
|
||||||
n->name = $1;
|
$$->name = $1;
|
||||||
n->indirection = $2;
|
$$->indirection = $2;
|
||||||
n->val = NULL;
|
$$->val = NULL;
|
||||||
$$ = (Node *)n;
|
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -6203,35 +6213,28 @@ b_expr: c_expr
|
|||||||
* inside parentheses, such as function arguments; that cannot introduce
|
* inside parentheses, such as function arguments; that cannot introduce
|
||||||
* ambiguity to the b_expr syntax.
|
* ambiguity to the b_expr syntax.
|
||||||
*/
|
*/
|
||||||
c_expr: columnref { $$ = (Node *) $1; }
|
c_expr: columnref { $$ = $1; }
|
||||||
| AexprConst { $$ = $1; }
|
| AexprConst { $$ = $1; }
|
||||||
| PARAM attrs opt_indirection
|
| PARAM opt_indirection
|
||||||
{
|
{
|
||||||
/*
|
ParamRef *p = makeNode(ParamRef);
|
||||||
* PARAM without field names is considered a constant,
|
p->number = $1;
|
||||||
* but with 'em, it is not. Not very consistent ...
|
if ($2)
|
||||||
*/
|
{
|
||||||
ParamRef *n = makeNode(ParamRef);
|
A_Indirection *n = makeNode(A_Indirection);
|
||||||
n->number = $1;
|
n->arg = (Node *) p;
|
||||||
n->fields = $2;
|
n->indirection = $2;
|
||||||
n->indirection = $3;
|
$$ = (Node *) n;
|
||||||
$$ = (Node *)n;
|
}
|
||||||
}
|
else
|
||||||
| '(' a_expr ')' attrs opt_indirection
|
$$ = (Node *) p;
|
||||||
{
|
|
||||||
ExprFieldSelect *n = makeNode(ExprFieldSelect);
|
|
||||||
n->arg = $2;
|
|
||||||
n->fields = $4;
|
|
||||||
n->indirection = $5;
|
|
||||||
$$ = (Node *)n;
|
|
||||||
}
|
}
|
||||||
| '(' a_expr ')' opt_indirection
|
| '(' a_expr ')' opt_indirection
|
||||||
{
|
{
|
||||||
if ($4)
|
if ($4)
|
||||||
{
|
{
|
||||||
ExprFieldSelect *n = makeNode(ExprFieldSelect);
|
A_Indirection *n = makeNode(A_Indirection);
|
||||||
n->arg = $2;
|
n->arg = $2;
|
||||||
n->fields = NIL;
|
|
||||||
n->indirection = $4;
|
n->indirection = $4;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
@ -6806,25 +6809,6 @@ subquery_Op:
|
|||||||
*/
|
*/
|
||||||
;
|
;
|
||||||
|
|
||||||
opt_indirection:
|
|
||||||
opt_indirection '[' a_expr ']'
|
|
||||||
{
|
|
||||||
A_Indices *ai = makeNode(A_Indices);
|
|
||||||
ai->lidx = NULL;
|
|
||||||
ai->uidx = $3;
|
|
||||||
$$ = lappend($1, ai);
|
|
||||||
}
|
|
||||||
| opt_indirection '[' a_expr ':' a_expr ']'
|
|
||||||
{
|
|
||||||
A_Indices *ai = makeNode(A_Indices);
|
|
||||||
ai->lidx = $3;
|
|
||||||
ai->uidx = $5;
|
|
||||||
$$ = lappend($1, ai);
|
|
||||||
}
|
|
||||||
| /*EMPTY*/
|
|
||||||
{ $$ = NIL; }
|
|
||||||
;
|
|
||||||
|
|
||||||
expr_list: a_expr
|
expr_list: a_expr
|
||||||
{
|
{
|
||||||
$$ = list_make1($1);
|
$$ = list_make1($1);
|
||||||
@ -7050,42 +7034,58 @@ case_arg: a_expr { $$ = $1; }
|
|||||||
* references can be accepted. Note that when there are more than two
|
* references can be accepted. Note that when there are more than two
|
||||||
* dotted names, the first name is not actually a relation name...
|
* dotted names, the first name is not actually a relation name...
|
||||||
*/
|
*/
|
||||||
columnref: relation_name opt_indirection
|
columnref: relation_name
|
||||||
{
|
{
|
||||||
$$ = makeNode(ColumnRef);
|
$$ = makeColumnRef($1, NIL);
|
||||||
$$->fields = list_make1(makeString($1));
|
|
||||||
$$->indirection = $2;
|
|
||||||
}
|
}
|
||||||
| dotted_name opt_indirection
|
| relation_name indirection
|
||||||
{
|
{
|
||||||
$$ = makeNode(ColumnRef);
|
$$ = makeColumnRef($1, $2);
|
||||||
$$->fields = $1;
|
|
||||||
$$->indirection = $2;
|
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
dotted_name:
|
indirection_el:
|
||||||
relation_name attrs
|
'.' attr_name
|
||||||
{ $$ = lcons(makeString($1), $2); }
|
{
|
||||||
;
|
$$ = (Node *) makeString($2);
|
||||||
|
}
|
||||||
attrs: '.' attr_name
|
|
||||||
{ $$ = list_make1(makeString($2)); }
|
|
||||||
| '.' '*'
|
| '.' '*'
|
||||||
{ $$ = list_make1(makeString("*")); }
|
{
|
||||||
| '.' attr_name attrs
|
$$ = (Node *) makeString("*");
|
||||||
{ $$ = lcons(makeString($2), $3); }
|
}
|
||||||
|
| '[' a_expr ']'
|
||||||
|
{
|
||||||
|
A_Indices *ai = makeNode(A_Indices);
|
||||||
|
ai->lidx = NULL;
|
||||||
|
ai->uidx = $2;
|
||||||
|
$$ = (Node *) ai;
|
||||||
|
}
|
||||||
|
| '[' a_expr ':' a_expr ']'
|
||||||
|
{
|
||||||
|
A_Indices *ai = makeNode(A_Indices);
|
||||||
|
ai->lidx = $2;
|
||||||
|
ai->uidx = $4;
|
||||||
|
$$ = (Node *) ai;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
indirection:
|
||||||
|
indirection_el { $$ = list_make1($1); }
|
||||||
|
| indirection indirection_el { $$ = lappend($1, $2); }
|
||||||
|
;
|
||||||
|
|
||||||
|
opt_indirection:
|
||||||
|
/*EMPTY*/ { $$ = NIL; }
|
||||||
|
| opt_indirection indirection_el { $$ = lappend($1, $2); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* target lists
|
* target lists for SELECT, UPDATE, INSERT
|
||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/* Target lists as found in SELECT ... and INSERT VALUES ( ... ) */
|
|
||||||
|
|
||||||
target_list:
|
target_list:
|
||||||
target_el { $$ = list_make1($1); }
|
target_el { $$ = list_make1($1); }
|
||||||
| target_list ',' target_el { $$ = lappend($1, $3); }
|
| target_list ',' target_el { $$ = lappend($1, $3); }
|
||||||
@ -7110,7 +7110,7 @@ target_el: a_expr AS ColLabel
|
|||||||
{
|
{
|
||||||
ColumnRef *n = makeNode(ColumnRef);
|
ColumnRef *n = makeNode(ColumnRef);
|
||||||
n->fields = list_make1(makeString("*"));
|
n->fields = list_make1(makeString("*"));
|
||||||
n->indirection = NIL;
|
|
||||||
$$ = makeNode(ResTarget);
|
$$ = makeNode(ResTarget);
|
||||||
$$->name = NULL;
|
$$->name = NULL;
|
||||||
$$->indirection = NIL;
|
$$->indirection = NIL;
|
||||||
@ -7118,12 +7118,6 @@ target_el: a_expr AS ColLabel
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
/* Target list as found in UPDATE table SET ...
|
|
||||||
| '(' row_ ')' = '(' row_ ')'
|
|
||||||
{
|
|
||||||
$$ = NULL;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
update_target_list:
|
update_target_list:
|
||||||
update_target_el { $$ = list_make1($1); }
|
update_target_el { $$ = list_make1($1); }
|
||||||
| update_target_list ',' update_target_el { $$ = lappend($1,$3); }
|
| update_target_list ',' update_target_el { $$ = lappend($1,$3); }
|
||||||
@ -7153,7 +7147,13 @@ insert_target_list:
|
|||||||
;
|
;
|
||||||
|
|
||||||
insert_target_el:
|
insert_target_el:
|
||||||
target_el { $$ = $1; }
|
a_expr
|
||||||
|
{
|
||||||
|
$$ = makeNode(ResTarget);
|
||||||
|
$$->name = NULL;
|
||||||
|
$$->indirection = NIL;
|
||||||
|
$$->val = (Node *)$1;
|
||||||
|
}
|
||||||
| DEFAULT
|
| DEFAULT
|
||||||
{
|
{
|
||||||
$$ = makeNode(ResTarget);
|
$$ = makeNode(ResTarget);
|
||||||
@ -7188,26 +7188,26 @@ qualified_name:
|
|||||||
$$->schemaname = NULL;
|
$$->schemaname = NULL;
|
||||||
$$->relname = $1;
|
$$->relname = $1;
|
||||||
}
|
}
|
||||||
| dotted_name
|
| relation_name attrs
|
||||||
{
|
{
|
||||||
$$ = makeNode(RangeVar);
|
$$ = makeNode(RangeVar);
|
||||||
switch (list_length($1))
|
switch (list_length($2))
|
||||||
{
|
{
|
||||||
case 2:
|
case 1:
|
||||||
$$->catalogname = NULL;
|
$$->catalogname = NULL;
|
||||||
$$->schemaname = strVal(linitial($1));
|
$$->schemaname = $1;
|
||||||
$$->relname = strVal(lsecond($1));
|
$$->relname = strVal(linitial($2));
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 2:
|
||||||
$$->catalogname = strVal(linitial($1));
|
$$->catalogname = $1;
|
||||||
$$->schemaname = strVal(lsecond($1));
|
$$->schemaname = strVal(linitial($2));
|
||||||
$$->relname = strVal(lthird($1));
|
$$->relname = strVal(lsecond($2));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("improper qualified name (too many dotted names): %s",
|
errmsg("improper qualified name (too many dotted names): %s",
|
||||||
NameListToString($1))));
|
NameListToString(lcons(makeString($1), $2)))));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7234,9 +7234,18 @@ index_name: ColId { $$ = $1; };
|
|||||||
|
|
||||||
file_name: Sconst { $$ = $1; };
|
file_name: Sconst { $$ = $1; };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The production for a qualified func_name has to exactly match the
|
||||||
|
* production for a qualified columnref, because we cannot tell which we
|
||||||
|
* are parsing until we see what comes after it ('(' for a func_name,
|
||||||
|
* anything else for a columnref). Therefore we allow 'indirection' which
|
||||||
|
* may contain subscripts, and reject that case in the C code. (If we
|
||||||
|
* ever implement SQL99-like methods, such syntax may actually become legal!)
|
||||||
|
*/
|
||||||
func_name: function_name
|
func_name: function_name
|
||||||
{ $$ = list_make1(makeString($1)); }
|
{ $$ = list_make1(makeString($1)); }
|
||||||
| dotted_name { $$ = $1; }
|
| relation_name indirection
|
||||||
|
{ $$ = check_func_name(lcons(makeString($1), $2)); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
@ -7325,14 +7334,6 @@ AexprConst: Iconst
|
|||||||
n->typename->typmod = INTERVAL_TYPMOD($3, $6);
|
n->typename->typmod = INTERVAL_TYPMOD($3, $6);
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| PARAM opt_indirection
|
|
||||||
{
|
|
||||||
ParamRef *n = makeNode(ParamRef);
|
|
||||||
n->number = $1;
|
|
||||||
n->fields = NIL;
|
|
||||||
n->indirection = $2;
|
|
||||||
$$ = (Node *)n;
|
|
||||||
}
|
|
||||||
| TRUE_P
|
| TRUE_P
|
||||||
{
|
{
|
||||||
$$ = (Node *)makeBoolAConst(TRUE);
|
$$ = (Node *)makeBoolAConst(TRUE);
|
||||||
@ -7781,6 +7782,48 @@ SpecialRuleRelation:
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
static Node *
|
||||||
|
makeColumnRef(char *relname, List *indirection)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Generate a ColumnRef node, with an A_Indirection node added if there
|
||||||
|
* is any subscripting in the specified indirection list. However,
|
||||||
|
* any field selection at the start of the indirection list must be
|
||||||
|
* transposed into the "fields" part of the ColumnRef node.
|
||||||
|
*/
|
||||||
|
ColumnRef *c = makeNode(ColumnRef);
|
||||||
|
int nfields = 0;
|
||||||
|
ListCell *l;
|
||||||
|
|
||||||
|
foreach(l, indirection)
|
||||||
|
{
|
||||||
|
if (IsA(lfirst(l), A_Indices))
|
||||||
|
{
|
||||||
|
A_Indirection *i = makeNode(A_Indirection);
|
||||||
|
|
||||||
|
if (nfields == 0)
|
||||||
|
{
|
||||||
|
/* easy case - all indirection goes to A_Indirection */
|
||||||
|
c->fields = list_make1(makeString(relname));
|
||||||
|
i->indirection = indirection;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* got to split the list in two */
|
||||||
|
i->indirection = list_copy_tail(indirection, nfields);
|
||||||
|
indirection = list_truncate(indirection, nfields);
|
||||||
|
c->fields = lcons(makeString(relname), indirection);
|
||||||
|
}
|
||||||
|
i->arg = (Node *) c;
|
||||||
|
return (Node *) i;
|
||||||
|
}
|
||||||
|
nfields++;
|
||||||
|
}
|
||||||
|
/* No subscripting, so all indirection gets added to field list */
|
||||||
|
c->fields = lcons(makeString(relname), indirection);
|
||||||
|
return (Node *) c;
|
||||||
|
}
|
||||||
|
|
||||||
static Node *
|
static Node *
|
||||||
makeTypeCast(Node *arg, TypeName *typename)
|
makeTypeCast(Node *arg, TypeName *typename)
|
||||||
{
|
{
|
||||||
@ -7945,6 +7988,26 @@ makeOverlaps(List *largs, List *rargs)
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* check_func_name --- check the result of func_name production
|
||||||
|
*
|
||||||
|
* It's easiest to let the grammar production for func_name allow subscripts
|
||||||
|
* and '*', which we then must reject here.
|
||||||
|
*/
|
||||||
|
static List *
|
||||||
|
check_func_name(List *names)
|
||||||
|
{
|
||||||
|
ListCell *i;
|
||||||
|
|
||||||
|
foreach(i, names)
|
||||||
|
{
|
||||||
|
if (!IsA(lfirst(i), String))
|
||||||
|
yyerror("syntax error");
|
||||||
|
else if (strcmp(strVal(lfirst(i)), "*") == 0)
|
||||||
|
yyerror("syntax error");
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
/* extractArgTypes()
|
/* extractArgTypes()
|
||||||
* Given a list of FunctionParameter nodes, extract a list of just the
|
* Given a list of FunctionParameter nodes, extract a list of just the
|
||||||
* argument types (TypeNames). Most of the productions using func_args
|
* argument types (TypeNames). Most of the productions using func_args
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.131 2004/05/30 23:40:34 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.132 2004/06/09 19:08:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1122,8 +1122,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
|
|||||||
*----------
|
*----------
|
||||||
*/
|
*/
|
||||||
if (IsA(node, ColumnRef) &&
|
if (IsA(node, ColumnRef) &&
|
||||||
list_length(((ColumnRef *) node)->fields) == 1 &&
|
list_length(((ColumnRef *) node)->fields) == 1)
|
||||||
((ColumnRef *) node)->indirection == NIL)
|
|
||||||
{
|
{
|
||||||
char *name = strVal(linitial(((ColumnRef *) node)->fields));
|
char *name = strVal(linitial(((ColumnRef *) node)->fields));
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.172 2004/05/30 23:40:35 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.173 2004/06/09 19:08:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -100,7 +100,6 @@ transformExpr(ParseState *pstate, Node *expr)
|
|||||||
int paramno = pref->number;
|
int paramno = pref->number;
|
||||||
ParseState *toppstate;
|
ParseState *toppstate;
|
||||||
Param *param;
|
Param *param;
|
||||||
ListCell *fields;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find topmost ParseState, which is where paramtype info
|
* Find topmost ParseState, which is where paramtype info
|
||||||
@ -148,18 +147,6 @@ transformExpr(ParseState *pstate, Node *expr)
|
|||||||
param->paramid = (AttrNumber) paramno;
|
param->paramid = (AttrNumber) paramno;
|
||||||
param->paramtype = toppstate->p_paramtypes[paramno - 1];
|
param->paramtype = toppstate->p_paramtypes[paramno - 1];
|
||||||
result = (Node *) param;
|
result = (Node *) param;
|
||||||
|
|
||||||
/* handle qualification, if any */
|
|
||||||
foreach(fields, pref->fields)
|
|
||||||
{
|
|
||||||
result = ParseFuncOrColumn(pstate,
|
|
||||||
list_make1(lfirst(fields)),
|
|
||||||
list_make1(result),
|
|
||||||
false, false, true);
|
|
||||||
}
|
|
||||||
/* handle subscripts, if any */
|
|
||||||
result = transformIndirection(pstate, result,
|
|
||||||
pref->indirection);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case T_A_Const:
|
case T_A_Const:
|
||||||
@ -173,23 +160,13 @@ transformExpr(ParseState *pstate, Node *expr)
|
|||||||
con->typename);
|
con->typename);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case T_ExprFieldSelect:
|
case T_A_Indirection:
|
||||||
{
|
{
|
||||||
ExprFieldSelect *efs = (ExprFieldSelect *) expr;
|
A_Indirection *ind = (A_Indirection *) expr;
|
||||||
ListCell *fields;
|
|
||||||
|
|
||||||
result = transformExpr(pstate, efs->arg);
|
result = transformExpr(pstate, ind->arg);
|
||||||
/* handle qualification, if any */
|
|
||||||
foreach(fields, efs->fields)
|
|
||||||
{
|
|
||||||
result = ParseFuncOrColumn(pstate,
|
|
||||||
list_make1(lfirst(fields)),
|
|
||||||
list_make1(result),
|
|
||||||
false, false, true);
|
|
||||||
}
|
|
||||||
/* handle subscripts, if any */
|
|
||||||
result = transformIndirection(pstate, result,
|
result = transformIndirection(pstate, result,
|
||||||
efs->indirection);
|
ind->indirection);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case T_TypeCast:
|
case T_TypeCast:
|
||||||
@ -961,6 +938,7 @@ transformExpr(ParseState *pstate, Node *expr)
|
|||||||
case T_NullIfExpr:
|
case T_NullIfExpr:
|
||||||
case T_BoolExpr:
|
case T_BoolExpr:
|
||||||
case T_FieldSelect:
|
case T_FieldSelect:
|
||||||
|
case T_FieldStore:
|
||||||
case T_RelabelType:
|
case T_RelabelType:
|
||||||
case T_CaseTestExpr:
|
case T_CaseTestExpr:
|
||||||
case T_CoerceToDomain:
|
case T_CoerceToDomain:
|
||||||
@ -983,15 +961,55 @@ transformExpr(ParseState *pstate, Node *expr)
|
|||||||
static Node *
|
static Node *
|
||||||
transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
|
transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
|
||||||
{
|
{
|
||||||
if (indirection == NIL)
|
Node *result = basenode;
|
||||||
return basenode;
|
List *subscripts = NIL;
|
||||||
return (Node *) transformArraySubscripts(pstate,
|
ListCell *i;
|
||||||
basenode,
|
|
||||||
exprType(basenode),
|
/*
|
||||||
exprTypmod(basenode),
|
* We have to split any field-selection operations apart from
|
||||||
indirection,
|
* subscripting. Adjacent A_Indices nodes have to be treated
|
||||||
false,
|
* as a single multidimensional subscript operation.
|
||||||
NULL);
|
*/
|
||||||
|
foreach(i, indirection)
|
||||||
|
{
|
||||||
|
Node *n = lfirst(i);
|
||||||
|
|
||||||
|
if (IsA(n, A_Indices))
|
||||||
|
{
|
||||||
|
subscripts = lappend(subscripts, n);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert(IsA(n, String));
|
||||||
|
|
||||||
|
/* process subscripts before this field selection */
|
||||||
|
if (subscripts)
|
||||||
|
result = (Node *) transformArraySubscripts(pstate,
|
||||||
|
result,
|
||||||
|
exprType(result),
|
||||||
|
InvalidOid,
|
||||||
|
-1,
|
||||||
|
subscripts,
|
||||||
|
NULL);
|
||||||
|
subscripts = NIL;
|
||||||
|
|
||||||
|
result = ParseFuncOrColumn(pstate,
|
||||||
|
list_make1(n),
|
||||||
|
list_make1(result),
|
||||||
|
false, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* process trailing subscripts, if any */
|
||||||
|
if (subscripts)
|
||||||
|
result = (Node *) transformArraySubscripts(pstate,
|
||||||
|
result,
|
||||||
|
exprType(result),
|
||||||
|
InvalidOid,
|
||||||
|
-1,
|
||||||
|
subscripts,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Node *
|
static Node *
|
||||||
@ -1051,17 +1069,15 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to find the name as a relation ... but not if
|
* Try to find the name as a relation. Note that only
|
||||||
* subscripts appear. Note also that only relations
|
* relations already entered into the rangetable will be
|
||||||
* already entered into the rangetable will be
|
|
||||||
* recognized.
|
* recognized.
|
||||||
*
|
*
|
||||||
* This is a hack for backwards compatibility with
|
* This is a hack for backwards compatibility with
|
||||||
* PostQUEL-inspired syntax. The preferred form now
|
* PostQUEL-inspired syntax. The preferred form now
|
||||||
* is "rel.*".
|
* is "rel.*".
|
||||||
*/
|
*/
|
||||||
if (cref->indirection == NIL &&
|
if (refnameRangeTblEntry(pstate, NULL, name,
|
||||||
refnameRangeTblEntry(pstate, NULL, name,
|
|
||||||
&levels_up) != NULL)
|
&levels_up) != NULL)
|
||||||
node = transformWholeRowRef(pstate, NULL, name);
|
node = transformWholeRowRef(pstate, NULL, name);
|
||||||
else
|
else
|
||||||
@ -1172,7 +1188,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return transformIndirection(pstate, node, cref->indirection);
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1385,6 +1401,9 @@ exprType(Node *expr)
|
|||||||
case T_FieldSelect:
|
case T_FieldSelect:
|
||||||
type = ((FieldSelect *) expr)->resulttype;
|
type = ((FieldSelect *) expr)->resulttype;
|
||||||
break;
|
break;
|
||||||
|
case T_FieldStore:
|
||||||
|
type = ((FieldStore *) expr)->resulttype;
|
||||||
|
break;
|
||||||
case T_RelabelType:
|
case T_RelabelType:
|
||||||
type = ((RelabelType *) expr)->resulttype;
|
type = ((RelabelType *) expr)->resulttype;
|
||||||
break;
|
break;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.83 2004/05/26 04:41:30 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.84 2004/06/09 19:08:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -67,6 +67,39 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
|
|||||||
return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
|
return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* transformArrayType()
|
||||||
|
* Get the element type of an array type in preparation for subscripting
|
||||||
|
*/
|
||||||
|
Oid
|
||||||
|
transformArrayType(Oid arrayType)
|
||||||
|
{
|
||||||
|
Oid elementType;
|
||||||
|
HeapTuple type_tuple_array;
|
||||||
|
Form_pg_type type_struct_array;
|
||||||
|
|
||||||
|
/* Get the type tuple for the array */
|
||||||
|
type_tuple_array = SearchSysCache(TYPEOID,
|
||||||
|
ObjectIdGetDatum(arrayType),
|
||||||
|
0, 0, 0);
|
||||||
|
if (!HeapTupleIsValid(type_tuple_array))
|
||||||
|
elog(ERROR, "cache lookup failed for type %u", arrayType);
|
||||||
|
type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
|
||||||
|
|
||||||
|
/* needn't check typisdefined since this will fail anyway */
|
||||||
|
|
||||||
|
elementType = type_struct_array->typelem;
|
||||||
|
if (elementType == InvalidOid)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("cannot subscript type %s because it is not an array",
|
||||||
|
format_type_be(arrayType))));
|
||||||
|
|
||||||
|
ReleaseSysCache(type_tuple_array);
|
||||||
|
|
||||||
|
return elementType;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* transformArraySubscripts()
|
* transformArraySubscripts()
|
||||||
* Transform array subscripting. This is used for both
|
* Transform array subscripting. This is used for both
|
||||||
@ -83,68 +116,49 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
|
|||||||
*
|
*
|
||||||
* pstate Parse state
|
* pstate Parse state
|
||||||
* arrayBase Already-transformed expression for the array as a whole
|
* arrayBase Already-transformed expression for the array as a whole
|
||||||
* (may be NULL if we are handling an INSERT)
|
* arrayType OID of array's datatype (should match type of arrayBase)
|
||||||
* arrayType OID of array's datatype
|
* elementType OID of array's element type (fetch with transformArrayType,
|
||||||
* arrayTypMod typmod to be applied to array elements
|
* or pass InvalidOid to do it here)
|
||||||
|
* elementTypMod typmod to be applied to array elements (if storing)
|
||||||
* indirection Untransformed list of subscripts (must not be NIL)
|
* indirection Untransformed list of subscripts (must not be NIL)
|
||||||
* forceSlice If true, treat subscript as array slice in all cases
|
|
||||||
* assignFrom NULL for array fetch, else transformed expression for source.
|
* assignFrom NULL for array fetch, else transformed expression for source.
|
||||||
*/
|
*/
|
||||||
ArrayRef *
|
ArrayRef *
|
||||||
transformArraySubscripts(ParseState *pstate,
|
transformArraySubscripts(ParseState *pstate,
|
||||||
Node *arrayBase,
|
Node *arrayBase,
|
||||||
Oid arrayType,
|
Oid arrayType,
|
||||||
int32 arrayTypMod,
|
Oid elementType,
|
||||||
|
int32 elementTypMod,
|
||||||
List *indirection,
|
List *indirection,
|
||||||
bool forceSlice,
|
|
||||||
Node *assignFrom)
|
Node *assignFrom)
|
||||||
{
|
{
|
||||||
Oid elementType,
|
Oid resultType;
|
||||||
resultType;
|
bool isSlice = false;
|
||||||
HeapTuple type_tuple_array;
|
|
||||||
Form_pg_type type_struct_array;
|
|
||||||
bool isSlice = forceSlice;
|
|
||||||
List *upperIndexpr = NIL;
|
List *upperIndexpr = NIL;
|
||||||
List *lowerIndexpr = NIL;
|
List *lowerIndexpr = NIL;
|
||||||
ListCell *idx;
|
ListCell *idx;
|
||||||
ArrayRef *aref;
|
ArrayRef *aref;
|
||||||
|
|
||||||
/* Get the type tuple for the array */
|
/* Caller may or may not have bothered to determine elementType */
|
||||||
type_tuple_array = SearchSysCache(TYPEOID,
|
if (!OidIsValid(elementType))
|
||||||
ObjectIdGetDatum(arrayType),
|
elementType = transformArrayType(arrayType);
|
||||||
0, 0, 0);
|
|
||||||
if (!HeapTupleIsValid(type_tuple_array))
|
|
||||||
elog(ERROR, "cache lookup failed for type %u", arrayType);
|
|
||||||
type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
|
|
||||||
|
|
||||||
elementType = type_struct_array->typelem;
|
|
||||||
if (elementType == InvalidOid)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
||||||
errmsg("cannot subscript type %s because it is not an array",
|
|
||||||
format_type_be(arrayType))));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A list containing only single subscripts refers to a single array
|
* A list containing only single subscripts refers to a single array
|
||||||
* element. If any of the items are double subscripts (lower:upper),
|
* element. If any of the items are double subscripts (lower:upper),
|
||||||
* then the subscript expression means an array slice operation. In
|
* then the subscript expression means an array slice operation. In
|
||||||
* this case, we supply a default lower bound of 1 for any items that
|
* this case, we supply a default lower bound of 1 for any items that
|
||||||
* contain only a single subscript. The forceSlice parameter forces us
|
* contain only a single subscript. We have to prescan the indirection
|
||||||
* to treat the operation as a slice, even if no lower bounds are
|
* list to see if there are any double subscripts.
|
||||||
* mentioned. Otherwise, we have to prescan the indirection list to
|
|
||||||
* see if there are any double subscripts.
|
|
||||||
*/
|
*/
|
||||||
if (!isSlice)
|
foreach(idx, indirection)
|
||||||
{
|
{
|
||||||
foreach(idx, indirection)
|
A_Indices *ai = (A_Indices *) lfirst(idx);
|
||||||
{
|
|
||||||
A_Indices *ai = (A_Indices *) lfirst(idx);
|
|
||||||
|
|
||||||
if (ai->lidx != NULL)
|
if (ai->lidx != NULL)
|
||||||
{
|
{
|
||||||
isSlice = true;
|
isSlice = true;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,6 +180,7 @@ transformArraySubscripts(ParseState *pstate,
|
|||||||
A_Indices *ai = (A_Indices *) lfirst(idx);
|
A_Indices *ai = (A_Indices *) lfirst(idx);
|
||||||
Node *subexpr;
|
Node *subexpr;
|
||||||
|
|
||||||
|
Assert(IsA(ai, A_Indices));
|
||||||
if (isSlice)
|
if (isSlice)
|
||||||
{
|
{
|
||||||
if (ai->lidx)
|
if (ai->lidx)
|
||||||
@ -209,28 +224,26 @@ transformArraySubscripts(ParseState *pstate,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If doing an array store, coerce the source value to the right type.
|
* If doing an array store, coerce the source value to the right type.
|
||||||
|
* (This should agree with the coercion done by updateTargetListEntry.)
|
||||||
*/
|
*/
|
||||||
if (assignFrom != NULL)
|
if (assignFrom != NULL)
|
||||||
{
|
{
|
||||||
Oid typesource = exprType(assignFrom);
|
Oid typesource = exprType(assignFrom);
|
||||||
Oid typeneeded = isSlice ? arrayType : elementType;
|
Oid typeneeded = isSlice ? arrayType : elementType;
|
||||||
|
|
||||||
if (typesource != InvalidOid)
|
assignFrom = coerce_to_target_type(pstate,
|
||||||
{
|
assignFrom, typesource,
|
||||||
assignFrom = coerce_to_target_type(pstate,
|
typeneeded, elementTypMod,
|
||||||
assignFrom, typesource,
|
COERCION_ASSIGNMENT,
|
||||||
typeneeded, arrayTypMod,
|
COERCE_IMPLICIT_CAST);
|
||||||
COERCION_ASSIGNMENT,
|
if (assignFrom == NULL)
|
||||||
COERCE_IMPLICIT_CAST);
|
ereport(ERROR,
|
||||||
if (assignFrom == NULL)
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
ereport(ERROR,
|
errmsg("array assignment requires type %s"
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
" but expression is of type %s",
|
||||||
errmsg("array assignment requires type %s"
|
format_type_be(typeneeded),
|
||||||
" but expression is of type %s",
|
format_type_be(typesource)),
|
||||||
format_type_be(typeneeded),
|
errhint("You will need to rewrite or cast the expression.")));
|
||||||
format_type_be(typesource)),
|
|
||||||
errhint("You will need to rewrite or cast the expression.")));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -245,8 +258,6 @@ transformArraySubscripts(ParseState *pstate,
|
|||||||
aref->refexpr = (Expr *) arrayBase;
|
aref->refexpr = (Expr *) arrayBase;
|
||||||
aref->refassgnexpr = (Expr *) assignFrom;
|
aref->refassgnexpr = (Expr *) assignFrom;
|
||||||
|
|
||||||
ReleaseSysCache(type_tuple_array);
|
|
||||||
|
|
||||||
return aref;
|
return aref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.120 2004/06/01 03:28:48 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.121 2004/06/09 19:08:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -25,9 +25,18 @@
|
|||||||
#include "parser/parse_target.h"
|
#include "parser/parse_target.h"
|
||||||
#include "parser/parse_type.h"
|
#include "parser/parse_type.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
|
||||||
|
|
||||||
static void markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var);
|
static void markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var);
|
||||||
|
static Node *transformAssignmentIndirection(ParseState *pstate,
|
||||||
|
Node *basenode,
|
||||||
|
const char *targetName,
|
||||||
|
bool targetIsArray,
|
||||||
|
Oid targetTypeId,
|
||||||
|
int32 targetTypMod,
|
||||||
|
ListCell *indirection,
|
||||||
|
Node *rhs);
|
||||||
static List *ExpandAllTables(ParseState *pstate);
|
static List *ExpandAllTables(ParseState *pstate);
|
||||||
static char *FigureColname(Node *node);
|
static char *FigureColname(Node *node);
|
||||||
static int FigureColnameInternal(Node *node, char **name);
|
static int FigureColnameInternal(Node *node, char **name);
|
||||||
@ -87,7 +96,7 @@ transformTargetEntry(ParseState *pstate,
|
|||||||
* Turns a list of ResTarget's into a list of TargetEntry's.
|
* Turns a list of ResTarget's into a list of TargetEntry's.
|
||||||
*
|
*
|
||||||
* At this point, we don't care whether we are doing SELECT, INSERT,
|
* At this point, we don't care whether we are doing SELECT, INSERT,
|
||||||
* or UPDATE; we just transform the given expressions.
|
* or UPDATE; we just transform the given expressions (the "val" fields).
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
transformTargetList(ParseState *pstate, List *targetlist)
|
transformTargetList(ParseState *pstate, List *targetlist)
|
||||||
@ -284,14 +293,14 @@ markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var)
|
|||||||
* This is used in INSERT and UPDATE statements only. It prepares a
|
* This is used in INSERT and UPDATE statements only. It prepares a
|
||||||
* TargetEntry for assignment to a column of the target table.
|
* TargetEntry for assignment to a column of the target table.
|
||||||
* This includes coercing the given value to the target column's type
|
* This includes coercing the given value to the target column's type
|
||||||
* (if necessary), and dealing with any subscripts attached to the target
|
* (if necessary), and dealing with any subfield names or subscripts
|
||||||
* column itself.
|
* attached to the target column itself.
|
||||||
*
|
*
|
||||||
* pstate parse state
|
* pstate parse state
|
||||||
* tle target list entry to be modified
|
* tle target list entry to be modified
|
||||||
* colname target column name (ie, name of attribute to be assigned to)
|
* colname target column name (ie, name of attribute to be assigned to)
|
||||||
* attrno target attribute number
|
* attrno target attribute number
|
||||||
* indirection subscripts for target column, if any
|
* indirection subscripts/field names for target column, if any
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
updateTargetListEntry(ParseState *pstate,
|
updateTargetListEntry(ParseState *pstate,
|
||||||
@ -320,8 +329,8 @@ updateTargetListEntry(ParseState *pstate,
|
|||||||
* type/typmod into it so that exprType will report the right things.
|
* type/typmod into it so that exprType will report the right things.
|
||||||
* (We expect that the eventually substituted default expression will
|
* (We expect that the eventually substituted default expression will
|
||||||
* in fact have this type and typmod.) Also, reject trying to update
|
* in fact have this type and typmod.) Also, reject trying to update
|
||||||
* an array element with DEFAULT, since there can't be any default for
|
* a subfield or array element with DEFAULT, since there can't be any
|
||||||
* individual elements of a column.
|
* default for portions of a column.
|
||||||
*/
|
*/
|
||||||
if (tle->expr && IsA(tle->expr, SetToDefault))
|
if (tle->expr && IsA(tle->expr, SetToDefault))
|
||||||
{
|
{
|
||||||
@ -330,82 +339,81 @@ updateTargetListEntry(ParseState *pstate,
|
|||||||
def->typeId = attrtype;
|
def->typeId = attrtype;
|
||||||
def->typeMod = attrtypmod;
|
def->typeMod = attrtypmod;
|
||||||
if (indirection)
|
if (indirection)
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
if (IsA(linitial(indirection), A_Indices))
|
||||||
errmsg("cannot set an array element to DEFAULT")));
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot set an array element to DEFAULT")));
|
||||||
|
else
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot set a subfield to DEFAULT")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now we can use exprType() safely. */
|
/* Now we can use exprType() safely. */
|
||||||
type_id = exprType((Node *) tle->expr);
|
type_id = exprType((Node *) tle->expr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there are subscripts on the target column, prepare an array
|
* If there is indirection on the target column, prepare an array or
|
||||||
* assignment expression. This will generate an array value that the
|
* subfield assignment expression. This will generate a new column value
|
||||||
* source value has been inserted into, which can then be placed in
|
* that the source value has been inserted into, which can then be placed
|
||||||
* the new tuple constructed by INSERT or UPDATE. Note that
|
* in the new tuple constructed by INSERT or UPDATE.
|
||||||
* transformArraySubscripts takes care of type coercion.
|
|
||||||
*/
|
*/
|
||||||
if (indirection)
|
if (indirection)
|
||||||
{
|
{
|
||||||
Node *arrayBase;
|
Node *colVar;
|
||||||
ArrayRef *aref;
|
|
||||||
|
|
||||||
if (pstate->p_is_insert)
|
if (pstate->p_is_insert)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The command is INSERT INTO table (arraycol[subscripts]) ...
|
* The command is INSERT INTO table (col.something) ...
|
||||||
* so there is not really a source array value to work with.
|
* so there is not really a source value to work with.
|
||||||
* Let the executor do something reasonable, if it can. Notice
|
* Insert a NULL constant as the source value.
|
||||||
* that we force transformArraySubscripts to treat the
|
|
||||||
* subscripting op as an array-slice op below, so the source
|
|
||||||
* data will have been coerced to the array type.
|
|
||||||
*/
|
*/
|
||||||
arrayBase = NULL; /* signal there is no source array */
|
colVar = (Node *) makeNullConst(attrtype);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Build a Var for the array to be updated.
|
* Build a Var for the column to be updated.
|
||||||
*/
|
*/
|
||||||
arrayBase = (Node *) make_var(pstate,
|
colVar = (Node *) make_var(pstate,
|
||||||
pstate->p_target_rangetblentry,
|
pstate->p_target_rangetblentry,
|
||||||
attrno);
|
attrno);
|
||||||
}
|
}
|
||||||
|
|
||||||
aref = transformArraySubscripts(pstate,
|
tle->expr = (Expr *)
|
||||||
arrayBase,
|
transformAssignmentIndirection(pstate,
|
||||||
attrtype,
|
colVar,
|
||||||
attrtypmod,
|
colname,
|
||||||
indirection,
|
false,
|
||||||
pstate->p_is_insert,
|
attrtype,
|
||||||
(Node *) tle->expr);
|
attrtypmod,
|
||||||
tle->expr = (Expr *) aref;
|
list_head(indirection),
|
||||||
|
(Node *) tle->expr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* For normal non-subscripted target column, do type checking and
|
* For normal non-qualified target column, do type checking and
|
||||||
* coercion. But accept InvalidOid, which indicates the source is
|
* coercion.
|
||||||
* a NULL constant. (XXX is that still true?)
|
|
||||||
*/
|
*/
|
||||||
if (type_id != InvalidOid)
|
tle->expr = (Expr *)
|
||||||
{
|
coerce_to_target_type(pstate,
|
||||||
tle->expr = (Expr *)
|
(Node *) tle->expr, type_id,
|
||||||
coerce_to_target_type(pstate,
|
attrtype, attrtypmod,
|
||||||
(Node *) tle->expr, type_id,
|
COERCION_ASSIGNMENT,
|
||||||
attrtype, attrtypmod,
|
COERCE_IMPLICIT_CAST);
|
||||||
COERCION_ASSIGNMENT,
|
if (tle->expr == NULL)
|
||||||
COERCE_IMPLICIT_CAST);
|
ereport(ERROR,
|
||||||
if (tle->expr == NULL)
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
ereport(ERROR,
|
errmsg("column \"%s\" is of type %s"
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
" but expression is of type %s",
|
||||||
errmsg("column \"%s\" is of type %s"
|
colname,
|
||||||
" but expression is of type %s",
|
format_type_be(attrtype),
|
||||||
colname,
|
format_type_be(type_id)),
|
||||||
format_type_be(attrtype),
|
errhint("You will need to rewrite or cast the expression.")));
|
||||||
format_type_be(type_id)),
|
|
||||||
errhint("You will need to rewrite or cast the expression.")));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -425,6 +433,208 @@ updateTargetListEntry(ParseState *pstate,
|
|||||||
resnode->resname = colname;
|
resnode->resname = colname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process indirection (field selection or subscripting) of the target
|
||||||
|
* column in INSERT/UPDATE. This routine recurses for multiple levels
|
||||||
|
* of indirection --- but note that several adjacent A_Indices nodes in
|
||||||
|
* the indirection list are treated as a single multidimensional subscript
|
||||||
|
* operation.
|
||||||
|
*
|
||||||
|
* In the initial call, basenode is a Var for the target column in UPDATE,
|
||||||
|
* or a null Const of the target's type in INSERT. In recursive calls,
|
||||||
|
* basenode is NULL, indicating that a substitute node should be consed up if
|
||||||
|
* needed.
|
||||||
|
*
|
||||||
|
* targetName is the name of the field or subfield we're assigning to, and
|
||||||
|
* targetIsArray is true if we're subscripting it. These are just for
|
||||||
|
* error reporting.
|
||||||
|
*
|
||||||
|
* targetTypeId and targetTypMod indicate the datatype of the object to
|
||||||
|
* be assigned to (initially the target column, later some subobject).
|
||||||
|
*
|
||||||
|
* indirection is the sublist remaining to process. When it's NULL, we're
|
||||||
|
* done recursing and can just coerce and return the RHS.
|
||||||
|
*
|
||||||
|
* rhs is the already-transformed value to be assigned; note it has not been
|
||||||
|
* coerced to any particular type.
|
||||||
|
*/
|
||||||
|
static Node *
|
||||||
|
transformAssignmentIndirection(ParseState *pstate,
|
||||||
|
Node *basenode,
|
||||||
|
const char *targetName,
|
||||||
|
bool targetIsArray,
|
||||||
|
Oid targetTypeId,
|
||||||
|
int32 targetTypMod,
|
||||||
|
ListCell *indirection,
|
||||||
|
Node *rhs)
|
||||||
|
{
|
||||||
|
Node *result;
|
||||||
|
List *subscripts = NIL;
|
||||||
|
bool isSlice = false;
|
||||||
|
ListCell *i;
|
||||||
|
|
||||||
|
if (indirection && !basenode)
|
||||||
|
{
|
||||||
|
/* Set up a substitution. We reuse CaseTestExpr for this. */
|
||||||
|
CaseTestExpr *ctest = makeNode(CaseTestExpr);
|
||||||
|
|
||||||
|
ctest->typeId = targetTypeId;
|
||||||
|
ctest->typeMod = targetTypMod;
|
||||||
|
basenode = (Node *) ctest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have to split any field-selection operations apart from
|
||||||
|
* subscripting. Adjacent A_Indices nodes have to be treated
|
||||||
|
* as a single multidimensional subscript operation.
|
||||||
|
*/
|
||||||
|
for_each_cell(i, indirection)
|
||||||
|
{
|
||||||
|
Node *n = lfirst(i);
|
||||||
|
|
||||||
|
if (IsA(n, A_Indices))
|
||||||
|
{
|
||||||
|
subscripts = lappend(subscripts, n);
|
||||||
|
if (((A_Indices *) n)->lidx != NULL)
|
||||||
|
isSlice = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FieldStore *fstore;
|
||||||
|
Oid typrelid;
|
||||||
|
AttrNumber attnum;
|
||||||
|
Oid fieldTypeId;
|
||||||
|
int32 fieldTypMod;
|
||||||
|
|
||||||
|
Assert(IsA(n, String));
|
||||||
|
|
||||||
|
/* process subscripts before this field selection */
|
||||||
|
if (subscripts)
|
||||||
|
{
|
||||||
|
Oid elementTypeId = transformArrayType(targetTypeId);
|
||||||
|
Oid typeNeeded = isSlice ? targetTypeId : elementTypeId;
|
||||||
|
|
||||||
|
/* recurse to create appropriate RHS for array assign */
|
||||||
|
rhs = transformAssignmentIndirection(pstate,
|
||||||
|
NULL,
|
||||||
|
targetName,
|
||||||
|
true,
|
||||||
|
typeNeeded,
|
||||||
|
targetTypMod,
|
||||||
|
i,
|
||||||
|
rhs);
|
||||||
|
/* process subscripts */
|
||||||
|
return (Node *) transformArraySubscripts(pstate,
|
||||||
|
basenode,
|
||||||
|
targetTypeId,
|
||||||
|
elementTypeId,
|
||||||
|
targetTypMod,
|
||||||
|
subscripts,
|
||||||
|
rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No subscripts, so can process field selection here */
|
||||||
|
|
||||||
|
typrelid = typeidTypeRelid(targetTypeId);
|
||||||
|
if (!typrelid)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("cannot assign to a column of type %s because it is not a composite type",
|
||||||
|
format_type_be(targetTypeId))));
|
||||||
|
|
||||||
|
attnum = get_attnum(typrelid, strVal(n));
|
||||||
|
if (attnum == InvalidAttrNumber)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||||
|
errmsg("column \"%s\" not found in data type %s",
|
||||||
|
strVal(n), format_type_be(targetTypeId))));
|
||||||
|
if (attnum < 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||||
|
errmsg("cannot assign to system column \"%s\"",
|
||||||
|
strVal(n))));
|
||||||
|
|
||||||
|
get_atttypetypmod(typrelid, attnum,
|
||||||
|
&fieldTypeId, &fieldTypMod);
|
||||||
|
|
||||||
|
/* recurse to create appropriate RHS for field assign */
|
||||||
|
rhs = transformAssignmentIndirection(pstate,
|
||||||
|
NULL,
|
||||||
|
strVal(n),
|
||||||
|
false,
|
||||||
|
fieldTypeId,
|
||||||
|
fieldTypMod,
|
||||||
|
lnext(i),
|
||||||
|
rhs);
|
||||||
|
|
||||||
|
/* and build a FieldStore node */
|
||||||
|
fstore = makeNode(FieldStore);
|
||||||
|
fstore->arg = (Expr *) basenode;
|
||||||
|
fstore->newvals = list_make1(rhs);
|
||||||
|
fstore->fieldnums = list_make1_int(attnum);
|
||||||
|
fstore->resulttype = targetTypeId;
|
||||||
|
|
||||||
|
return (Node *) fstore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* process trailing subscripts, if any */
|
||||||
|
if (subscripts)
|
||||||
|
{
|
||||||
|
Oid elementTypeId = transformArrayType(targetTypeId);
|
||||||
|
Oid typeNeeded = isSlice ? targetTypeId : elementTypeId;
|
||||||
|
|
||||||
|
/* recurse to create appropriate RHS for array assign */
|
||||||
|
rhs = transformAssignmentIndirection(pstate,
|
||||||
|
NULL,
|
||||||
|
targetName,
|
||||||
|
true,
|
||||||
|
typeNeeded,
|
||||||
|
targetTypMod,
|
||||||
|
NULL,
|
||||||
|
rhs);
|
||||||
|
/* process subscripts */
|
||||||
|
return (Node *) transformArraySubscripts(pstate,
|
||||||
|
basenode,
|
||||||
|
targetTypeId,
|
||||||
|
elementTypeId,
|
||||||
|
targetTypMod,
|
||||||
|
subscripts,
|
||||||
|
rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* base case: just coerce RHS to match target type ID */
|
||||||
|
|
||||||
|
result = coerce_to_target_type(pstate,
|
||||||
|
rhs, exprType(rhs),
|
||||||
|
targetTypeId, targetTypMod,
|
||||||
|
COERCION_ASSIGNMENT,
|
||||||
|
COERCE_IMPLICIT_CAST);
|
||||||
|
if (result == NULL)
|
||||||
|
{
|
||||||
|
if (targetIsArray)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("array assignment to \"%s\" requires type %s"
|
||||||
|
" but expression is of type %s",
|
||||||
|
targetName,
|
||||||
|
format_type_be(targetTypeId),
|
||||||
|
format_type_be(exprType(rhs))),
|
||||||
|
errhint("You will need to rewrite or cast the expression.")));
|
||||||
|
else
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("subfield \"%s\" is of type %s"
|
||||||
|
" but expression is of type %s",
|
||||||
|
targetName,
|
||||||
|
format_type_be(targetTypeId),
|
||||||
|
format_type_be(exprType(rhs))),
|
||||||
|
errhint("You will need to rewrite or cast the expression.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* checkInsertTargets -
|
* checkInsertTargets -
|
||||||
@ -466,21 +676,42 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
|
|||||||
/*
|
/*
|
||||||
* Do initial validation of user-supplied INSERT column list.
|
* Do initial validation of user-supplied INSERT column list.
|
||||||
*/
|
*/
|
||||||
|
List *wholecols = NIL;
|
||||||
ListCell *tl;
|
ListCell *tl;
|
||||||
|
|
||||||
foreach(tl, cols)
|
foreach(tl, cols)
|
||||||
{
|
{
|
||||||
char *name = ((ResTarget *) lfirst(tl))->name;
|
ResTarget *col = (ResTarget *) lfirst(tl);
|
||||||
|
char *name = col->name;
|
||||||
int attrno;
|
int attrno;
|
||||||
|
|
||||||
/* Lookup column name, ereport on failure */
|
/* Lookup column name, ereport on failure */
|
||||||
attrno = attnameAttNum(pstate->p_target_relation, name, false);
|
attrno = attnameAttNum(pstate->p_target_relation, name, false);
|
||||||
/* Check for duplicates */
|
|
||||||
if (list_member_int(*attrnos, attrno))
|
/*
|
||||||
ereport(ERROR,
|
* Check for duplicates, but only of whole columns --- we
|
||||||
(errcode(ERRCODE_DUPLICATE_COLUMN),
|
* allow INSERT INTO foo (col.subcol1, col.subcol2)
|
||||||
errmsg("column \"%s\" specified more than once",
|
*/
|
||||||
name)));
|
if (col->indirection == NIL)
|
||||||
|
{
|
||||||
|
/* whole column; must not have any other assignment */
|
||||||
|
if (list_member_int(*attrnos, attrno))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DUPLICATE_COLUMN),
|
||||||
|
errmsg("column \"%s\" specified more than once",
|
||||||
|
name)));
|
||||||
|
wholecols = lappend_int(wholecols, attrno);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* partial column; must not have any whole assignment */
|
||||||
|
if (list_member_int(wholecols, attrno))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DUPLICATE_COLUMN),
|
||||||
|
errmsg("column \"%s\" specified more than once",
|
||||||
|
name)));
|
||||||
|
}
|
||||||
|
|
||||||
*attrnos = lappend_int(*attrnos, attrno);
|
*attrnos = lappend_int(*attrnos, attrno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -572,30 +803,45 @@ FigureColnameInternal(Node *node, char **name)
|
|||||||
{
|
{
|
||||||
case T_ColumnRef:
|
case T_ColumnRef:
|
||||||
{
|
{
|
||||||
char *cname = strVal(llast(((ColumnRef *) node)->fields));
|
char *fname = NULL;
|
||||||
|
ListCell *l;
|
||||||
|
|
||||||
if (strcmp(cname, "*") != 0)
|
/* find last field name, if any, ignoring "*" */
|
||||||
|
foreach(l, ((ColumnRef *) node)->fields)
|
||||||
{
|
{
|
||||||
*name = cname;
|
Node *i = lfirst(l);
|
||||||
|
|
||||||
|
if (strcmp(strVal(i), "*") != 0)
|
||||||
|
fname = strVal(i);
|
||||||
|
}
|
||||||
|
if (fname)
|
||||||
|
{
|
||||||
|
*name = fname;
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case T_ExprFieldSelect:
|
case T_A_Indirection:
|
||||||
{
|
{
|
||||||
ExprFieldSelect *efs = (ExprFieldSelect *) node;
|
A_Indirection *ind = (A_Indirection *) node;
|
||||||
|
char *fname = NULL;
|
||||||
|
ListCell *l;
|
||||||
|
|
||||||
if (efs->fields)
|
/* find last field name, if any, ignoring "*" */
|
||||||
|
foreach(l, ind->indirection)
|
||||||
{
|
{
|
||||||
char *fname = strVal(llast(efs->fields));
|
Node *i = lfirst(l);
|
||||||
|
|
||||||
if (strcmp(fname, "*") != 0)
|
if (IsA(i, String) &&
|
||||||
{
|
strcmp(strVal(i), "*") != 0)
|
||||||
*name = fname;
|
fname = strVal(i);
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return FigureColnameInternal(efs->arg, name);
|
if (fname)
|
||||||
|
{
|
||||||
|
*name = fname;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return FigureColnameInternal(ind->arg, name);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case T_FuncCall:
|
case T_FuncCall:
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.138 2004/05/30 23:40:35 neilc Exp $
|
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.139 2004/06/09 19:08:17 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -50,6 +50,7 @@ static void rewriteTargetList(Query *parsetree, Relation target_relation);
|
|||||||
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
|
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
|
||||||
TargetEntry *prior_tle,
|
TargetEntry *prior_tle,
|
||||||
const char *attrName);
|
const char *attrName);
|
||||||
|
static Node *get_assignment_input(Node *node);
|
||||||
static void markQueryForUpdate(Query *qry, bool skipOldNew);
|
static void markQueryForUpdate(Query *qry, bool skipOldNew);
|
||||||
static List *matchLocks(CmdType event, RuleLock *rulelocks,
|
static List *matchLocks(CmdType event, RuleLock *rulelocks,
|
||||||
int varno, Query *parsetree);
|
int varno, Query *parsetree);
|
||||||
@ -273,8 +274,9 @@ adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
|
|||||||
* expressions.
|
* expressions.
|
||||||
*
|
*
|
||||||
* 2. Merge multiple entries for the same target attribute, or declare error
|
* 2. Merge multiple entries for the same target attribute, or declare error
|
||||||
* if we can't. Presently, multiple entries are only allowed for UPDATE of
|
* if we can't. Multiple entries are only allowed for INSERT/UPDATE of
|
||||||
* an array field, for example "UPDATE table SET foo[2] = 42, foo[4] = 43".
|
* portions of an array or record field, for example
|
||||||
|
* UPDATE table SET foo[2] = 42, foo[4] = 43;
|
||||||
* We can merge such operations into a single assignment op. Essentially,
|
* We can merge such operations into a single assignment op. Essentially,
|
||||||
* the expression we want to produce in this case is like
|
* the expression we want to produce in this case is like
|
||||||
* foo = array_set(array_set(foo, 2, 42), 4, 43)
|
* foo = array_set(array_set(foo, 2, 42), 4, 43)
|
||||||
@ -431,8 +433,12 @@ process_matched_tle(TargetEntry *src_tle,
|
|||||||
const char *attrName)
|
const char *attrName)
|
||||||
{
|
{
|
||||||
Resdom *resdom = src_tle->resdom;
|
Resdom *resdom = src_tle->resdom;
|
||||||
|
Node *src_expr;
|
||||||
|
Node *prior_expr;
|
||||||
|
Node *src_input;
|
||||||
|
Node *prior_input;
|
||||||
Node *priorbottom;
|
Node *priorbottom;
|
||||||
ArrayRef *newexpr;
|
Node *newexpr;
|
||||||
|
|
||||||
if (prior_tle == NULL)
|
if (prior_tle == NULL)
|
||||||
{
|
{
|
||||||
@ -443,30 +449,55 @@ process_matched_tle(TargetEntry *src_tle,
|
|||||||
return src_tle;
|
return src_tle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*----------
|
||||||
* Multiple assignments to same attribute. Allow only if all are
|
* Multiple assignments to same attribute. Allow only if all are
|
||||||
* array-assign operators with same bottom array object.
|
* FieldStore or ArrayRef assignment operations. This is a bit
|
||||||
|
* tricky because what we may actually be looking at is a nest of
|
||||||
|
* such nodes; consider
|
||||||
|
* UPDATE tab SET col.fld1.subfld1 = x, col.fld2.subfld2 = y
|
||||||
|
* The two expressions produced by the parser will look like
|
||||||
|
* FieldStore(col, fld1, FieldStore(placeholder, subfld1, x))
|
||||||
|
* FieldStore(col, fld2, FieldStore(placeholder, subfld2, x))
|
||||||
|
* However, we can ignore the substructure and just consider the top
|
||||||
|
* FieldStore or ArrayRef from each assignment, because it works to
|
||||||
|
* combine these as
|
||||||
|
* FieldStore(FieldStore(col, fld1,
|
||||||
|
* FieldStore(placeholder, subfld1, x)),
|
||||||
|
* fld2, FieldStore(placeholder, subfld2, x))
|
||||||
|
* Note the leftmost expression goes on the inside so that the
|
||||||
|
* assignments appear to occur left-to-right.
|
||||||
|
*
|
||||||
|
* For FieldStore, instead of nesting we can generate a single
|
||||||
|
* FieldStore with multiple target fields. We must nest when
|
||||||
|
* ArrayRefs are involved though.
|
||||||
|
*----------
|
||||||
*/
|
*/
|
||||||
if (src_tle->expr == NULL || !IsA(src_tle->expr, ArrayRef) ||
|
src_expr = (Node *) src_tle->expr;
|
||||||
((ArrayRef *) src_tle->expr)->refassgnexpr == NULL ||
|
prior_expr = (Node *) prior_tle->expr;
|
||||||
prior_tle->expr == NULL || !IsA(prior_tle->expr, ArrayRef) ||
|
src_input = get_assignment_input(src_expr);
|
||||||
((ArrayRef *) prior_tle->expr)->refassgnexpr == NULL ||
|
prior_input = get_assignment_input(prior_expr);
|
||||||
((ArrayRef *) src_tle->expr)->refrestype !=
|
if (src_input == NULL ||
|
||||||
((ArrayRef *) prior_tle->expr)->refrestype)
|
prior_input == NULL ||
|
||||||
|
exprType(src_expr) != exprType(prior_expr))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("multiple assignments to same column \"%s\"",
|
errmsg("multiple assignments to same column \"%s\"",
|
||||||
attrName)));
|
attrName)));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prior TLE could be a nest of ArrayRefs if we do this more than
|
* Prior TLE could be a nest of assignments if we do this more than
|
||||||
* once.
|
* once.
|
||||||
*/
|
*/
|
||||||
priorbottom = (Node *) ((ArrayRef *) prior_tle->expr)->refexpr;
|
priorbottom = prior_input;
|
||||||
while (priorbottom != NULL && IsA(priorbottom, ArrayRef) &&
|
for (;;)
|
||||||
((ArrayRef *) priorbottom)->refassgnexpr != NULL)
|
{
|
||||||
priorbottom = (Node *) ((ArrayRef *) priorbottom)->refexpr;
|
Node *newbottom = get_assignment_input(priorbottom);
|
||||||
if (!equal(priorbottom, ((ArrayRef *) src_tle->expr)->refexpr))
|
|
||||||
|
if (newbottom == NULL)
|
||||||
|
break; /* found the original Var reference */
|
||||||
|
priorbottom = newbottom;
|
||||||
|
}
|
||||||
|
if (!equal(priorbottom, src_input))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("multiple assignments to same column \"%s\"",
|
errmsg("multiple assignments to same column \"%s\"",
|
||||||
@ -475,13 +506,70 @@ process_matched_tle(TargetEntry *src_tle,
|
|||||||
/*
|
/*
|
||||||
* Looks OK to nest 'em.
|
* Looks OK to nest 'em.
|
||||||
*/
|
*/
|
||||||
newexpr = makeNode(ArrayRef);
|
if (IsA(src_expr, FieldStore))
|
||||||
memcpy(newexpr, src_tle->expr, sizeof(ArrayRef));
|
{
|
||||||
newexpr->refexpr = prior_tle->expr;
|
FieldStore *fstore = makeNode(FieldStore);
|
||||||
|
|
||||||
|
if (IsA(prior_expr, FieldStore))
|
||||||
|
{
|
||||||
|
/* combine the two */
|
||||||
|
memcpy(fstore, prior_expr, sizeof(FieldStore));
|
||||||
|
fstore->newvals =
|
||||||
|
list_concat(list_copy(((FieldStore *) prior_expr)->newvals),
|
||||||
|
list_copy(((FieldStore *) src_expr)->newvals));
|
||||||
|
fstore->fieldnums =
|
||||||
|
list_concat(list_copy(((FieldStore *) prior_expr)->fieldnums),
|
||||||
|
list_copy(((FieldStore *) src_expr)->fieldnums));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* general case, just nest 'em */
|
||||||
|
memcpy(fstore, src_expr, sizeof(FieldStore));
|
||||||
|
fstore->arg = (Expr *) prior_expr;
|
||||||
|
}
|
||||||
|
newexpr = (Node *) fstore;
|
||||||
|
}
|
||||||
|
else if (IsA(src_expr, ArrayRef))
|
||||||
|
{
|
||||||
|
ArrayRef *aref = makeNode(ArrayRef);
|
||||||
|
|
||||||
|
memcpy(aref, src_expr, sizeof(ArrayRef));
|
||||||
|
aref->refexpr = (Expr *) prior_expr;
|
||||||
|
newexpr = (Node *) aref;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
elog(ERROR, "can't happen");
|
||||||
|
newexpr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return makeTargetEntry(resdom, (Expr *) newexpr);
|
return makeTargetEntry(resdom, (Expr *) newexpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If node is an assignment node, return its input; else return NULL
|
||||||
|
*/
|
||||||
|
static Node *
|
||||||
|
get_assignment_input(Node *node)
|
||||||
|
{
|
||||||
|
if (node == NULL)
|
||||||
|
return NULL;
|
||||||
|
if (IsA(node, FieldStore))
|
||||||
|
{
|
||||||
|
FieldStore *fstore = (FieldStore *) node;
|
||||||
|
|
||||||
|
return (Node *) fstore->arg;
|
||||||
|
}
|
||||||
|
else if (IsA(node, ArrayRef))
|
||||||
|
{
|
||||||
|
ArrayRef *aref = (ArrayRef *) node;
|
||||||
|
|
||||||
|
if (aref->refassgnexpr == NULL)
|
||||||
|
return NULL;
|
||||||
|
return (Node *) aref->refexpr;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make an expression tree for the default value for a column.
|
* Make an expression tree for the default value for a column.
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* back to source text
|
* back to source text
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.170 2004/06/06 00:41:27 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.171 2004/06/09 19:08:18 tgl Exp $
|
||||||
*
|
*
|
||||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -204,7 +204,8 @@ static void get_from_clause_coldeflist(List *coldeflist,
|
|||||||
deparse_context *context);
|
deparse_context *context);
|
||||||
static void get_opclass_name(Oid opclass, Oid actual_datatype,
|
static void get_opclass_name(Oid opclass, Oid actual_datatype,
|
||||||
StringInfo buf);
|
StringInfo buf);
|
||||||
static bool tleIsArrayAssign(TargetEntry *tle);
|
static Node *processIndirection(Node *node, deparse_context *context);
|
||||||
|
static void printSubscripts(ArrayRef *aref, deparse_context *context);
|
||||||
static char *generate_relation_name(Oid relid);
|
static char *generate_relation_name(Oid relid);
|
||||||
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
|
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
|
||||||
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
|
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
|
||||||
@ -2061,6 +2062,7 @@ get_insert_query_def(Query *query, deparse_context *context)
|
|||||||
RangeTblEntry *rte;
|
RangeTblEntry *rte;
|
||||||
char *sep;
|
char *sep;
|
||||||
ListCell *l;
|
ListCell *l;
|
||||||
|
List *strippedexprs;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If it's an INSERT ... SELECT there will be a single subquery RTE
|
* If it's an INSERT ... SELECT there will be a single subquery RTE
|
||||||
@ -2087,11 +2089,15 @@ get_insert_query_def(Query *query, deparse_context *context)
|
|||||||
context->indentLevel += PRETTYINDENT_STD;
|
context->indentLevel += PRETTYINDENT_STD;
|
||||||
appendStringInfoChar(buf, ' ');
|
appendStringInfoChar(buf, ' ');
|
||||||
}
|
}
|
||||||
appendStringInfo(buf, "INSERT INTO %s",
|
appendStringInfo(buf, "INSERT INTO %s (",
|
||||||
generate_relation_name(rte->relid));
|
generate_relation_name(rte->relid));
|
||||||
|
|
||||||
/* Add the insert-column-names list */
|
/*
|
||||||
sep = " (";
|
* Add the insert-column-names list, and make a list of the actual
|
||||||
|
* assignment source expressions.
|
||||||
|
*/
|
||||||
|
strippedexprs = NIL;
|
||||||
|
sep = "";
|
||||||
foreach(l, query->targetList)
|
foreach(l, query->targetList)
|
||||||
{
|
{
|
||||||
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||||
@ -2101,9 +2107,22 @@ get_insert_query_def(Query *query, deparse_context *context)
|
|||||||
|
|
||||||
appendStringInfo(buf, sep);
|
appendStringInfo(buf, sep);
|
||||||
sep = ", ";
|
sep = ", ";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Put out name of target column; look in the catalogs, not at
|
||||||
|
* tle->resname, since resname will fail to track RENAME.
|
||||||
|
*/
|
||||||
appendStringInfoString(buf,
|
appendStringInfoString(buf,
|
||||||
quote_identifier(get_relid_attribute_name(rte->relid,
|
quote_identifier(get_relid_attribute_name(rte->relid,
|
||||||
tle->resdom->resno)));
|
tle->resdom->resno)));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print any indirection needed (subfields or subscripts), and strip
|
||||||
|
* off the top-level nodes representing the indirection assignments.
|
||||||
|
*/
|
||||||
|
strippedexprs = lappend(strippedexprs,
|
||||||
|
processIndirection((Node *) tle->expr,
|
||||||
|
context));
|
||||||
}
|
}
|
||||||
appendStringInfo(buf, ") ");
|
appendStringInfo(buf, ") ");
|
||||||
|
|
||||||
@ -2113,16 +2132,13 @@ get_insert_query_def(Query *query, deparse_context *context)
|
|||||||
appendContextKeyword(context, "VALUES (",
|
appendContextKeyword(context, "VALUES (",
|
||||||
-PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
|
-PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
|
||||||
sep = "";
|
sep = "";
|
||||||
foreach(l, query->targetList)
|
foreach(l, strippedexprs)
|
||||||
{
|
{
|
||||||
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
Node *expr = lfirst(l);
|
||||||
|
|
||||||
if (tle->resdom->resjunk)
|
|
||||||
continue; /* ignore junk entries */
|
|
||||||
|
|
||||||
appendStringInfo(buf, sep);
|
appendStringInfo(buf, sep);
|
||||||
sep = ", ";
|
sep = ", ";
|
||||||
get_rule_expr((Node *) tle->expr, context, false);
|
get_rule_expr(expr, context, false);
|
||||||
}
|
}
|
||||||
appendStringInfoChar(buf, ')');
|
appendStringInfoChar(buf, ')');
|
||||||
}
|
}
|
||||||
@ -2163,6 +2179,7 @@ get_update_query_def(Query *query, deparse_context *context)
|
|||||||
foreach(l, query->targetList)
|
foreach(l, query->targetList)
|
||||||
{
|
{
|
||||||
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||||
|
Node *expr;
|
||||||
|
|
||||||
if (tle->resdom->resjunk)
|
if (tle->resdom->resjunk)
|
||||||
continue; /* ignore junk entries */
|
continue; /* ignore junk entries */
|
||||||
@ -2171,15 +2188,22 @@ get_update_query_def(Query *query, deparse_context *context)
|
|||||||
sep = ", ";
|
sep = ", ";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the update expression is an array assignment, we mustn't put
|
* Put out name of target column; look in the catalogs, not at
|
||||||
* out "attname =" here; it will come out of the display of the
|
* tle->resname, since resname will fail to track RENAME.
|
||||||
* ArrayRef node instead.
|
|
||||||
*/
|
*/
|
||||||
if (!tleIsArrayAssign(tle))
|
appendStringInfoString(buf,
|
||||||
appendStringInfo(buf, "%s = ",
|
|
||||||
quote_identifier(get_relid_attribute_name(rte->relid,
|
quote_identifier(get_relid_attribute_name(rte->relid,
|
||||||
tle->resdom->resno)));
|
tle->resdom->resno)));
|
||||||
get_rule_expr((Node *) tle->expr, context, false);
|
|
||||||
|
/*
|
||||||
|
* Print any indirection needed (subfields or subscripts), and strip
|
||||||
|
* off the top-level nodes representing the indirection assignments.
|
||||||
|
*/
|
||||||
|
expr = processIndirection((Node *) tle->expr, context);
|
||||||
|
|
||||||
|
appendStringInfo(buf, " = ");
|
||||||
|
|
||||||
|
get_rule_expr(expr, context, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add the FROM clause if needed */
|
/* Add the FROM clause if needed */
|
||||||
@ -2452,6 +2476,13 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
|
|||||||
*/
|
*/
|
||||||
return (IsA(parentNode, FieldSelect) ? false : true);
|
return (IsA(parentNode, FieldSelect) ? false : true);
|
||||||
|
|
||||||
|
case T_FieldStore:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* treat like FieldSelect (probably doesn't matter)
|
||||||
|
*/
|
||||||
|
return (IsA(parentNode, FieldStore) ? false : true);
|
||||||
|
|
||||||
case T_CoerceToDomain:
|
case T_CoerceToDomain:
|
||||||
/* maybe simple, check args */
|
/* maybe simple, check args */
|
||||||
return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
|
return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
|
||||||
@ -2757,53 +2788,27 @@ get_rule_expr(Node *node, deparse_context *context,
|
|||||||
case T_ArrayRef:
|
case T_ArrayRef:
|
||||||
{
|
{
|
||||||
ArrayRef *aref = (ArrayRef *) node;
|
ArrayRef *aref = (ArrayRef *) node;
|
||||||
bool savevarprefix = context->varprefix;
|
|
||||||
bool need_parens;
|
bool need_parens;
|
||||||
ListCell *lowlist_item;
|
|
||||||
ListCell *uplist_item;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we are doing UPDATE array[n] = expr, we need to
|
* Parenthesize the argument unless it's a simple Var or
|
||||||
* suppress any prefix on the array name. Currently, that
|
* a FieldSelect. (In particular, if it's another ArrayRef,
|
||||||
* is the only context in which we will see a non-null
|
* we *must* parenthesize to avoid confusion.)
|
||||||
* refassgnexpr --- but someday a smarter test may be
|
|
||||||
* needed.
|
|
||||||
*/
|
*/
|
||||||
if (aref->refassgnexpr)
|
need_parens = !IsA(aref->refexpr, Var) &&
|
||||||
context->varprefix = false;
|
!IsA(aref->refexpr, FieldSelect);
|
||||||
|
|
||||||
/*
|
|
||||||
* Parenthesize the argument unless it's a simple Var.
|
|
||||||
*/
|
|
||||||
need_parens = (aref->refassgnexpr == NULL) &&
|
|
||||||
!IsA(aref->refexpr, Var);
|
|
||||||
if (need_parens)
|
if (need_parens)
|
||||||
appendStringInfoChar(buf, '(');
|
appendStringInfoChar(buf, '(');
|
||||||
get_rule_expr((Node *) aref->refexpr, context, showimplicit);
|
get_rule_expr((Node *) aref->refexpr, context, showimplicit);
|
||||||
if (need_parens)
|
if (need_parens)
|
||||||
appendStringInfoChar(buf, ')');
|
appendStringInfoChar(buf, ')');
|
||||||
context->varprefix = savevarprefix;
|
printSubscripts(aref, context);
|
||||||
lowlist_item = list_head(aref->reflowerindexpr);
|
/*
|
||||||
foreach(uplist_item, aref->refupperindexpr)
|
* Array assignment nodes should have been handled in
|
||||||
{
|
* processIndirection().
|
||||||
appendStringInfo(buf, "[");
|
*/
|
||||||
if (lowlist_item)
|
|
||||||
{
|
|
||||||
get_rule_expr((Node *) lfirst(lowlist_item), context,
|
|
||||||
false);
|
|
||||||
appendStringInfo(buf, ":");
|
|
||||||
lowlist_item = lnext(lowlist_item);
|
|
||||||
}
|
|
||||||
get_rule_expr((Node *) lfirst(uplist_item),
|
|
||||||
context, false);
|
|
||||||
appendStringInfo(buf, "]");
|
|
||||||
}
|
|
||||||
if (aref->refassgnexpr)
|
if (aref->refassgnexpr)
|
||||||
{
|
elog(ERROR, "unexpected refassgnexpr");
|
||||||
appendStringInfo(buf, " = ");
|
|
||||||
get_rule_expr((Node *) aref->refassgnexpr, context,
|
|
||||||
showimplicit);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -2935,6 +2940,7 @@ get_rule_expr(Node *node, deparse_context *context,
|
|||||||
Oid argType = exprType((Node *) fselect->arg);
|
Oid argType = exprType((Node *) fselect->arg);
|
||||||
Oid typrelid;
|
Oid typrelid;
|
||||||
char *fieldname;
|
char *fieldname;
|
||||||
|
bool need_parens;
|
||||||
|
|
||||||
/* lookup arg type and get the field name */
|
/* lookup arg type and get the field name */
|
||||||
typrelid = get_typ_typrelid(argType);
|
typrelid = get_typ_typrelid(argType);
|
||||||
@ -2943,19 +2949,31 @@ get_rule_expr(Node *node, deparse_context *context,
|
|||||||
format_type_be(argType));
|
format_type_be(argType));
|
||||||
fieldname = get_relid_attribute_name(typrelid,
|
fieldname = get_relid_attribute_name(typrelid,
|
||||||
fselect->fieldnum);
|
fselect->fieldnum);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the argument is simple enough, we could emit
|
* Parenthesize the argument unless it's an ArrayRef or
|
||||||
* arg.fieldname, but most cases where FieldSelect is used
|
* another FieldSelect. Note in particular that it would be
|
||||||
* are *not* simple. So, always use parenthesized syntax.
|
* WRONG to not parenthesize a Var argument; simplicity is not
|
||||||
|
* the issue here, having the right number of names is.
|
||||||
*/
|
*/
|
||||||
appendStringInfoChar(buf, '(');
|
need_parens = !IsA(fselect->arg, ArrayRef) &&
|
||||||
get_rule_expr_paren((Node *) fselect->arg, context, true, node);
|
!IsA(fselect->arg, FieldSelect);
|
||||||
appendStringInfoChar(buf, ')');
|
if (need_parens)
|
||||||
|
appendStringInfoChar(buf, '(');
|
||||||
|
get_rule_expr((Node *) fselect->arg, context, true);
|
||||||
|
if (need_parens)
|
||||||
|
appendStringInfoChar(buf, ')');
|
||||||
appendStringInfo(buf, ".%s", quote_identifier(fieldname));
|
appendStringInfo(buf, ".%s", quote_identifier(fieldname));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_FieldStore:
|
||||||
|
/*
|
||||||
|
* We shouldn't see FieldStore here; it should have been
|
||||||
|
* stripped off by processIndirection().
|
||||||
|
*/
|
||||||
|
elog(ERROR, "unexpected FieldStore");
|
||||||
|
break;
|
||||||
|
|
||||||
case T_RelabelType:
|
case T_RelabelType:
|
||||||
{
|
{
|
||||||
RelabelType *relabel = (RelabelType *) node;
|
RelabelType *relabel = (RelabelType *) node;
|
||||||
@ -4043,29 +4061,89 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* tleIsArrayAssign - check for array assignment
|
* processIndirection - take care of array and subfield assignment
|
||||||
|
*
|
||||||
|
* We strip any top-level FieldStore or assignment ArrayRef nodes that
|
||||||
|
* appear in the input, printing out the appropriate decoration for the
|
||||||
|
* base column name (that the caller just printed). We return the
|
||||||
|
* subexpression that's to be assigned.
|
||||||
*/
|
*/
|
||||||
static bool
|
static Node *
|
||||||
tleIsArrayAssign(TargetEntry *tle)
|
processIndirection(Node *node, deparse_context *context)
|
||||||
{
|
{
|
||||||
ArrayRef *aref;
|
StringInfo buf = context->buf;
|
||||||
|
|
||||||
if (tle->expr == NULL || !IsA(tle->expr, ArrayRef))
|
for (;;)
|
||||||
return false;
|
{
|
||||||
aref = (ArrayRef *) tle->expr;
|
if (node == NULL)
|
||||||
if (aref->refassgnexpr == NULL)
|
break;
|
||||||
return false;
|
if (IsA(node, FieldStore))
|
||||||
|
{
|
||||||
|
FieldStore *fstore = (FieldStore *) node;
|
||||||
|
Oid typrelid;
|
||||||
|
char *fieldname;
|
||||||
|
|
||||||
/*
|
/* lookup tuple type */
|
||||||
* Currently, it should only be possible to see non-null refassgnexpr
|
typrelid = get_typ_typrelid(fstore->resulttype);
|
||||||
* if we are indeed looking at an "UPDATE array[n] = expr" situation.
|
if (!OidIsValid(typrelid))
|
||||||
* So aref->refexpr ought to match the tle's target.
|
elog(ERROR, "argument type %s of FieldStore is not a tuple type",
|
||||||
*/
|
format_type_be(fstore->resulttype));
|
||||||
if (aref->refexpr == NULL || !IsA(aref->refexpr, Var) ||
|
/*
|
||||||
((Var *) aref->refexpr)->varattno != tle->resdom->resno)
|
* Get the field name. Note we assume here that there's only
|
||||||
elog(ERROR, "unrecognized situation in array assignment");
|
* one field being assigned to. This is okay in stored rules
|
||||||
|
* but could be wrong in executable target lists. Presently no
|
||||||
|
* problem since explain.c doesn't print plan targetlists, but
|
||||||
|
* someday may have to think of something ...
|
||||||
|
*/
|
||||||
|
fieldname = get_relid_attribute_name(typrelid,
|
||||||
|
linitial_int(fstore->fieldnums));
|
||||||
|
appendStringInfo(buf, ".%s", quote_identifier(fieldname));
|
||||||
|
/*
|
||||||
|
* We ignore arg since it should be an uninteresting reference
|
||||||
|
* to the target column or subcolumn.
|
||||||
|
*/
|
||||||
|
node = (Node *) linitial(fstore->newvals);
|
||||||
|
}
|
||||||
|
else if (IsA(node, ArrayRef))
|
||||||
|
{
|
||||||
|
ArrayRef *aref = (ArrayRef *) node;
|
||||||
|
|
||||||
return true;
|
if (aref->refassgnexpr == NULL)
|
||||||
|
break;
|
||||||
|
printSubscripts(aref, context);
|
||||||
|
/*
|
||||||
|
* We ignore refexpr since it should be an uninteresting reference
|
||||||
|
* to the target column or subcolumn.
|
||||||
|
*/
|
||||||
|
node = (Node *) aref->refassgnexpr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
printSubscripts(ArrayRef *aref, deparse_context *context)
|
||||||
|
{
|
||||||
|
StringInfo buf = context->buf;
|
||||||
|
ListCell *lowlist_item;
|
||||||
|
ListCell *uplist_item;
|
||||||
|
|
||||||
|
lowlist_item = list_head(aref->reflowerindexpr); /* could be NULL */
|
||||||
|
foreach(uplist_item, aref->refupperindexpr)
|
||||||
|
{
|
||||||
|
appendStringInfoChar(buf, '[');
|
||||||
|
if (lowlist_item)
|
||||||
|
{
|
||||||
|
get_rule_expr((Node *) lfirst(lowlist_item), context, false);
|
||||||
|
appendStringInfoChar(buf, ':');
|
||||||
|
lowlist_item = lnext(lowlist_item);
|
||||||
|
}
|
||||||
|
get_rule_expr((Node *) lfirst(uplist_item), context, false);
|
||||||
|
appendStringInfoChar(buf, ']');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.116 2004/05/10 22:44:49 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.117 2004/06/09 19:08:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -574,6 +574,18 @@ typedef struct FieldSelectState
|
|||||||
TupleDesc argdesc; /* tupdesc for most recent input */
|
TupleDesc argdesc; /* tupdesc for most recent input */
|
||||||
} FieldSelectState;
|
} FieldSelectState;
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* FieldStoreState node
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
typedef struct FieldStoreState
|
||||||
|
{
|
||||||
|
ExprState xprstate;
|
||||||
|
ExprState *arg; /* input tuple value */
|
||||||
|
List *newvals; /* new value(s) for field(s) */
|
||||||
|
TupleDesc argdesc; /* tupdesc for most recent input */
|
||||||
|
} FieldStoreState;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* CaseExprState node
|
* CaseExprState node
|
||||||
* ----------------
|
* ----------------
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.156 2004/05/26 13:57:02 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.157 2004/06/09 19:08:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -110,6 +110,7 @@ typedef enum NodeTag
|
|||||||
T_SubLink,
|
T_SubLink,
|
||||||
T_SubPlan,
|
T_SubPlan,
|
||||||
T_FieldSelect,
|
T_FieldSelect,
|
||||||
|
T_FieldStore,
|
||||||
T_RelabelType,
|
T_RelabelType,
|
||||||
T_CaseExpr,
|
T_CaseExpr,
|
||||||
T_CaseWhen,
|
T_CaseWhen,
|
||||||
@ -143,6 +144,7 @@ typedef enum NodeTag
|
|||||||
T_BoolExprState,
|
T_BoolExprState,
|
||||||
T_SubPlanState,
|
T_SubPlanState,
|
||||||
T_FieldSelectState,
|
T_FieldSelectState,
|
||||||
|
T_FieldStoreState,
|
||||||
T_CaseExprState,
|
T_CaseExprState,
|
||||||
T_CaseWhenState,
|
T_CaseWhenState,
|
||||||
T_ArrayExprState,
|
T_ArrayExprState,
|
||||||
@ -274,7 +276,7 @@ typedef enum NodeTag
|
|||||||
T_A_Const,
|
T_A_Const,
|
||||||
T_FuncCall,
|
T_FuncCall,
|
||||||
T_A_Indices,
|
T_A_Indices,
|
||||||
T_ExprFieldSelect,
|
T_A_Indirection,
|
||||||
T_ResTarget,
|
T_ResTarget,
|
||||||
T_TypeCast,
|
T_TypeCast,
|
||||||
T_SortBy,
|
T_SortBy,
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.257 2004/06/02 21:01:09 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.258 2004/06/09 19:08:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -166,26 +166,26 @@ typedef struct TypeName
|
|||||||
* ColumnRef - specifies a reference to a column, or possibly a whole tuple
|
* ColumnRef - specifies a reference to a column, or possibly a whole tuple
|
||||||
*
|
*
|
||||||
* The "fields" list must be nonempty; its last component may be "*"
|
* The "fields" list must be nonempty; its last component may be "*"
|
||||||
* instead of a field name. Subscripts are optional.
|
* instead of a regular field name.
|
||||||
|
*
|
||||||
|
* Note: any array subscripting or selection of fields from composite columns
|
||||||
|
* is represented by an A_Indirection node above the ColumnRef. However,
|
||||||
|
* for simplicity in the normal case, initial field selection from a table
|
||||||
|
* name is represented within ColumnRef and not by adding A_Indirection.
|
||||||
*/
|
*/
|
||||||
typedef struct ColumnRef
|
typedef struct ColumnRef
|
||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
List *fields; /* field names (list of Value strings) */
|
List *fields; /* field names (list of Value strings) */
|
||||||
List *indirection; /* subscripts (list of A_Indices) */
|
|
||||||
} ColumnRef;
|
} ColumnRef;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ParamRef - specifies a parameter reference
|
* ParamRef - specifies a $n parameter reference
|
||||||
*
|
|
||||||
* The parameter could be qualified with field names and/or subscripts
|
|
||||||
*/
|
*/
|
||||||
typedef struct ParamRef
|
typedef struct ParamRef
|
||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
int number; /* the number of the parameter */
|
int number; /* the number of the parameter */
|
||||||
List *fields; /* field names (list of Value strings) */
|
|
||||||
List *indirection; /* subscripts (list of A_Indices) */
|
|
||||||
} ParamRef;
|
} ParamRef;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -267,40 +267,50 @@ typedef struct A_Indices
|
|||||||
} A_Indices;
|
} A_Indices;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ExprFieldSelect - select a field and/or array element from an expression
|
* A_Indirection - select a field and/or array element from an expression
|
||||||
*
|
*
|
||||||
* This is used in the raw parsetree to represent selection from an
|
* The indirection list can contain both A_Indices nodes (representing
|
||||||
* arbitrary expression (not a column or param reference). Either
|
* subscripting) and string Value nodes (representing field selection
|
||||||
* fields or indirection may be NIL if not used.
|
* --- the string value is the name of the field to select). For example,
|
||||||
|
* a complex selection operation like
|
||||||
|
* (foo).field1[42][7].field2
|
||||||
|
* would be represented with a single A_Indirection node having a 4-element
|
||||||
|
* indirection list.
|
||||||
|
*
|
||||||
|
* Note: as of Postgres 7.5, we don't support arrays of composite values,
|
||||||
|
* so cases in which a field select follows a subscript aren't actually
|
||||||
|
* semantically legal. However the parser is prepared to handle such.
|
||||||
*/
|
*/
|
||||||
typedef struct ExprFieldSelect
|
typedef struct A_Indirection
|
||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
Node *arg; /* the thing being selected from */
|
Node *arg; /* the thing being selected from */
|
||||||
List *fields; /* field names (list of Value strings) */
|
List *indirection; /* subscripts and/or field names */
|
||||||
List *indirection; /* subscripts (list of A_Indices) */
|
} A_Indirection;
|
||||||
} ExprFieldSelect;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ResTarget -
|
* ResTarget -
|
||||||
* result target (used in target list of pre-transformed Parse trees)
|
* result target (used in target list of pre-transformed parse trees)
|
||||||
*
|
*
|
||||||
* In a SELECT or INSERT target list, 'name' is either NULL or
|
* In a SELECT or INSERT target list, 'name' is the column label from an
|
||||||
* the column name assigned to the value. (If there is an 'AS ColumnLabel'
|
* 'AS ColumnLabel' clause, or NULL if there was none, and 'val' is the
|
||||||
* clause, the grammar sets 'name' from it; otherwise 'name' is initially NULL
|
* value expression itself. The 'indirection' field is not used.
|
||||||
* and is filled in during the parse analysis phase.)
|
*
|
||||||
* The 'indirection' field is not used at all.
|
* INSERT has a second ResTarget list which is the target-column-names list.
|
||||||
|
* Here, 'val' is not used, 'name' is the name of the destination column,
|
||||||
|
* and 'indirection' stores any subscripts attached to the destination.
|
||||||
*
|
*
|
||||||
* In an UPDATE target list, 'name' is the name of the destination column,
|
* In an UPDATE target list, 'name' is the name of the destination column,
|
||||||
* and 'indirection' stores any subscripts attached to the destination.
|
* 'indirection' stores any subscripts attached to the destination, and
|
||||||
* That is, our representation is UPDATE table SET name [indirection] = val.
|
* 'val' is the expression to assign.
|
||||||
|
*
|
||||||
|
* See A_Indirection for more info about what can appear in 'indirection'.
|
||||||
*/
|
*/
|
||||||
typedef struct ResTarget
|
typedef struct ResTarget
|
||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
char *name; /* column name or NULL */
|
char *name; /* column name or NULL */
|
||||||
List *indirection; /* subscripts for destination column, or
|
List *indirection; /* subscripts and field names, or NIL */
|
||||||
* NIL */
|
|
||||||
Node *val; /* the value expression to compute or
|
Node *val; /* the value expression to compute or
|
||||||
* assign */
|
* assign */
|
||||||
} ResTarget;
|
} ResTarget;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.99 2004/05/30 23:40:39 neilc Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.100 2004/06/09 19:08:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -36,14 +36,15 @@
|
|||||||
* ordinal position (counting from 1). However, in an INSERT or UPDATE
|
* ordinal position (counting from 1). However, in an INSERT or UPDATE
|
||||||
* targetlist, resno represents the attribute number of the destination
|
* targetlist, resno represents the attribute number of the destination
|
||||||
* column for the item; so there may be missing or out-of-order resnos.
|
* column for the item; so there may be missing or out-of-order resnos.
|
||||||
* In an UPDATE, it is even legal to have duplicated resnos; consider
|
* It is even legal to have duplicated resnos; consider
|
||||||
* UPDATE table SET arraycol[1] = ..., arraycol[2] = ..., ...
|
* UPDATE table SET arraycol[1] = ..., arraycol[2] = ..., ...
|
||||||
* The two meanings come together in the executor, because the planner
|
* The two meanings come together in the executor, because the planner
|
||||||
* transforms INSERT/UPDATE tlists into a normalized form with exactly
|
* transforms INSERT/UPDATE tlists into a normalized form with exactly
|
||||||
* one entry for each column of the destination table. Before that's
|
* one entry for each column of the destination table. Before that's
|
||||||
* happened, however, it is risky to assume that resno == position.
|
* happened, however, it is risky to assume that resno == position.
|
||||||
* Generally get_tle_by_resno() should be used rather than list_nth()
|
* Generally get_tle_by_resno() should be used rather than list_nth()
|
||||||
* to fetch tlist entries by resno.
|
* to fetch tlist entries by resno, and only in SELECT should you assume
|
||||||
|
* that resno is a unique identifier.
|
||||||
*
|
*
|
||||||
* resname is required to represent the correct column name in non-resjunk
|
* resname is required to represent the correct column name in non-resjunk
|
||||||
* entries of top-level SELECT targetlists, since it will be used as the
|
* entries of top-level SELECT targetlists, since it will be used as the
|
||||||
@ -540,6 +541,31 @@ typedef struct FieldSelect
|
|||||||
int32 resulttypmod; /* output typmod (usually -1) */
|
int32 resulttypmod; /* output typmod (usually -1) */
|
||||||
} FieldSelect;
|
} FieldSelect;
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* FieldStore
|
||||||
|
*
|
||||||
|
* FieldStore represents the operation of modifying one field in a tuple
|
||||||
|
* value, yielding a new tuple value (the input is not touched!). Like
|
||||||
|
* the assign case of ArrayRef, this is used to implement UPDATE of a
|
||||||
|
* portion of a column.
|
||||||
|
*
|
||||||
|
* A single FieldStore can actually represent updates of several different
|
||||||
|
* fields. The parser only generates FieldStores with single-element lists,
|
||||||
|
* but the planner will collapse multiple updates of the same base column
|
||||||
|
* into one FieldStore.
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct FieldStore
|
||||||
|
{
|
||||||
|
Expr xpr;
|
||||||
|
Expr *arg; /* input tuple value */
|
||||||
|
List *newvals; /* new value(s) for field(s) */
|
||||||
|
List *fieldnums; /* integer list of field attnums */
|
||||||
|
Oid resulttype; /* type of result (same as type of arg) */
|
||||||
|
/* Like RowExpr, we deliberately omit a typmod here */
|
||||||
|
} FieldStore;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* RelabelType
|
* RelabelType
|
||||||
*
|
*
|
||||||
@ -607,6 +633,9 @@ typedef struct CaseWhen
|
|||||||
* Placeholder node for the test value to be processed by a CASE expression.
|
* Placeholder node for the test value to be processed by a CASE expression.
|
||||||
* This is effectively like a Param, but can be implemented more simply
|
* This is effectively like a Param, but can be implemented more simply
|
||||||
* since we need only one replacement value at a time.
|
* since we need only one replacement value at a time.
|
||||||
|
*
|
||||||
|
* We also use this in nested UPDATE expressions.
|
||||||
|
* See transformAssignmentIndirection().
|
||||||
*/
|
*/
|
||||||
typedef struct CaseTestExpr
|
typedef struct CaseTestExpr
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.38 2003/11/29 22:41:09 pgsql Exp $
|
* $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.39 2004/06/09 19:08:19 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -67,12 +67,13 @@ typedef struct ParseState
|
|||||||
|
|
||||||
extern ParseState *make_parsestate(ParseState *parentParseState);
|
extern ParseState *make_parsestate(ParseState *parentParseState);
|
||||||
extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno);
|
extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno);
|
||||||
|
extern Oid transformArrayType(Oid arrayType);
|
||||||
extern ArrayRef *transformArraySubscripts(ParseState *pstate,
|
extern ArrayRef *transformArraySubscripts(ParseState *pstate,
|
||||||
Node *arrayBase,
|
Node *arrayBase,
|
||||||
Oid arrayType,
|
Oid arrayType,
|
||||||
int32 arrayTypMod,
|
Oid elementType,
|
||||||
|
int32 elementTypMod,
|
||||||
List *indirection,
|
List *indirection,
|
||||||
bool forceSlice,
|
|
||||||
Node *assignFrom);
|
Node *assignFrom);
|
||||||
extern Const *make_const(Value *value);
|
extern Const *make_const(Value *value);
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.106 2004/06/06 00:41:28 tgl Exp $
|
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.107 2004/06/09 19:08:19 tgl Exp $
|
||||||
*
|
*
|
||||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -3852,6 +3852,18 @@ exec_simple_check_node(Node *node)
|
|||||||
case T_FieldSelect:
|
case T_FieldSelect:
|
||||||
return exec_simple_check_node((Node *) ((FieldSelect *) node)->arg);
|
return exec_simple_check_node((Node *) ((FieldSelect *) node)->arg);
|
||||||
|
|
||||||
|
case T_FieldStore:
|
||||||
|
{
|
||||||
|
FieldStore *expr = (FieldStore *) node;
|
||||||
|
|
||||||
|
if (!exec_simple_check_node((Node *) expr->arg))
|
||||||
|
return FALSE;
|
||||||
|
if (!exec_simple_check_node((Node *) expr->newvals))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
case T_RelabelType:
|
case T_RelabelType:
|
||||||
return exec_simple_check_node((Node *) ((RelabelType *) node)->arg);
|
return exec_simple_check_node((Node *) ((RelabelType *) node)->arg);
|
||||||
|
|
||||||
|
@ -11,26 +11,25 @@ CREATE TABLE arrtest (
|
|||||||
g varchar(5)[]
|
g varchar(5)[]
|
||||||
);
|
);
|
||||||
--
|
--
|
||||||
-- only this array as a 0-based 'e', the others are 1-based.
|
-- only the 'e' array is 0-based, the others are 1-based.
|
||||||
-- 'e' is also a large object.
|
|
||||||
--
|
--
|
||||||
INSERT INTO arrtest (a[5], b[2][1][2], c, d, f, g)
|
INSERT INTO arrtest (a[1:5], b[1:1][1:2][1:2], c, d, f, g)
|
||||||
VALUES ('{1,2,3,4,5}', '{{{0,0},{1,2}}}', '{}', '{}', '{}', '{}');
|
VALUES ('{1,2,3,4,5}', '{{{0,0},{1,2}}}', '{}', '{}', '{}', '{}');
|
||||||
UPDATE arrtest SET e[0] = '1.1';
|
UPDATE arrtest SET e[0] = '1.1';
|
||||||
UPDATE arrtest SET e[1] = '2.2';
|
UPDATE arrtest SET e[1] = '2.2';
|
||||||
INSERT INTO arrtest (f)
|
INSERT INTO arrtest (f)
|
||||||
VALUES ('{"too long"}');
|
VALUES ('{"too long"}');
|
||||||
ERROR: value too long for type character(5)
|
ERROR: value too long for type character(5)
|
||||||
INSERT INTO arrtest (a, b[2][2][1], c, d, e, f, g)
|
INSERT INTO arrtest (a, b[1:2][1:2], c, d, e, f, g)
|
||||||
VALUES ('{11,12,23}', '{{3,4},{4,5}}', '{"foobar"}',
|
VALUES ('{11,12,23}', '{{3,4},{4,5}}', '{"foobar"}',
|
||||||
'{{"elt1", "elt2"}}', '{"3.4", "6.7"}',
|
'{{"elt1", "elt2"}}', '{"3.4", "6.7"}',
|
||||||
'{"abc","abcde"}', '{"abc","abcde"}');
|
'{"abc","abcde"}', '{"abc","abcde"}');
|
||||||
INSERT INTO arrtest (a, b[1][2][2], c, d[2][1])
|
INSERT INTO arrtest (a, b[1:2], c, d[1:2])
|
||||||
VALUES ('{}', '{3,4}', '{foo,bar}', '{bar,foo}');
|
VALUES ('{}', '{3,4}', '{foo,bar}', '{bar,foo}');
|
||||||
SELECT * FROM arrtest;
|
SELECT * FROM arrtest;
|
||||||
a | b | c | d | e | f | g
|
a | b | c | d | e | f | g
|
||||||
-------------+-----------------+-----------+---------------+-----------+-----------------+-------------
|
-------------+-----------------+-----------+---------------+-----------+-----------------+-------------
|
||||||
{1,2,3,4,5} | {{{0,0},{1,2}}} | {} | {} | | {} | {}
|
{1,2,3,4,5} | {{{0,0},{1,2}}} | {} | {} | {1.1,2.2} | {} | {}
|
||||||
{11,12,23} | {{3,4},{4,5}} | {foobar} | {{elt1,elt2}} | {3.4,6.7} | {"abc ",abcde} | {abc,abcde}
|
{11,12,23} | {{3,4},{4,5}} | {foobar} | {{elt1,elt2}} | {3.4,6.7} | {"abc ",abcde} | {abc,abcde}
|
||||||
{} | {3,4} | {foo,bar} | {bar,foo} | | |
|
{} | {3,4} | {foo,bar} | {bar,foo} | | |
|
||||||
(3 rows)
|
(3 rows)
|
||||||
@ -41,20 +40,20 @@ SELECT arrtest.a[1],
|
|||||||
arrtest.d[1][1],
|
arrtest.d[1][1],
|
||||||
arrtest.e[0]
|
arrtest.e[0]
|
||||||
FROM arrtest;
|
FROM arrtest;
|
||||||
a | b | c | d | e
|
a | b | c | d | e
|
||||||
----+---+--------+------+---
|
----+---+--------+------+-----
|
||||||
1 | 0 | | |
|
1 | 0 | | | 1.1
|
||||||
11 | | foobar | elt1 |
|
11 | | foobar | elt1 |
|
||||||
| | foo | |
|
| | foo | |
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
SELECT a[1], b[1][1][1], c[1], d[1][1], e[0]
|
SELECT a[1], b[1][1][1], c[1], d[1][1], e[0]
|
||||||
FROM arrtest;
|
FROM arrtest;
|
||||||
a | b | c | d | e
|
a | b | c | d | e
|
||||||
----+---+--------+------+---
|
----+---+--------+------+-----
|
||||||
1 | 0 | | |
|
1 | 0 | | | 1.1
|
||||||
11 | | foobar | elt1 |
|
11 | | foobar | elt1 |
|
||||||
| | foo | |
|
| | foo | |
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
SELECT a[1:3],
|
SELECT a[1:3],
|
||||||
|
@ -87,19 +87,23 @@ select * from people;
|
|||||||
(Joe,Blow,) | 01-10-1984
|
(Joe,Blow,) | 01-10-1984
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
-- This fails at the moment, would like it to work though:
|
-- test insertion/updating of subfields
|
||||||
update people set fn.suffix = 'Jr';
|
update people set fn.suffix = 'Jr';
|
||||||
ERROR: syntax error at or near "." at character 21
|
|
||||||
LINE 1: update people set fn.suffix = 'Jr';
|
|
||||||
^
|
|
||||||
-- ugly workaround:
|
|
||||||
update people set fn = ((fn).first, (fn).last, 'III');
|
|
||||||
select * from people;
|
select * from people;
|
||||||
fn | bd
|
fn | bd
|
||||||
----------------+------------
|
---------------+------------
|
||||||
(Joe,Blow,III) | 01-10-1984
|
(Joe,Blow,Jr) | 01-10-1984
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
insert into quadtable (f1, q.c1.r, q.c2.i) values(44,55,66);
|
||||||
|
select * from quadtable;
|
||||||
|
f1 | q
|
||||||
|
----+---------------------------
|
||||||
|
1 | ("(3.3,4.4)","(5.5,6.6)")
|
||||||
|
2 | ("(,4.4)","(5.5,6.6)")
|
||||||
|
44 | ("(55,)","(,66)")
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
-- The object here is to ensure that toasted references inside
|
-- The object here is to ensure that toasted references inside
|
||||||
-- composite values don't cause problems. The large f1 value will
|
-- composite values don't cause problems. The large f1 value will
|
||||||
-- be toasted inside pp, it must still work after being copied to people.
|
-- be toasted inside pp, it must still work after being copied to people.
|
||||||
|
@ -13,11 +13,10 @@ CREATE TABLE arrtest (
|
|||||||
);
|
);
|
||||||
|
|
||||||
--
|
--
|
||||||
-- only this array as a 0-based 'e', the others are 1-based.
|
-- only the 'e' array is 0-based, the others are 1-based.
|
||||||
-- 'e' is also a large object.
|
|
||||||
--
|
--
|
||||||
|
|
||||||
INSERT INTO arrtest (a[5], b[2][1][2], c, d, f, g)
|
INSERT INTO arrtest (a[1:5], b[1:1][1:2][1:2], c, d, f, g)
|
||||||
VALUES ('{1,2,3,4,5}', '{{{0,0},{1,2}}}', '{}', '{}', '{}', '{}');
|
VALUES ('{1,2,3,4,5}', '{{{0,0},{1,2}}}', '{}', '{}', '{}', '{}');
|
||||||
|
|
||||||
UPDATE arrtest SET e[0] = '1.1';
|
UPDATE arrtest SET e[0] = '1.1';
|
||||||
@ -27,12 +26,12 @@ UPDATE arrtest SET e[1] = '2.2';
|
|||||||
INSERT INTO arrtest (f)
|
INSERT INTO arrtest (f)
|
||||||
VALUES ('{"too long"}');
|
VALUES ('{"too long"}');
|
||||||
|
|
||||||
INSERT INTO arrtest (a, b[2][2][1], c, d, e, f, g)
|
INSERT INTO arrtest (a, b[1:2][1:2], c, d, e, f, g)
|
||||||
VALUES ('{11,12,23}', '{{3,4},{4,5}}', '{"foobar"}',
|
VALUES ('{11,12,23}', '{{3,4},{4,5}}', '{"foobar"}',
|
||||||
'{{"elt1", "elt2"}}', '{"3.4", "6.7"}',
|
'{{"elt1", "elt2"}}', '{"3.4", "6.7"}',
|
||||||
'{"abc","abcde"}', '{"abc","abcde"}');
|
'{"abc","abcde"}', '{"abc","abcde"}');
|
||||||
|
|
||||||
INSERT INTO arrtest (a, b[1][2][2], c, d[2][1])
|
INSERT INTO arrtest (a, b[1:2], c, d[1:2])
|
||||||
VALUES ('{}', '{3,4}', '{foo,bar}', '{bar,foo}');
|
VALUES ('{}', '{3,4}', '{foo,bar}', '{bar,foo}');
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,14 +53,15 @@ alter table fullname add column suffix text default null;
|
|||||||
|
|
||||||
select * from people;
|
select * from people;
|
||||||
|
|
||||||
-- This fails at the moment, would like it to work though:
|
-- test insertion/updating of subfields
|
||||||
update people set fn.suffix = 'Jr';
|
update people set fn.suffix = 'Jr';
|
||||||
|
|
||||||
-- ugly workaround:
|
|
||||||
update people set fn = ((fn).first, (fn).last, 'III');
|
|
||||||
|
|
||||||
select * from people;
|
select * from people;
|
||||||
|
|
||||||
|
insert into quadtable (f1, q.c1.r, q.c2.i) values(44,55,66);
|
||||||
|
|
||||||
|
select * from quadtable;
|
||||||
|
|
||||||
-- The object here is to ensure that toasted references inside
|
-- The object here is to ensure that toasted references inside
|
||||||
-- composite values don't cause problems. The large f1 value will
|
-- composite values don't cause problems. The large f1 value will
|
||||||
-- be toasted inside pp, it must still work after being copied to people.
|
-- be toasted inside pp, it must still work after being copied to people.
|
||||||
|
Reference in New Issue
Block a user