Commit 2f63232d authored by Tom Lane's avatar Tom Lane

Promote row expressions to full-fledged citizens of the expression syntax,

rather than allowing them only in a few special cases as before.  In
particular you can now pass a ROW() construct to a function that accepts
a rowtype parameter.  Internal generation of RowExprs fixes a number of
corner cases that used to not work very well, such as referencing the
whole-row result of a JOIN or subquery.  This represents a further step in
the work I started a month or so back to make rowtype values into
first-class citizens.
parent 9a939886
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.200 2004/05/10 21:08:28 neilc Exp $ $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.201 2004/05/10 22:44:42 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -7822,13 +7822,15 @@ SELECT col1 FROM tab1 ...@@ -7822,13 +7822,15 @@ SELECT col1 FROM tab1
</para> </para>
<synopsis> <synopsis>
(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) IN (<replaceable>subquery</replaceable>) <replaceable>row_constructor</replaceable> IN (<replaceable>subquery</replaceable>)
</synopsis> </synopsis>
<para> <para>
The right-hand side of this form of <token>IN</token> is a parenthesized The left-hand side of this form of <token>IN</token> is a row constructor,
as described in <xref linkend="sql-syntax-row-constructors">.
The right-hand side is a parenthesized
subquery, which must return exactly as many columns as there are subquery, which must return exactly as many columns as there are
expressions in the left-hand list. The left-hand expressions are expressions in the left-hand row. The left-hand expressions are
evaluated and compared row-wise to each row of the subquery result. evaluated and compared row-wise to each row of the subquery result.
The result of <token>IN</token> is <quote>true</> if any equal subquery row is found. The result of <token>IN</token> is <quote>true</> if any equal subquery row is found.
The result is <quote>false</> if no equal row is found (including the special The result is <quote>false</> if no equal row is found (including the special
...@@ -7876,13 +7878,15 @@ SELECT col1 FROM tab1 ...@@ -7876,13 +7878,15 @@ SELECT col1 FROM tab1
</para> </para>
<synopsis> <synopsis>
(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) NOT IN (<replaceable>subquery</replaceable>) <replaceable>row_constructor</replaceable> NOT IN (<replaceable>subquery</replaceable>)
</synopsis> </synopsis>
<para> <para>
The right-hand side of this form of <token>NOT IN</token> is a parenthesized The left-hand side of this form of <token>NOT IN</token> is a row constructor,
as described in <xref linkend="sql-syntax-row-constructors">.
The right-hand side is a parenthesized
subquery, which must return exactly as many columns as there are subquery, which must return exactly as many columns as there are
expressions in the left-hand list. The left-hand expressions are expressions in the left-hand row. The left-hand expressions are
evaluated and compared row-wise to each row of the subquery result. evaluated and compared row-wise to each row of the subquery result.
The result of <token>NOT IN</token> is <quote>true</> if only unequal subquery rows The result of <token>NOT IN</token> is <quote>true</> if only unequal subquery rows
are found (including the special case where the subquery returns no rows). are found (including the special case where the subquery returns no rows).
...@@ -7938,14 +7942,16 @@ SELECT col1 FROM tab1 ...@@ -7938,14 +7942,16 @@ SELECT col1 FROM tab1
</para> </para>
<synopsis> <synopsis>
(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) <replaceable>operator</> ANY (<replaceable>subquery</replaceable>) <replaceable>row_constructor</replaceable> <replaceable>operator</> ANY (<replaceable>subquery</replaceable>)
(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) <replaceable>operator</> SOME (<replaceable>subquery</replaceable>) <replaceable>row_constructor</replaceable> <replaceable>operator</> SOME (<replaceable>subquery</replaceable>)
</synopsis> </synopsis>
<para> <para>
The right-hand side of this form of <token>ANY</token> is a parenthesized The left-hand side of this form of <token>ANY</token> is a row constructor,
as described in <xref linkend="sql-syntax-row-constructors">.
The right-hand side is a parenthesized
subquery, which must return exactly as many columns as there are subquery, which must return exactly as many columns as there are
expressions in the left-hand list. The left-hand expressions are expressions in the left-hand row. The left-hand expressions are
evaluated and compared row-wise to each row of the subquery result, evaluated and compared row-wise to each row of the subquery result,
using the given <replaceable>operator</replaceable>. Presently, using the given <replaceable>operator</replaceable>. Presently,
only <literal>=</literal> and <literal>&lt;&gt;</literal> operators are allowed only <literal>=</literal> and <literal>&lt;&gt;</literal> operators are allowed
...@@ -8003,13 +8009,15 @@ SELECT col1 FROM tab1 ...@@ -8003,13 +8009,15 @@ SELECT col1 FROM tab1
</para> </para>
<synopsis> <synopsis>
(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) <replaceable>operator</replaceable> ALL (<replaceable>subquery</replaceable>) <replaceable>row_constructor</replaceable> <replaceable>operator</replaceable> ALL (<replaceable>subquery</replaceable>)
</synopsis> </synopsis>
<para> <para>
The right-hand side of this form of <token>ALL</token> is a parenthesized The left-hand side of this form of <token>ALL</token> is a row constructor,
as described in <xref linkend="sql-syntax-row-constructors">.
The right-hand side is a parenthesized
subquery, which must return exactly as many columns as there are subquery, which must return exactly as many columns as there are
expressions in the left-hand list. The left-hand expressions are expressions in the left-hand row. The left-hand expressions are
evaluated and compared row-wise to each row of the subquery result, evaluated and compared row-wise to each row of the subquery result,
using the given <replaceable>operator</replaceable>. Presently, using the given <replaceable>operator</replaceable>. Presently,
only <literal>=</literal> and <literal>&lt;&gt;</literal> operators are allowed only <literal>=</literal> and <literal>&lt;&gt;</literal> operators are allowed
...@@ -8041,16 +8049,17 @@ SELECT col1 FROM tab1 ...@@ -8041,16 +8049,17 @@ SELECT col1 FROM tab1
</indexterm> </indexterm>
<synopsis> <synopsis>
(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) <replaceable>operator</replaceable> (<replaceable>subquery</replaceable>) <replaceable>row_constructor</replaceable> <replaceable>operator</replaceable> (<replaceable>subquery</replaceable>)
</synopsis> </synopsis>
<para> <para>
The left-hand side is a list of scalar expressions. The right-hand side is The left-hand side is a row constructor,
a parenthesized subquery, which must return exactly as many columns as there as described in <xref linkend="sql-syntax-row-constructors">.
are expressions on the left-hand side. Furthermore, the subquery cannot The right-hand side is a parenthesized subquery, which must return exactly
return more than one row. (If it returns zero rows, the result is taken to as many columns as there are expressions in the left-hand row. Furthermore,
be null.) The left-hand side is evaluated and compared row-wise to the the subquery cannot return more than one row. (If it returns zero rows,
single subquery result row. the result is taken to be null.) The left-hand side is evaluated and
compared row-wise to the single subquery result row.
Presently, only <literal>=</literal> and <literal>&lt;&gt;</literal> operators are allowed Presently, only <literal>=</literal> and <literal>&lt;&gt;</literal> operators are allowed
in row-wise comparisons. in row-wise comparisons.
The result is <quote>true</> if the two rows are equal or unequal, respectively. The result is <quote>true</> if the two rows are equal or unequal, respectively.
...@@ -8223,13 +8232,14 @@ AND ...@@ -8223,13 +8232,14 @@ AND
<title>Row-wise Comparison</title> <title>Row-wise Comparison</title>
<synopsis> <synopsis>
(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) <replaceable>operator</replaceable> (<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) <replaceable>row_constructor</replaceable> <replaceable>operator</replaceable> <replaceable>row_constructor</replaceable>
</synopsis> </synopsis>
<para> <para>
Each side is a list of scalar expressions; the two lists must be Each side is a row constructor,
of the same length. Each side is evaluated and they are compared as described in <xref linkend="sql-syntax-row-constructors">.
row-wise. The two row values must have the same number of fields.
Each side is evaluated and they are compared row-wise.
Presently, only <literal>=</literal> and <literal>&lt;&gt;</literal> operators are allowed Presently, only <literal>=</literal> and <literal>&lt;&gt;</literal> operators are allowed
in row-wise comparisons. in row-wise comparisons.
The result is <quote>true</> if the two rows are equal or unequal, respectively. The result is <quote>true</> if the two rows are equal or unequal, respectively.
...@@ -8242,6 +8252,29 @@ AND ...@@ -8242,6 +8252,29 @@ AND
are unequal if any corresponding members are non-null and unequal; are unequal if any corresponding members are non-null and unequal;
otherwise the result of the row comparison is unknown (null). otherwise the result of the row comparison is unknown (null).
</para> </para>
<synopsis>
<replaceable>row_constructor</replaceable> IS DISTINCT FROM <replaceable>row_constructor</replaceable>
</synopsis>
<para>
This construct is similar to a <literal>&lt;&gt;</literal> row comparison,
but it does not yield null for null inputs. Instead, any null value is
considered unequal to (distinct from) any non-null value, and any two
nulls are considered equal (not distinct). Thus the result will always
be either true or false, never null.
</para>
<synopsis>
<replaceable>row_constructor</replaceable> IS NULL
<replaceable>row_constructor</replaceable> IS NOT NULL
</synopsis>
<para>
These constructs test a row value for null or not null. A row value
is considered not null if it has at least one field that is not null.
</para>
</sect2> </sect2>
</sect1> </sect1>
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.90 2004/03/12 00:25:40 neilc Exp $ $PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.91 2004/05/10 22:44:43 tgl Exp $
--> -->
<chapter id="sql-syntax"> <chapter id="sql-syntax">
...@@ -920,6 +920,12 @@ SELECT 3 OPERATOR(pg_catalog.+) 4; ...@@ -920,6 +920,12 @@ SELECT 3 OPERATOR(pg_catalog.+) 4;
</para> </para>
</listitem> </listitem>
<listitem>
<para>
A row constructor.
</para>
</listitem>
<listitem> <listitem>
<para> <para>
Another value expression in parentheses, useful to group Another value expression in parentheses, useful to group
...@@ -1428,6 +1434,79 @@ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%'); ...@@ -1428,6 +1434,79 @@ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
</sect2> </sect2>
<sect2 id="sql-syntax-row-constructors">
<title>Row Constructors</title>
<indexterm>
<primary>row</primary>
<secondary>constructor</secondary>
</indexterm>
<para>
A row constructor is an expression that builds a row value from values
for its member fields. A row constructor consists of the key word
<literal>ROW</literal>, a left parenthesis <literal>(</>, zero or more
expressions (separated by commas) for the row field values, and finally
a right parenthesis <literal>)</>. For example,
<programlisting>
SELECT myfunc(ROW(1,2.5,'this is a test'));
</programlisting>
The key word <literal>ROW</> is optional when there is more than one
expression in the list.
</para>
<para>
By default, the value created by a <literal>ROW</> expression is of
an anonymous record type. If necessary, it can be cast to a named
composite type --- either the rowtype of a table, or a composite type
created with <command>CREATE TYPE AS</>. An explicit cast may be needed
to avoid ambiguity. For example:
<programlisting>
CREATE TABLE mytable(f1 int, f2 float, f3 text);
CREATE FUNCTION getf1(mytable) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL;
-- No cast needed since only one getf1() exists
SELECT getf1(ROW(1,2.5,'this is a test'));
getf1
-------
1
(1 row)
CREATE TYPE myrowtype AS (f1 int, f2 text, f3 numeric);
CREATE FUNCTION getf1(myrowtype) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL;
-- Now we need a cast to indicate which function to call:
SELECT getf1(ROW(1,2.5,'this is a test'));
ERROR: function getf1(record) is not unique
SELECT getf1(ROW(1,2.5,'this is a test')::mytable);
getf1
-------
1
(1 row)
SELECT getf1(CAST(ROW(11,'this is a test',2.5) AS myrowtype));
getf1
-------
11
(1 row)
</programlisting>
</para>
<para>
Row constructors have only limited uses, other than creating an argument
value for a user-defined function that accepts a rowtype parameter, as
illustrated above.
It is possible to compare two row values or test a row with
<literal>IS NULL</> or <literal>IS NOT NULL</>, for example
<programlisting>
SELECT ROW(1,2.5,'this is a test') = ROW(1, 3, 'not the same');
SELECT ROW(a, b, c) IS NOT NULL FROM table;
</programlisting>
For more detail see <xref linkend="functions-comparisons">.
Row constructors can also be used in connection with subqueries,
as discussed in <xref linkend="functions-subquery">.
</para>
</sect2>
<sect2 id="syntax-express-eval"> <sect2 id="syntax-express-eval">
<title>Expression Evaluation Rules</title> <title>Expression Evaluation Rules</title>
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.81 2004/04/01 21:28:43 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.82 2004/05/10 22:44:43 tgl Exp $
--> -->
<sect1 id="xfunc"> <sect1 id="xfunc">
...@@ -240,10 +240,11 @@ SELECT clean_emp(); ...@@ -240,10 +240,11 @@ SELECT clean_emp();
<title><acronym>SQL</acronym> Functions on Composite Types</title> <title><acronym>SQL</acronym> Functions on Composite Types</title>
<para> <para>
When specifying functions with arguments of composite When writing functions with arguments of composite
types, we must not only specify which types, we must not only specify which
argument we want (as we did above with <literal>$1</> and <literal>$2</literal>) but argument we want (as we did above with <literal>$1</> and <literal>$2</literal>) but
also the attributes of that argument. For example, suppose that also the desired attribute (field) of that argument. For example,
suppose that
<type>emp</type> is a table containing employee data, and therefore <type>emp</type> is a table containing employee data, and therefore
also the name of the composite type of each row of the table. Here also the name of the composite type of each row of the table. Here
is a function <function>double_salary</function> that computes what someone's is a function <function>double_salary</function> that computes what someone's
...@@ -252,16 +253,16 @@ SELECT clean_emp(); ...@@ -252,16 +253,16 @@ SELECT clean_emp();
<screen> <screen>
CREATE TABLE emp ( CREATE TABLE emp (
name text, name text,
salary integer, salary numeric,
age integer, age integer,
cubicle point cubicle point
); );
CREATE FUNCTION double_salary(emp) RETURNS integer AS ' CREATE FUNCTION double_salary(emp) RETURNS numeric AS '
SELECT $1.salary * 2 AS salary; SELECT $1.salary * 2 AS salary;
' LANGUAGE SQL; ' LANGUAGE SQL;
SELECT name, double_salary(emp) AS dream SELECT name, double_salary(emp.*) AS dream
FROM emp FROM emp
WHERE emp.cubicle ~= point '(2,1)'; WHERE emp.cubicle ~= point '(2,1)';
...@@ -274,15 +275,27 @@ SELECT name, double_salary(emp) AS dream ...@@ -274,15 +275,27 @@ SELECT name, double_salary(emp) AS dream
<para> <para>
Notice the use of the syntax <literal>$1.salary</literal> Notice the use of the syntax <literal>$1.salary</literal>
to select one field of the argument row value. Also notice to select one field of the argument row value. Also notice
how the calling <command>SELECT</> command uses a table name to denote how the calling <command>SELECT</> command uses <literal>*</>
the entire current row of that table as a composite value. The table to select
row can alternatively be referenced like this: the entire current row of a table as a composite value. The table
row can alternatively be referenced using just the table name,
like this:
<screen> <screen>
SELECT name, double_salary(emp.*) AS dream SELECT name, double_salary(emp) AS dream
FROM emp FROM emp
WHERE emp.cubicle ~= point '(2,1)'; WHERE emp.cubicle ~= point '(2,1)';
</screen> </screen>
which emphasizes its row nature. but this usage is deprecated since it's easy to get confused.
</para>
<para>
Sometimes it is handy to construct a composite argument value
on-the-fly. This can be done with the <literal>ROW</> construct.
For example, we could adjust the data being passed to the function:
<screen>
SELECT name, double_salary(row(name, salary*1.1, age, cubicle)) AS dream
FROM emp;
</screen>
</para> </para>
<para> <para>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.158 2004/04/01 21:28:44 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.159 2004/05/10 22:44:43 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include "executor/execdebug.h" #include "executor/execdebug.h"
#include "executor/functions.h" #include "executor/functions.h"
#include "executor/nodeSubplan.h" #include "executor/nodeSubplan.h"
#include "funcapi.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "optimizer/planmain.h" #include "optimizer/planmain.h"
#include "parser/parse_expr.h" #include "parser/parse_expr.h"
...@@ -93,6 +94,9 @@ static Datum ExecEvalCaseTestExpr(ExprState *exprstate, ...@@ -93,6 +94,9 @@ static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
static Datum ExecEvalArray(ArrayExprState *astate, static Datum ExecEvalArray(ArrayExprState *astate,
ExprContext *econtext, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone); bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalRow(RowExprState *rstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr, static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
ExprContext *econtext, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone); bool *isNull, ExprDoneCond *isDone);
...@@ -2101,6 +2105,54 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext, ...@@ -2101,6 +2105,54 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
return PointerGetDatum(result); return PointerGetDatum(result);
} }
/* ----------------------------------------------------------------
* ExecEvalRow - ROW() expressions
* ----------------------------------------------------------------
*/
static Datum
ExecEvalRow(RowExprState *rstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
HeapTuple tuple;
Datum *values;
char *nulls;
int nargs;
List *arg;
int i;
/* Set default values for result flags: non-null, not a set result */
*isNull = false;
if (isDone)
*isDone = ExprSingleResult;
/* Allocate workspace */
nargs = length(rstate->args);
if (nargs == 0) /* avoid palloc(0) if no fields */
nargs = 1;
values = (Datum *) palloc(nargs * sizeof(Datum));
nulls = (char *) palloc(nargs * sizeof(char));
/* Evaluate field values */
i = 0;
foreach(arg, rstate->args)
{
ExprState *e = (ExprState *) lfirst(arg);
bool eisnull;
values[i] = ExecEvalExpr(e, econtext, &eisnull, NULL);
nulls[i] = eisnull ? 'n' : ' ';
i++;
}
tuple = heap_formtuple(rstate->tupdesc, values, nulls);
pfree(values);
pfree(nulls);
return HeapTupleGetDatum(tuple);
}
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecEvalCoalesce * ExecEvalCoalesce
* ---------------------------------------------------------------- * ----------------------------------------------------------------
...@@ -2822,6 +2874,39 @@ ExecInitExpr(Expr *node, PlanState *parent) ...@@ -2822,6 +2874,39 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) astate; state = (ExprState *) astate;
} }
break; break;
case T_RowExpr:
{
RowExpr *rowexpr = (RowExpr *) node;
RowExprState *rstate = makeNode(RowExprState);
List *outlist;
List *inlist;
rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRow;
outlist = NIL;
foreach(inlist, rowexpr->args)
{
Expr *e = (Expr *) lfirst(inlist);
ExprState *estate;
estate = ExecInitExpr(e, parent);
outlist = lappend(outlist, estate);
}
rstate->args = outlist;
/* Build tupdesc to describe result tuples */
if (rowexpr->row_typeid == RECORDOID)
{
/* generic record, use runtime type assignment */
rstate->tupdesc = ExecTypeFromExprList(rowexpr->args);
rstate->tupdesc = BlessTupleDesc(rstate->tupdesc);
}
else
{
/* it's been cast to a named type, use that */
rstate->tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
}
state = (ExprState *) rstate;
}
break;
case T_CoalesceExpr: case T_CoalesceExpr:
{ {
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.76 2004/04/01 21:28:44 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.77 2004/05/10 22:44:44 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -111,6 +111,7 @@ ...@@ -111,6 +111,7 @@
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "parser/parse_expr.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/typcache.h" #include "utils/typcache.h"
...@@ -118,6 +119,7 @@ ...@@ -118,6 +119,7 @@
static TupleDesc ExecTypeFromTLInternal(List *targetList, static TupleDesc ExecTypeFromTLInternal(List *targetList,
bool hasoid, bool skipjunk); bool hasoid, bool skipjunk);
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* tuple table create/delete functions * tuple table create/delete functions
* ---------------------------------------------------------------- * ----------------------------------------------------------------
...@@ -595,6 +597,38 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) ...@@ -595,6 +597,38 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk)
return typeInfo; return typeInfo;
} }
/*
* ExecTypeFromExprList - build a tuple descriptor from a list of Exprs
*
* Here we must make up an arbitrary set of field names.
*/
TupleDesc
ExecTypeFromExprList(List *exprList)
{
TupleDesc typeInfo;
List *l;
int cur_resno = 1;
char fldname[NAMEDATALEN];
typeInfo = CreateTemplateTupleDesc(length(exprList), false);
foreach(l, exprList)
{
Node *e = lfirst(l);
sprintf(fldname, "f%d", cur_resno);
TupleDescInitEntry(typeInfo,
cur_resno++,
fldname,
exprType(e),
exprTypmod(e),
0);
}
return typeInfo;
}
/* /*
* BlessTupleDesc - make a completed tuple descriptor useful for SRFs * BlessTupleDesc - make a completed tuple descriptor useful for SRFs
* *
......
...@@ -15,7 +15,7 @@ ...@@ -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.280 2004/05/05 04:48:45 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.281 2004/05/10 22:44:44 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -998,6 +998,21 @@ _copyArrayExpr(ArrayExpr *from) ...@@ -998,6 +998,21 @@ _copyArrayExpr(ArrayExpr *from)
return newnode; return newnode;
} }
/*
* _copyRowExpr
*/
static RowExpr *
_copyRowExpr(RowExpr *from)
{
RowExpr *newnode = makeNode(RowExpr);
COPY_NODE_FIELD(args);
COPY_SCALAR_FIELD(row_typeid);
COPY_SCALAR_FIELD(row_format);
return newnode;
}
/* /*
* _copyCoalesceExpr * _copyCoalesceExpr
*/ */
...@@ -2674,6 +2689,9 @@ copyObject(void *from) ...@@ -2674,6 +2689,9 @@ copyObject(void *from)
case T_ArrayExpr: case T_ArrayExpr:
retval = _copyArrayExpr(from); retval = _copyArrayExpr(from);
break; break;
case T_RowExpr:
retval = _copyRowExpr(from);
break;
case T_CoalesceExpr: case T_CoalesceExpr:
retval = _copyCoalesceExpr(from); retval = _copyCoalesceExpr(from);
break; break;
......
...@@ -18,7 +18,7 @@ ...@@ -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.219 2004/05/05 04:48:45 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.220 2004/05/10 22:44:44 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -423,6 +423,24 @@ _equalArrayExpr(ArrayExpr *a, ArrayExpr *b) ...@@ -423,6 +423,24 @@ _equalArrayExpr(ArrayExpr *a, ArrayExpr *b)
return true; return true;
} }
static bool
_equalRowExpr(RowExpr *a, RowExpr *b)
{
COMPARE_NODE_FIELD(args);
COMPARE_SCALAR_FIELD(row_typeid);
/*
* Special-case COERCE_DONTCARE, so that planner can build coercion
* nodes that are equal() to both explicit and implicit coercions.
*/
if (a->row_format != b->row_format &&
a->row_format != COERCE_DONTCARE &&
b->row_format != COERCE_DONTCARE)
return false;
return true;
}
static bool static bool
_equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b) _equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b)
{ {
...@@ -1748,6 +1766,9 @@ equal(void *a, void *b) ...@@ -1748,6 +1766,9 @@ equal(void *a, void *b)
case T_ArrayExpr: case T_ArrayExpr:
retval = _equalArrayExpr(a, b); retval = _equalArrayExpr(a, b);
break; break;
case T_RowExpr:
retval = _equalRowExpr(a, b);
break;
case T_CoalesceExpr: case T_CoalesceExpr:
retval = _equalCoalesceExpr(a, b); retval = _equalCoalesceExpr(a, b);
break; break;
......
...@@ -9,12 +9,13 @@ ...@@ -9,12 +9,13 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.42 2003/11/29 19:51:49 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.43 2004/05/10 22:44:44 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
...@@ -170,6 +171,17 @@ makeNullConst(Oid consttype) ...@@ -170,6 +171,17 @@ makeNullConst(Oid consttype)
typByVal); typByVal);
} }
/*
* makeBoolConst -
* creates a Const node representing a boolean value (can be NULL too)
*/
Node *
makeBoolConst(bool value, bool isnull)
{
/* note that pg_type.h hardwires size of bool as 1 ... duplicate it */
return (Node *) makeConst(BOOLOID, 1, BoolGetDatum(value), isnull, true);
}
/* /*
* makeBoolExpr - * makeBoolExpr -
* creates a BoolExpr node * creates a BoolExpr node
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.235 2004/05/08 21:21:18 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.236 2004/05/10 22:44:44 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*
...@@ -828,6 +828,16 @@ _outArrayExpr(StringInfo str, ArrayExpr *node) ...@@ -828,6 +828,16 @@ _outArrayExpr(StringInfo str, ArrayExpr *node)
WRITE_BOOL_FIELD(multidims); WRITE_BOOL_FIELD(multidims);
} }
static void
_outRowExpr(StringInfo str, RowExpr *node)
{
WRITE_NODE_TYPE("ROW");
WRITE_NODE_FIELD(args);
WRITE_OID_FIELD(row_typeid);
WRITE_ENUM_FIELD(row_format, CoercionForm);
}
static void static void
_outCoalesceExpr(StringInfo str, CoalesceExpr *node) _outCoalesceExpr(StringInfo str, CoalesceExpr *node)
{ {
...@@ -1719,6 +1729,9 @@ _outNode(StringInfo str, void *obj) ...@@ -1719,6 +1729,9 @@ _outNode(StringInfo str, void *obj)
case T_ArrayExpr: case T_ArrayExpr:
_outArrayExpr(str, obj); _outArrayExpr(str, obj);
break; break;
case T_RowExpr:
_outRowExpr(str, obj);
break;
case T_CoalesceExpr: case T_CoalesceExpr:
_outCoalesceExpr(str, obj); _outCoalesceExpr(str, obj);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.168 2004/05/08 21:21:18 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.169 2004/05/10 22:44:44 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
...@@ -628,6 +628,21 @@ _readArrayExpr(void) ...@@ -628,6 +628,21 @@ _readArrayExpr(void)
READ_DONE(); READ_DONE();
} }
/*
* _readRowExpr
*/
static RowExpr *
_readRowExpr(void)
{
READ_LOCALS(RowExpr);
READ_NODE_FIELD(args);
READ_OID_FIELD(row_typeid);
READ_ENUM_FIELD(row_format, CoercionForm);
READ_DONE();
}
/* /*
* _readCoalesceExpr * _readCoalesceExpr
*/ */
...@@ -978,6 +993,8 @@ parseNodeString(void) ...@@ -978,6 +993,8 @@ parseNodeString(void)
return_value = _readCaseTestExpr(); return_value = _readCaseTestExpr();
else if (MATCH("ARRAY", 5)) else if (MATCH("ARRAY", 5))
return_value = _readArrayExpr(); return_value = _readArrayExpr();
else if (MATCH("ROW", 3))
return_value = _readRowExpr();
else if (MATCH("COALESCE", 8)) else if (MATCH("COALESCE", 8))
return_value = _readCoalesceExpr(); return_value = _readCoalesceExpr();
else if (MATCH("NULLIFEXPR", 10)) else if (MATCH("NULLIFEXPR", 10))
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.113 2004/04/25 18:23:56 neilc Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.114 2004/05/10 22:44:44 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -57,9 +57,10 @@ static void compare_tlist_datatypes(List *tlist, List *colTypes, ...@@ -57,9 +57,10 @@ static void compare_tlist_datatypes(List *tlist, List *colTypes,
bool *differentTypes); bool *differentTypes);
static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual, static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
bool *differentTypes); bool *differentTypes);
static void subquery_push_qual(Query *subquery, Index rti, Node *qual); static void subquery_push_qual(Query *subquery,
RangeTblEntry *rte, Index rti, Node *qual);
static void recurse_push_qual(Node *setOp, Query *topquery, static void recurse_push_qual(Node *setOp, Query *topquery,
Index rti, Node *qual); RangeTblEntry *rte, Index rti, Node *qual);
/* /*
...@@ -375,7 +376,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel, ...@@ -375,7 +376,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
if (qual_is_pushdown_safe(subquery, rti, clause, differentTypes)) if (qual_is_pushdown_safe(subquery, rti, clause, differentTypes))
{ {
/* Push it down */ /* Push it down */
subquery_push_qual(subquery, rti, clause); subquery_push_qual(subquery, rte, rti, clause);
} }
else else
{ {
...@@ -778,12 +779,12 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual, ...@@ -778,12 +779,12 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
* subquery_push_qual - push down a qual that we have determined is safe * subquery_push_qual - push down a qual that we have determined is safe
*/ */
static void static void
subquery_push_qual(Query *subquery, Index rti, Node *qual) subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual)
{ {
if (subquery->setOperations != NULL) if (subquery->setOperations != NULL)
{ {
/* Recurse to push it separately to each component query */ /* Recurse to push it separately to each component query */
recurse_push_qual(subquery->setOperations, subquery, rti, qual); recurse_push_qual(subquery->setOperations, subquery, rte, rti, qual);
} }
else else
{ {
...@@ -797,7 +798,7 @@ subquery_push_qual(Query *subquery, Index rti, Node *qual) ...@@ -797,7 +798,7 @@ subquery_push_qual(Query *subquery, Index rti, Node *qual)
* This step also ensures that when we are pushing into a setop tree, * This step also ensures that when we are pushing into a setop tree,
* each component query gets its own copy of the qual. * each component query gets its own copy of the qual.
*/ */
qual = ResolveNew(qual, rti, 0, qual = ResolveNew(qual, rti, 0, rte,
subquery->targetList, subquery->targetList,
CMD_SELECT, 0); CMD_SELECT, 0);
subquery->havingQual = make_and_qual(subquery->havingQual, subquery->havingQual = make_and_qual(subquery->havingQual,
...@@ -816,23 +817,23 @@ subquery_push_qual(Query *subquery, Index rti, Node *qual) ...@@ -816,23 +817,23 @@ subquery_push_qual(Query *subquery, Index rti, Node *qual)
*/ */
static void static void
recurse_push_qual(Node *setOp, Query *topquery, recurse_push_qual(Node *setOp, Query *topquery,
Index rti, Node *qual) RangeTblEntry *rte, Index rti, Node *qual)
{ {
if (IsA(setOp, RangeTblRef)) if (IsA(setOp, RangeTblRef))
{ {
RangeTblRef *rtr = (RangeTblRef *) setOp; RangeTblRef *rtr = (RangeTblRef *) setOp;
RangeTblEntry *rte = rt_fetch(rtr->rtindex, topquery->rtable); RangeTblEntry *subrte = rt_fetch(rtr->rtindex, topquery->rtable);
Query *subquery = rte->subquery; Query *subquery = subrte->subquery;
Assert(subquery != NULL); Assert(subquery != NULL);
subquery_push_qual(subquery, rti, qual); subquery_push_qual(subquery, rte, rti, qual);
} }
else if (IsA(setOp, SetOperationStmt)) else if (IsA(setOp, SetOperationStmt))
{ {
SetOperationStmt *op = (SetOperationStmt *) setOp; SetOperationStmt *op = (SetOperationStmt *) setOp;
recurse_push_qual(op->larg, topquery, rti, qual); recurse_push_qual(op->larg, topquery, rte, rti, qual);
recurse_push_qual(op->rarg, topquery, rti, qual); recurse_push_qual(op->rarg, topquery, rte, rti, qual);
} }
else else
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.64 2004/01/05 16:44:40 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.65 2004/05/10 22:44:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -27,11 +27,6 @@ ...@@ -27,11 +27,6 @@
#include "utils/selfuncs.h" #include "utils/selfuncs.h"
/* note that pg_type.h hardwires size of bool as 1 ... duplicate it */
#define MAKEBOOLCONST(val,isnull) \
((Node *) makeConst(BOOLOID, 1, (Datum) (val), (isnull), true))
/* /*
* Data structure for accumulating info about possible range-query * Data structure for accumulating info about possible range-query
* clause pairs in clauselist_selectivity. * clause pairs in clauselist_selectivity.
...@@ -486,7 +481,7 @@ clause_selectivity(Query *root, ...@@ -486,7 +481,7 @@ clause_selectivity(Query *root,
s1 = restriction_selectivity(root, s1 = restriction_selectivity(root,
BooleanEqualOperator, BooleanEqualOperator,
makeList2(var, makeList2(var,
MAKEBOOLCONST(true, makeBoolConst(true,
false)), false)),
varRelid); varRelid);
} }
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.16 2004/01/10 18:13:53 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.17 2004/05/10 22:44:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -45,7 +45,8 @@ typedef struct reduce_outer_joins_state ...@@ -45,7 +45,8 @@ typedef struct reduce_outer_joins_state
static bool is_simple_subquery(Query *subquery); static bool is_simple_subquery(Query *subquery);
static bool has_nullable_targetlist(Query *subquery); static bool has_nullable_targetlist(Query *subquery);
static void resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist); static void resolvenew_in_jointree(Node *jtnode, int varno,
RangeTblEntry *rte, List *subtlist);
static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode); static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode);
static void reduce_outer_joins_pass2(Node *jtnode, static void reduce_outer_joins_pass2(Node *jtnode,
reduce_outer_joins_state *state, reduce_outer_joins_state *state,
...@@ -154,14 +155,10 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join) ...@@ -154,14 +155,10 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
* such expressions; we'd have to figure out how to get the pseudo- * such expressions; we'd have to figure out how to get the pseudo-
* variables evaluated at the right place in the modified plan * variables evaluated at the right place in the modified plan
* tree. Fix it someday. * tree. Fix it someday.
*
* Note: even if the subquery itself is simple enough, we can't pull
* it up if there is a reference to its whole tuple result.
* Perhaps a pseudo-variable is the answer here too.
*/ */
if (rte->rtekind == RTE_SUBQUERY && is_simple_subquery(subquery) && if (rte->rtekind == RTE_SUBQUERY &&
(!below_outer_join || has_nullable_targetlist(subquery)) && is_simple_subquery(subquery) &&
!contain_whole_tuple_var((Node *) parse, varno, 0)) (!below_outer_join || has_nullable_targetlist(subquery)))
{ {
int rtoffset; int rtoffset;
List *subtlist; List *subtlist;
...@@ -206,8 +203,7 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join) ...@@ -206,8 +203,7 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
* the one above. * the one above.
*/ */
if (is_simple_subquery(subquery) && if (is_simple_subquery(subquery) &&
(!below_outer_join || has_nullable_targetlist(subquery)) && (!below_outer_join || has_nullable_targetlist(subquery)))
!contain_whole_tuple_var((Node *) parse, varno, 0))
{ {
/* good to go */ /* good to go */
} }
...@@ -247,24 +243,25 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join) ...@@ -247,24 +243,25 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
subtlist = subquery->targetList; subtlist = subquery->targetList;
parse->targetList = (List *) parse->targetList = (List *)
ResolveNew((Node *) parse->targetList, ResolveNew((Node *) parse->targetList,
varno, 0, subtlist, CMD_SELECT, 0); varno, 0, rte, subtlist, CMD_SELECT, 0);
resolvenew_in_jointree((Node *) parse->jointree, varno, subtlist); resolvenew_in_jointree((Node *) parse->jointree, varno,
rte, subtlist);
Assert(parse->setOperations == NULL); Assert(parse->setOperations == NULL);
parse->havingQual = parse->havingQual =
ResolveNew(parse->havingQual, ResolveNew(parse->havingQual,
varno, 0, subtlist, CMD_SELECT, 0); varno, 0, rte, subtlist, CMD_SELECT, 0);
parse->in_info_list = (List *) parse->in_info_list = (List *)
ResolveNew((Node *) parse->in_info_list, ResolveNew((Node *) parse->in_info_list,
varno, 0, subtlist, CMD_SELECT, 0); varno, 0, rte, subtlist, CMD_SELECT, 0);
foreach(rt, parse->rtable) foreach(rt, parse->rtable)
{ {
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(rt);
if (rte->rtekind == RTE_JOIN) if (otherrte->rtekind == RTE_JOIN)
rte->joinaliasvars = (List *) otherrte->joinaliasvars = (List *)
ResolveNew((Node *) rte->joinaliasvars, ResolveNew((Node *) otherrte->joinaliasvars,
varno, 0, subtlist, CMD_SELECT, 0); varno, 0, rte, subtlist, CMD_SELECT, 0);
} }
/* /*
...@@ -479,7 +476,8 @@ has_nullable_targetlist(Query *subquery) ...@@ -479,7 +476,8 @@ has_nullable_targetlist(Query *subquery)
* but there's no other way... * but there's no other way...
*/ */
static void static void
resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist) resolvenew_in_jointree(Node *jtnode, int varno,
RangeTblEntry *rte, List *subtlist)
{ {
if (jtnode == NULL) if (jtnode == NULL)
return; return;
...@@ -493,18 +491,18 @@ resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist) ...@@ -493,18 +491,18 @@ resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist)
List *l; List *l;
foreach(l, f->fromlist) foreach(l, f->fromlist)
resolvenew_in_jointree(lfirst(l), varno, subtlist); resolvenew_in_jointree(lfirst(l), varno, rte, subtlist);
f->quals = ResolveNew(f->quals, f->quals = ResolveNew(f->quals,
varno, 0, subtlist, CMD_SELECT, 0); varno, 0, rte, subtlist, CMD_SELECT, 0);
} }
else if (IsA(jtnode, JoinExpr)) else if (IsA(jtnode, JoinExpr))
{ {
JoinExpr *j = (JoinExpr *) jtnode; JoinExpr *j = (JoinExpr *) jtnode;
resolvenew_in_jointree(j->larg, varno, subtlist); resolvenew_in_jointree(j->larg, varno, rte, subtlist);
resolvenew_in_jointree(j->rarg, varno, subtlist); resolvenew_in_jointree(j->rarg, varno, rte, subtlist);
j->quals = ResolveNew(j->quals, j->quals = ResolveNew(j->quals,
varno, 0, subtlist, CMD_SELECT, 0); varno, 0, rte, subtlist, CMD_SELECT, 0);
/* /*
* We don't bother to update the colvars list, since it won't be * We don't bother to update the colvars list, since it won't be
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.169 2004/04/02 23:14:08 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.170 2004/05/10 22:44:45 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -41,10 +41,6 @@ ...@@ -41,10 +41,6 @@
#include "utils/syscache.h" #include "utils/syscache.h"
/* note that pg_type.h hardwires size of bool as 1 ... duplicate it */
#define MAKEBOOLCONST(val,isnull) \
((Node *) makeConst(BOOLOID, 1, (Datum) (val), (isnull), true))
typedef struct typedef struct
{ {
int nargs; int nargs;
...@@ -281,7 +277,7 @@ Expr * ...@@ -281,7 +277,7 @@ Expr *
make_ands_explicit(List *andclauses) make_ands_explicit(List *andclauses)
{ {
if (andclauses == NIL) if (andclauses == NIL)
return (Expr *) MAKEBOOLCONST(true, false); return (Expr *) makeBoolConst(true, false);
else if (lnext(andclauses) == NIL) else if (lnext(andclauses) == NIL)
return (Expr *) lfirst(andclauses); return (Expr *) lfirst(andclauses);
else else
...@@ -484,6 +480,8 @@ expression_returns_set_walker(Node *node, void *context) ...@@ -484,6 +480,8 @@ expression_returns_set_walker(Node *node, void *context)
return false; return false;
if (IsA(node, ArrayExpr)) if (IsA(node, ArrayExpr))
return false; return false;
if (IsA(node, RowExpr))
return false;
if (IsA(node, CoalesceExpr)) if (IsA(node, CoalesceExpr))
return false; return false;
if (IsA(node, NullIfExpr)) if (IsA(node, NullIfExpr))
...@@ -778,6 +776,8 @@ contain_nonstrict_functions_walker(Node *node, void *context) ...@@ -778,6 +776,8 @@ contain_nonstrict_functions_walker(Node *node, void *context)
if (IsA(node, CaseWhen)) if (IsA(node, CaseWhen))
return true; return true;
/* NB: ArrayExpr might someday be nonstrict */ /* NB: ArrayExpr might someday be nonstrict */
if (IsA(node, RowExpr))
return true;
if (IsA(node, CoalesceExpr)) if (IsA(node, CoalesceExpr))
return true; return true;
if (IsA(node, NullIfExpr)) if (IsA(node, NullIfExpr))
...@@ -1030,6 +1030,8 @@ set_coercionform_dontcare_walker(Node *node, void *context) ...@@ -1030,6 +1030,8 @@ set_coercionform_dontcare_walker(Node *node, void *context)
((FuncExpr *) node)->funcformat = COERCE_DONTCARE; ((FuncExpr *) node)->funcformat = COERCE_DONTCARE;
if (IsA(node, RelabelType)) if (IsA(node, RelabelType))
((RelabelType *) node)->relabelformat = COERCE_DONTCARE; ((RelabelType *) node)->relabelformat = COERCE_DONTCARE;
if (IsA(node, RowExpr))
((RowExpr *) node)->row_format = COERCE_DONTCARE;
if (IsA(node, CoerceToDomain)) if (IsA(node, CoerceToDomain))
((CoerceToDomain *) node)->coercionformat = COERCE_DONTCARE; ((CoerceToDomain *) node)->coercionformat = COERCE_DONTCARE;
return expression_tree_walker(node, set_coercionform_dontcare_walker, return expression_tree_walker(node, set_coercionform_dontcare_walker,
...@@ -1197,11 +1199,11 @@ eval_const_expressions_mutator(Node *node, List *active_fns) ...@@ -1197,11 +1199,11 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
{ {
/* all nulls? then not distinct */ /* all nulls? then not distinct */
if (all_null_input) if (all_null_input)
return MAKEBOOLCONST(false, false); return makeBoolConst(false, false);
/* one null? then distinct */ /* one null? then distinct */
if (has_null_input) if (has_null_input)
return MAKEBOOLCONST(true, false); return makeBoolConst(true, false);
/* otherwise try to evaluate the '=' operator */ /* otherwise try to evaluate the '=' operator */
/* (NOT okay to try to inline it, though!) */ /* (NOT okay to try to inline it, though!) */
...@@ -1272,12 +1274,12 @@ eval_const_expressions_mutator(Node *node, List *active_fns) ...@@ -1272,12 +1274,12 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
newargs = simplify_or_arguments(args, newargs = simplify_or_arguments(args,
&haveNull, &forceTrue); &haveNull, &forceTrue);
if (forceTrue) if (forceTrue)
return MAKEBOOLCONST(true, false); return makeBoolConst(true, false);
if (haveNull) if (haveNull)
newargs = lappend(newargs, MAKEBOOLCONST(false, true)); newargs = lappend(newargs, makeBoolConst(false, true));
/* If all the inputs are FALSE, result is FALSE */ /* If all the inputs are FALSE, result is FALSE */
if (newargs == NIL) if (newargs == NIL)
return MAKEBOOLCONST(false, false); return makeBoolConst(false, false);
/* If only one nonconst-or-NULL input, it's the result */ /* If only one nonconst-or-NULL input, it's the result */
if (lnext(newargs) == NIL) if (lnext(newargs) == NIL)
return (Node *) lfirst(newargs); return (Node *) lfirst(newargs);
...@@ -1293,12 +1295,12 @@ eval_const_expressions_mutator(Node *node, List *active_fns) ...@@ -1293,12 +1295,12 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
newargs = simplify_and_arguments(args, newargs = simplify_and_arguments(args,
&haveNull, &forceFalse); &haveNull, &forceFalse);
if (forceFalse) if (forceFalse)
return MAKEBOOLCONST(false, false); return makeBoolConst(false, false);
if (haveNull) if (haveNull)
newargs = lappend(newargs, MAKEBOOLCONST(false, true)); newargs = lappend(newargs, makeBoolConst(false, true));
/* If all the inputs are TRUE, result is TRUE */ /* If all the inputs are TRUE, result is TRUE */
if (newargs == NIL) if (newargs == NIL)
return MAKEBOOLCONST(true, false); return makeBoolConst(true, false);
/* If only one nonconst-or-NULL input, it's the result */ /* If only one nonconst-or-NULL input, it's the result */
if (lnext(newargs) == NIL) if (lnext(newargs) == NIL)
return (Node *) lfirst(newargs); return (Node *) lfirst(newargs);
...@@ -1313,9 +1315,9 @@ eval_const_expressions_mutator(Node *node, List *active_fns) ...@@ -1313,9 +1315,9 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
/* NOT NULL => NULL */ /* NOT NULL => NULL */
if (const_input->constisnull) if (const_input->constisnull)
return MAKEBOOLCONST(false, true); return makeBoolConst(false, true);
/* otherwise pretty easy */ /* otherwise pretty easy */
return MAKEBOOLCONST(!DatumGetBool(const_input->constvalue), return makeBoolConst(!DatumGetBool(const_input->constvalue),
false); false);
} }
else if (not_clause((Node *) lfirst(args))) else if (not_clause((Node *) lfirst(args)))
...@@ -1387,7 +1389,6 @@ eval_const_expressions_mutator(Node *node, List *active_fns) ...@@ -1387,7 +1389,6 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
} }
if (IsA(node, CaseExpr)) if (IsA(node, CaseExpr))
{ {
/*---------- /*----------
* CASE expressions can be simplified if there are constant * CASE expressions can be simplified if there are constant
* condition clauses: * condition clauses:
...@@ -1546,22 +1547,38 @@ eval_const_expressions_mutator(Node *node, List *active_fns) ...@@ -1546,22 +1547,38 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
* We can optimize field selection from a whole-row Var into a * We can optimize field selection from a whole-row Var into a
* simple Var. (This case won't be generated directly by the * simple Var. (This case won't be generated directly by the
* parser, because ParseComplexProjection short-circuits it. But * parser, because ParseComplexProjection short-circuits it. But
* it can arise while simplifying functions.) If the argument * it can arise while simplifying functions.) Also, we can
* isn't a whole-row Var, just fall through to do generic * optimize field selection from a RowExpr construct.
* processing.
*/ */
FieldSelect *fselect = (FieldSelect *) node; FieldSelect *fselect = (FieldSelect *) node;
Var *argvar = (Var *) fselect->arg; FieldSelect *newfselect;
Node *arg;
if (argvar && IsA(argvar, Var) && arg = eval_const_expressions_mutator((Node *) fselect->arg,
argvar->varattno == InvalidAttrNumber) active_fns);
if (arg && IsA(arg, Var) &&
((Var *) arg)->varattno == InvalidAttrNumber)
{ {
return (Node *) makeVar(argvar->varno, return (Node *) makeVar(((Var *) arg)->varno,
fselect->fieldnum, fselect->fieldnum,
fselect->resulttype, fselect->resulttype,
fselect->resulttypmod, fselect->resulttypmod,
argvar->varlevelsup); ((Var *) arg)->varlevelsup);
} }
if (arg && IsA(arg, RowExpr))
{
RowExpr *rowexpr = (RowExpr *) arg;
if (fselect->fieldnum > 0 &&
fselect->fieldnum <= length(rowexpr->args))
return (Node *) nth(fselect->fieldnum - 1, rowexpr->args);
}
newfselect = makeNode(FieldSelect);
newfselect->arg = (Expr *) arg;
newfselect->fieldnum = fselect->fieldnum;
newfselect->resulttype = fselect->resulttype;
newfselect->resulttypmod = fselect->resulttypmod;
return (Node *) newfselect;
} }
/* /*
...@@ -1759,7 +1776,6 @@ evaluate_function(Oid funcid, Oid result_type, List *args, ...@@ -1759,7 +1776,6 @@ evaluate_function(Oid funcid, Oid result_type, List *args,
bool has_null_input = false; bool has_null_input = false;
List *arg; List *arg;
FuncExpr *newexpr; FuncExpr *newexpr;
char result_typtype;
/* /*
* Can't simplify if it returns a set. * Can't simplify if it returns a set.
...@@ -1796,15 +1812,6 @@ evaluate_function(Oid funcid, Oid result_type, List *args, ...@@ -1796,15 +1812,6 @@ evaluate_function(Oid funcid, Oid result_type, List *args,
has_nonconst_input) has_nonconst_input)
return NULL; return NULL;
/*
* Can't simplify functions returning composite types (mainly because
* datumCopy() doesn't cope; FIXME someday when we have a saner
* representation for whole-tuple results).
*/
result_typtype = get_typtype(funcform->prorettype);
if (result_typtype == 'c')
return NULL;
/* /*
* OK, looks like we can simplify this operator/function. * OK, looks like we can simplify this operator/function.
* *
...@@ -1850,7 +1857,6 @@ inline_function(Oid funcid, Oid result_type, List *args, ...@@ -1850,7 +1857,6 @@ inline_function(Oid funcid, Oid result_type, List *args,
HeapTuple func_tuple, List *active_fns) HeapTuple func_tuple, List *active_fns)
{ {
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
char result_typtype;
bool polymorphic = false; bool polymorphic = false;
Oid argtypes[FUNC_MAX_ARGS]; Oid argtypes[FUNC_MAX_ARGS];
char *src; char *src;
...@@ -1877,21 +1883,6 @@ inline_function(Oid funcid, Oid result_type, List *args, ...@@ -1877,21 +1883,6 @@ inline_function(Oid funcid, Oid result_type, List *args,
funcform->pronargs != length(args)) funcform->pronargs != length(args))
return NULL; return NULL;
/*
* Forget it if declared return type is not base, domain, or
* polymorphic
*/
result_typtype = get_typtype(funcform->prorettype);
if (result_typtype != 'b' &&
result_typtype != 'd')
{
if (funcform->prorettype == ANYARRAYOID ||
funcform->prorettype == ANYELEMENTOID)
polymorphic = true;
else
return NULL;
}
/* Check for recursive function, and give up trying to expand if so */ /* Check for recursive function, and give up trying to expand if so */
if (oidMember(funcid, active_fns)) if (oidMember(funcid, active_fns))
return NULL; return NULL;
...@@ -1912,6 +1903,10 @@ inline_function(Oid funcid, Oid result_type, List *args, ...@@ -1912,6 +1903,10 @@ inline_function(Oid funcid, Oid result_type, List *args,
} }
} }
if (funcform->prorettype == ANYARRAYOID ||
funcform->prorettype == ANYELEMENTOID)
polymorphic = true;
/* /*
* Setup error traceback support for ereport(). This is so that we * Setup error traceback support for ereport(). This is so that we
* can finger the function that bad information came from. * can finger the function that bad information came from.
...@@ -2483,6 +2478,8 @@ expression_tree_walker(Node *node, ...@@ -2483,6 +2478,8 @@ expression_tree_walker(Node *node,
break; break;
case T_ArrayExpr: case T_ArrayExpr:
return walker(((ArrayExpr *) node)->elements, context); return walker(((ArrayExpr *) node)->elements, context);
case T_RowExpr:
return walker(((RowExpr *) node)->args, context);
case T_CoalesceExpr: case T_CoalesceExpr:
return walker(((CoalesceExpr *) node)->args, context); return walker(((CoalesceExpr *) node)->args, context);
case T_NullIfExpr: case T_NullIfExpr:
...@@ -2889,6 +2886,16 @@ expression_tree_mutator(Node *node, ...@@ -2889,6 +2886,16 @@ expression_tree_mutator(Node *node,
return (Node *) newnode; return (Node *) newnode;
} }
break; break;
case T_RowExpr:
{
RowExpr *rowexpr = (RowExpr *) node;
RowExpr *newnode;
FLATCOPY(newnode, rowexpr, RowExpr);
MUTATE(newnode->args, rowexpr->args, List *);
return (Node *) newnode;
}
break;
case T_CoalesceExpr: case T_CoalesceExpr:
{ {
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.55 2003/11/29 19:51:51 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.56 2004/05/10 22:44:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -191,19 +191,6 @@ contain_var_reference_walker(Node *node, ...@@ -191,19 +191,6 @@ contain_var_reference_walker(Node *node,
} }
/*
* contain_whole_tuple_var
*
* Detect whether a parsetree contains any references to the whole
* tuple of a given rtable entry (ie, a Var with varattno = 0).
*/
bool
contain_whole_tuple_var(Node *node, int varno, int levelsup)
{
return contain_var_reference(node, varno, InvalidAttrNumber, levelsup);
}
/* /*
* contain_var_clause * contain_var_clause
* Recursively scan a clause to discover whether it contains any Var nodes * Recursively scan a clause to discover whether it contains any Var nodes
...@@ -486,7 +473,10 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context) ...@@ -486,7 +473,10 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
* flatten_join_alias_vars * flatten_join_alias_vars
* Replace Vars that reference JOIN outputs with references to the original * Replace Vars that reference JOIN outputs with references to the original
* relation variables instead. This allows quals involving such vars to be * relation variables instead. This allows quals involving such vars to be
* pushed down. * pushed down. Whole-row Vars that reference JOIN relations are expanded
* into RowExpr constructs that name the individual output Vars. This
* is necessary since we will not scan the JOIN as a base relation, which
* is the only way that the executor can directly handle whole-row Vars.
* *
* NOTE: this is used on not-yet-planned expressions. We do not expect it * NOTE: this is used on not-yet-planned expressions. We do not expect it
* to be applied directly to a Query node. * to be applied directly to a Query node.
...@@ -520,8 +510,39 @@ flatten_join_alias_vars_mutator(Node *node, ...@@ -520,8 +510,39 @@ flatten_join_alias_vars_mutator(Node *node,
rte = rt_fetch(var->varno, context->root->rtable); rte = rt_fetch(var->varno, context->root->rtable);
if (rte->rtekind != RTE_JOIN) if (rte->rtekind != RTE_JOIN)
return node; return node;
if (var->varattno == InvalidAttrNumber)
{
/* Must expand whole-row reference */
RowExpr *rowexpr;
List *fields = NIL;
List *l;
foreach(l, rte->joinaliasvars)
{
newvar = (Node *) lfirst(l);
/*
* If we are expanding an alias carried down from an upper
* query, must adjust its varlevelsup fields.
*/
if (context->sublevels_up != 0)
{
newvar = copyObject(newvar);
IncrementVarSublevelsUp(newvar, context->sublevels_up, 0);
}
/* Recurse in case join input is itself a join */
newvar = flatten_join_alias_vars_mutator(newvar, context);
fields = lappend(fields, newvar);
}
rowexpr = makeNode(RowExpr);
rowexpr->args = fields;
rowexpr->row_typeid = var->vartype;
rowexpr->row_format = COERCE_IMPLICIT_CAST;
return (Node *) rowexpr;
}
/* Expand join alias reference */
Assert(var->varattno > 0); Assert(var->varattno > 0);
/* Okay, must expand it */
newvar = (Node *) nth(var->varattno - 1, rte->joinaliasvars); newvar = (Node *) nth(var->varattno - 1, rte->joinaliasvars);
/* /*
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.453 2004/05/05 04:48:46 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.454 2004/05/10 22:44:45 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -80,11 +80,9 @@ static Node *makeStringConst(char *str, TypeName *typename); ...@@ -80,11 +80,9 @@ static Node *makeStringConst(char *str, TypeName *typename);
static Node *makeIntConst(int val); static Node *makeIntConst(int val);
static Node *makeFloatConst(char *str); static Node *makeFloatConst(char *str);
static Node *makeAConst(Value *v); static Node *makeAConst(Value *v);
static Node *makeRowExpr(List *opr, List *largs, List *rargs); static Node *makeRowNullTest(NullTestType test, RowExpr *row);
static Node *makeDistinctExpr(List *largs, List *rargs);
static Node *makeRowNullTest(NullTestType test, List *args);
static DefElem *makeDefElem(char *name, Node *arg); static DefElem *makeDefElem(char *name, Node *arg);
static A_Const *makeBoolConst(bool state); static A_Const *makeBoolAConst(bool state);
static FuncCall *makeOverlaps(List *largs, List *rargs); static FuncCall *makeOverlaps(List *largs, List *rargs);
static List *extractArgTypes(List *parameters); static List *extractArgTypes(List *parameters);
static SelectStmt *findLeftmostSelect(SelectStmt *node); static SelectStmt *findLeftmostSelect(SelectStmt *node);
...@@ -277,9 +275,9 @@ static void doNegateFloat(Value *v); ...@@ -277,9 +275,9 @@ static void doNegateFloat(Value *v);
%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 insert_column_item
a_expr b_expr c_expr r_expr AexprConst a_expr b_expr c_expr AexprConst
in_expr having_clause func_table array_expr in_expr having_clause func_table array_expr
%type <list> row row_descriptor 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
%type <list> when_clause_list %type <list> when_clause_list
%type <ival> sub_type %type <ival> sub_type
...@@ -5710,163 +5708,6 @@ opt_interval: ...@@ -5710,163 +5708,6 @@ opt_interval:
* *
*****************************************************************************/ *****************************************************************************/
/* Expressions using row descriptors
* Define row_descriptor to allow yacc to break the reduce/reduce conflict
* with singleton expressions. Use SQL99's ROW keyword to allow rows of
* one element.
*/
r_expr: row IN_P select_with_parens
{
SubLink *n = makeNode(SubLink);
n->subLinkType = ANY_SUBLINK;
n->lefthand = $1;
n->operName = makeList1(makeString("="));
n->subselect = $3;
$$ = (Node *)n;
}
| row NOT IN_P select_with_parens
{
/* Make an IN node */
SubLink *n = makeNode(SubLink);
n->subLinkType = ANY_SUBLINK;
n->lefthand = $1;
n->operName = makeList1(makeString("="));
n->subselect = $4;
/* Stick a NOT on top */
$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, (Node *) n);
}
| row subquery_Op sub_type select_with_parens
%prec Op
{
SubLink *n = makeNode(SubLink);
n->subLinkType = $3;
n->lefthand = $1;
n->operName = $2;
n->subselect = $4;
$$ = (Node *)n;
}
| row subquery_Op select_with_parens
%prec Op
{
SubLink *n = makeNode(SubLink);
n->subLinkType = MULTIEXPR_SUBLINK;
n->lefthand = $1;
n->operName = $2;
n->subselect = $3;
$$ = (Node *)n;
}
| row subquery_Op row
%prec Op
{
$$ = makeRowExpr($2, $1, $3);
}
| row IS NULL_P
{
$$ = makeRowNullTest(IS_NULL, $1);
}
| row IS NOT NULL_P
{
$$ = makeRowNullTest(IS_NOT_NULL, $1);
}
| row OVERLAPS row
{
$$ = (Node *)makeOverlaps($1, $3);
}
| row IS DISTINCT FROM row
%prec IS
{
/* IS DISTINCT FROM has the following rules for non-array types:
* a) the row lengths must be equal
* b) if both rows are zero-length, then they are not distinct
* c) if any element is distinct, the rows are distinct
* The rules for an element being distinct:
* a) if the elements are both NULL, then they are not distinct
* b) if the elements compare to be equal, then they are not distinct
* c) otherwise, they are distinct
*/
List *largs = $1;
List *rargs = $5;
/* lengths don't match? then complain */
if (length(largs) != length(rargs))
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unequal number of entries in row expression")));
}
/* both are zero-length rows? then they are not distinct */
else if (length(largs) <= 0)
{
$$ = (Node *)makeBoolConst(FALSE);
}
/* otherwise, we need to compare each element */
else
{
$$ = (Node *)makeDistinctExpr(largs, rargs);
}
}
;
/* Explicit row production.
* SQL99 allows an optional ROW keyword, so we can now do single-element productions
* without conflicting with the parenthesized a_expr production.
*/
row: ROW '(' row_descriptor ')' { $$ = $3; }
| ROW '(' a_expr ')' { $$ = makeList1($3); }
| ROW '(' ')' { $$ = NULL; }
| '(' row_descriptor ')' { $$ = $2; }
;
row_descriptor: expr_list ',' a_expr { $$ = lappend($1, $3); }
;
sub_type: ANY { $$ = ANY_SUBLINK; }
| SOME { $$ = ANY_SUBLINK; }
| ALL { $$ = ALL_SUBLINK; }
;
all_Op: Op { $$ = $1; }
| MathOp { $$ = $1; }
;
MathOp: '+' { $$ = "+"; }
| '-' { $$ = "-"; }
| '*' { $$ = "*"; }
| '/' { $$ = "/"; }
| '%' { $$ = "%"; }
| '^' { $$ = "^"; }
| '<' { $$ = "<"; }
| '>' { $$ = ">"; }
| '=' { $$ = "="; }
;
qual_Op: Op
{ $$ = makeList1(makeString($1)); }
| OPERATOR '(' any_operator ')' { $$ = $3; }
;
qual_all_Op:
all_Op
{ $$ = makeList1(makeString($1)); }
| OPERATOR '(' any_operator ')' { $$ = $3; }
;
subquery_Op:
all_Op { $$ = makeList1(makeString($1)); }
| OPERATOR '(' any_operator ')' { $$ = $3; }
| LIKE { $$ = makeList1(makeString("~~")); }
| NOT LIKE { $$ = makeList1(makeString("!~~")); }
| ILIKE { $$ = makeList1(makeString("~~*")); }
| NOT ILIKE { $$ = makeList1(makeString("!~~*")); }
/* cannot put SIMILAR TO here, because SIMILAR TO is a hack.
* the regular expression is preprocessed by a function (similar_escape),
* and the ~ operator for posix regular expressions is used.
* x SIMILAR TO y -> x ~ similar_escape(y)
* this transformation is made on the fly by the parser upwards.
* however the SubLink structure which handles any/some/all stuff
* is not ready for such a thing.
*/
;
/* /*
* General expressions * General expressions
* This is the heart of the expression syntax. * This is the heart of the expression syntax.
...@@ -6046,31 +5887,55 @@ a_expr: c_expr { $$ = $1; } ...@@ -6046,31 +5887,55 @@ a_expr: c_expr { $$ = $1; }
*/ */
| a_expr ISNULL | a_expr ISNULL
{ {
NullTest *n = makeNode(NullTest); if (IsA($1, RowExpr))
n->arg = (Expr *) $1; $$ = makeRowNullTest(IS_NULL, (RowExpr *) $1);
n->nulltesttype = IS_NULL; else
$$ = (Node *)n; {
NullTest *n = makeNode(NullTest);
n->arg = (Expr *) $1;
n->nulltesttype = IS_NULL;
$$ = (Node *)n;
}
} }
| a_expr IS NULL_P | a_expr IS NULL_P
{ {
NullTest *n = makeNode(NullTest); if (IsA($1, RowExpr))
n->arg = (Expr *) $1; $$ = makeRowNullTest(IS_NULL, (RowExpr *) $1);
n->nulltesttype = IS_NULL; else
$$ = (Node *)n; {
NullTest *n = makeNode(NullTest);
n->arg = (Expr *) $1;
n->nulltesttype = IS_NULL;
$$ = (Node *)n;
}
} }
| a_expr NOTNULL | a_expr NOTNULL
{ {
NullTest *n = makeNode(NullTest); if (IsA($1, RowExpr))
n->arg = (Expr *) $1; $$ = makeRowNullTest(IS_NOT_NULL, (RowExpr *) $1);
n->nulltesttype = IS_NOT_NULL; else
$$ = (Node *)n; {
NullTest *n = makeNode(NullTest);
n->arg = (Expr *) $1;
n->nulltesttype = IS_NOT_NULL;
$$ = (Node *)n;
}
} }
| a_expr IS NOT NULL_P | a_expr IS NOT NULL_P
{ {
NullTest *n = makeNode(NullTest); if (IsA($1, RowExpr))
n->arg = (Expr *) $1; $$ = makeRowNullTest(IS_NOT_NULL, (RowExpr *) $1);
n->nulltesttype = IS_NOT_NULL; else
$$ = (Node *)n; {
NullTest *n = makeNode(NullTest);
n->arg = (Expr *) $1;
n->nulltesttype = IS_NOT_NULL;
$$ = (Node *)n;
}
}
| row OVERLAPS row
{
$$ = (Node *)makeOverlaps($1, $3);
} }
| a_expr IS TRUE_P | a_expr IS TRUE_P
{ {
...@@ -6115,7 +5980,9 @@ a_expr: c_expr { $$ = $1; } ...@@ -6115,7 +5980,9 @@ a_expr: c_expr { $$ = $1; }
$$ = (Node *)b; $$ = (Node *)b;
} }
| a_expr IS DISTINCT FROM a_expr %prec IS | a_expr IS DISTINCT FROM a_expr %prec IS
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5); } {
$$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5);
}
| a_expr IS OF '(' type_list ')' %prec IS | a_expr IS OF '(' type_list ')' %prec IS
{ {
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5); $$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5);
...@@ -6143,7 +6010,10 @@ a_expr: c_expr { $$ = $1; } ...@@ -6143,7 +6010,10 @@ a_expr: c_expr { $$ = $1; }
{ {
SubLink *n = (SubLink *)$3; SubLink *n = (SubLink *)$3;
n->subLinkType = ANY_SUBLINK; n->subLinkType = ANY_SUBLINK;
n->lefthand = makeList1($1); if (IsA($1, RowExpr))
n->lefthand = ((RowExpr *) $1)->args;
else
n->lefthand = makeList1($1);
n->operName = makeList1(makeString("=")); n->operName = makeList1(makeString("="));
$$ = (Node *)n; $$ = (Node *)n;
} }
...@@ -6171,7 +6041,10 @@ a_expr: c_expr { $$ = $1; } ...@@ -6171,7 +6041,10 @@ a_expr: c_expr { $$ = $1; }
/* Make an IN node */ /* Make an IN node */
SubLink *n = (SubLink *)$4; SubLink *n = (SubLink *)$4;
n->subLinkType = ANY_SUBLINK; n->subLinkType = ANY_SUBLINK;
n->lefthand = makeList1($1); if (IsA($1, RowExpr))
n->lefthand = ((RowExpr *) $1)->args;
else
n->lefthand = makeList1($1);
n->operName = makeList1(makeString("=")); n->operName = makeList1(makeString("="));
/* Stick a NOT on top */ /* Stick a NOT on top */
$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, (Node *) n); $$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, (Node *) n);
...@@ -6196,7 +6069,10 @@ a_expr: c_expr { $$ = $1; } ...@@ -6196,7 +6069,10 @@ a_expr: c_expr { $$ = $1; }
{ {
SubLink *n = makeNode(SubLink); SubLink *n = makeNode(SubLink);
n->subLinkType = $3; n->subLinkType = $3;
n->lefthand = makeList1($1); if (IsA($1, RowExpr))
n->lefthand = ((RowExpr *) $1)->args;
else
n->lefthand = makeList1($1);
n->operName = $2; n->operName = $2;
n->subselect = $4; n->subselect = $4;
$$ = (Node *)n; $$ = (Node *)n;
...@@ -6223,8 +6099,6 @@ a_expr: c_expr { $$ = $1; } ...@@ -6223,8 +6099,6 @@ a_expr: c_expr { $$ = $1; }
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("UNIQUE predicate is not yet implemented"))); errmsg("UNIQUE predicate is not yet implemented")));
} }
| r_expr
{ $$ = $1; }
; ;
/* /*
...@@ -6277,7 +6151,9 @@ b_expr: c_expr ...@@ -6277,7 +6151,9 @@ b_expr: c_expr
| b_expr qual_Op %prec POSTFIXOP | b_expr qual_Op %prec POSTFIXOP
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL); } { $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL); }
| b_expr IS DISTINCT FROM b_expr %prec IS | b_expr IS DISTINCT FROM b_expr %prec IS
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5); } {
$$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5);
}
| b_expr IS OF '(' type_list ')' %prec IS | b_expr IS OF '(' type_list ')' %prec IS
{ {
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5); $$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5);
...@@ -6819,12 +6695,86 @@ c_expr: columnref { $$ = (Node *) $1; } ...@@ -6819,12 +6695,86 @@ c_expr: columnref { $$ = (Node *) $1; }
} }
| ARRAY array_expr | ARRAY array_expr
{ $$ = $2; } { $$ = $2; }
| row
{
RowExpr *r = makeNode(RowExpr);
r->args = $1;
r->row_typeid = InvalidOid; /* not analyzed yet */
$$ = (Node *)r;
}
; ;
/* /*
* Supporting nonterminals for expressions. * Supporting nonterminals for expressions.
*/ */
/* Explicit row production.
*
* SQL99 allows an optional ROW keyword, so we can now do single-element rows
* without conflicting with the parenthesized a_expr production. Without the
* ROW keyword, there must be more than one a_expr inside the parens.
*/
row: ROW '(' expr_list ')' { $$ = $3; }
| ROW '(' ')' { $$ = NIL; }
| '(' expr_list ',' a_expr ')' { $$ = lappend($2, $4); }
;
sub_type: ANY { $$ = ANY_SUBLINK; }
| SOME { $$ = ANY_SUBLINK; }
| ALL { $$ = ALL_SUBLINK; }
;
all_Op: Op { $$ = $1; }
| MathOp { $$ = $1; }
;
MathOp: '+' { $$ = "+"; }
| '-' { $$ = "-"; }
| '*' { $$ = "*"; }
| '/' { $$ = "/"; }
| '%' { $$ = "%"; }
| '^' { $$ = "^"; }
| '<' { $$ = "<"; }
| '>' { $$ = ">"; }
| '=' { $$ = "="; }
;
qual_Op: Op
{ $$ = makeList1(makeString($1)); }
| OPERATOR '(' any_operator ')'
{ $$ = $3; }
;
qual_all_Op:
all_Op
{ $$ = makeList1(makeString($1)); }
| OPERATOR '(' any_operator ')'
{ $$ = $3; }
;
subquery_Op:
all_Op
{ $$ = makeList1(makeString($1)); }
| OPERATOR '(' any_operator ')'
{ $$ = $3; }
| LIKE
{ $$ = makeList1(makeString("~~")); }
| NOT LIKE
{ $$ = makeList1(makeString("!~~")); }
| ILIKE
{ $$ = makeList1(makeString("~~*")); }
| NOT ILIKE
{ $$ = makeList1(makeString("!~~*")); }
/* cannot put SIMILAR TO here, because SIMILAR TO is a hack.
* the regular expression is preprocessed by a function (similar_escape),
* and the ~ operator for posix regular expressions is used.
* x SIMILAR TO y -> x ~ similar_escape(y)
* this transformation is made on the fly by the parser upwards.
* however the SubLink structure which handles any/some/all stuff
* is not ready for such a thing.
*/
;
opt_indirection: opt_indirection:
opt_indirection '[' a_expr ']' opt_indirection '[' a_expr ']'
{ {
...@@ -7358,11 +7308,11 @@ AexprConst: Iconst ...@@ -7358,11 +7308,11 @@ AexprConst: Iconst
} }
| TRUE_P | TRUE_P
{ {
$$ = (Node *)makeBoolConst(TRUE); $$ = (Node *)makeBoolAConst(TRUE);
} }
| FALSE_P | FALSE_P
{ {
$$ = (Node *)makeBoolConst(FALSE); $$ = (Node *)makeBoolAConst(FALSE);
} }
| NULL_P | NULL_P
{ {
...@@ -7892,11 +7842,11 @@ makeDefElem(char *name, Node *arg) ...@@ -7892,11 +7842,11 @@ makeDefElem(char *name, Node *arg)
return f; return f;
} }
/* makeBoolConst() /* makeBoolAConst()
* Create an A_Const node and initialize to a boolean constant. * Create an A_Const node and initialize to a boolean constant.
*/ */
static A_Const * static A_Const *
makeBoolConst(bool state) makeBoolAConst(bool state)
{ {
A_Const *n = makeNode(A_Const); A_Const *n = makeNode(A_Const);
n->val.type = T_String; n->val.type = T_String;
...@@ -7905,119 +7855,41 @@ makeBoolConst(bool state) ...@@ -7905,119 +7855,41 @@ makeBoolConst(bool state)
return n; return n;
} }
/* makeRowExpr() /* makeRowNullTest()
* Generate separate operator nodes for a single row descriptor expression. * Generate separate operator nodes for a single row descriptor test.
* Perhaps this should go deeper in the parser someday... *
* - thomas 1997-12-22 * Eventually this should be eliminated in favor of making the NullTest
* node type capable of handling it directly.
*/ */
static Node * static Node *
makeRowExpr(List *opr, List *largs, List *rargs) makeRowNullTest(NullTestType test, RowExpr *row)
{ {
Node *expr = NULL; Node *result = NULL;
Node *larg, *rarg; List *arg;
char *oprname;
if (length(largs) != length(rargs)) foreach(arg, row->args)
ereport(ERROR, {
(errcode(ERRCODE_SYNTAX_ERROR), NullTest *n;
errmsg("unequal number of entries in row expression")));
if (lnext(largs) != NIL)
expr = makeRowExpr(opr, lnext(largs), lnext(rargs));
larg = lfirst(largs);
rarg = lfirst(rargs);
oprname = strVal(llast(opr)); n = makeNode(NullTest);
n->arg = (Expr *) lfirst(arg);
n->nulltesttype = test;
if ((strcmp(oprname, "=") == 0) || if (result == NULL)
(strcmp(oprname, "<") == 0) || result = (Node *) n;
(strcmp(oprname, "<=") == 0) || else if (test == IS_NOT_NULL)
(strcmp(oprname, ">") == 0) || result = (Node *) makeA_Expr(AEXPR_OR, NIL, result, (Node *)n);
(strcmp(oprname, ">=") == 0))
{
if (expr == NULL)
expr = (Node *) makeA_Expr(AEXPR_OP, opr, larg, rarg);
else
expr = (Node *) makeA_Expr(AEXPR_AND, NIL, expr,
(Node *) makeA_Expr(AEXPR_OP, opr,
larg, rarg));
}
else if (strcmp(oprname, "<>") == 0)
{
if (expr == NULL)
expr = (Node *) makeA_Expr(AEXPR_OP, opr, larg, rarg);
else else
expr = (Node *) makeA_Expr(AEXPR_OR, NIL, expr, result = (Node *) makeA_Expr(AEXPR_AND, NIL, result, (Node *)n);
(Node *) makeA_Expr(AEXPR_OP, opr,
larg, rarg));
} }
else
if (result == NULL)
{ {
ereport(ERROR, /* zero-length rows? Generate constant TRUE or FALSE */
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), result = (Node *) makeBoolAConst(test == IS_NULL);
errmsg("operator %s is not supported for row expressions",
oprname)));
} }
return expr; return result;
}
/* makeDistinctExpr()
* Generate separate operator nodes for a single row descriptor expression.
* Same comments as for makeRowExpr().
*/
static Node *
makeDistinctExpr(List *largs, List *rargs)
{
Node *expr = NULL;
Node *larg, *rarg;
if (length(largs) != length(rargs))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unequal number of entries in row expression")));
if (lnext(largs) != NIL)
expr = makeDistinctExpr(lnext(largs), lnext(rargs));
larg = lfirst(largs);
rarg = lfirst(rargs);
if (expr == NULL)
expr = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", larg, rarg);
else
expr = (Node *) makeA_Expr(AEXPR_OR, NIL, expr,
(Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=",
larg, rarg));
return expr;
}
/* makeRowNullTest()
* Generate separate operator nodes for a single row descriptor test.
*/
static Node *
makeRowNullTest(NullTestType test, List *args)
{
Node *expr = NULL;
NullTest *n;
if (lnext(args) != NIL)
expr = makeRowNullTest(test, lnext(args));
n = makeNode(NullTest);
n->arg = (Expr *) lfirst(args);
n->nulltesttype = test;
if (expr == NULL)
expr = (Node *) n;
else if (test == IS_NOT_NULL)
expr = (Node *) makeA_Expr(AEXPR_OR, NIL, expr, (Node *)n);
else
expr = (Node *) makeA_Expr(AEXPR_AND, NIL, expr, (Node *)n);
return expr;
} }
/* makeOverlaps() /* makeOverlaps()
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.114 2004/03/15 01:13:40 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.115 2004/05/10 22:44:46 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,19 +19,26 @@ ...@@ -19,19 +19,26 @@
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "nodes/params.h" #include "nodes/params.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "parser/parsetree.h"
#include "parser/parse_coerce.h" #include "parser/parse_coerce.h"
#include "parser/parse_expr.h" #include "parser/parse_expr.h"
#include "parser/parse_func.h" #include "parser/parse_func.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/fmgroids.h" #include "utils/fmgroids.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/syscache.h" #include "utils/syscache.h"
#include "utils/typcache.h"
static Node *coerce_type_typmod(Node *node, static Node *coerce_type_typmod(Node *node,
Oid targetTypeId, int32 targetTypMod, Oid targetTypeId, int32 targetTypMod,
CoercionForm cformat, bool isExplicit); CoercionForm cformat, bool isExplicit);
static Node *coerce_record_to_complex(ParseState *pstate, Node *node,
Oid targetTypeId,
CoercionContext ccontext,
CoercionForm cformat);
/* /*
...@@ -279,6 +286,13 @@ coerce_type(ParseState *pstate, Node *node, ...@@ -279,6 +286,13 @@ coerce_type(ParseState *pstate, Node *node,
} }
return result; return result;
} }
if (inputTypeId == RECORDOID &&
ISCOMPLEX(targetTypeId))
{
/* Coerce a RECORD to a specific complex type */
return coerce_record_to_complex(pstate, node, targetTypeId,
ccontext, cformat);
}
if (typeInheritsFrom(inputTypeId, targetTypeId)) if (typeInheritsFrom(inputTypeId, targetTypeId))
{ {
/* /*
...@@ -359,6 +373,14 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, ...@@ -359,6 +373,14 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
&funcId)) &funcId))
continue; continue;
/*
* If input is RECORD and target is a composite type, assume
* we can coerce (may need tighter checking here)
*/
if (inputTypeId == RECORDOID &&
ISCOMPLEX(targetTypeId))
continue;
/* /*
* If input is a class type that inherits from target, accept * If input is a class type that inherits from target, accept
*/ */
...@@ -506,6 +528,103 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, ...@@ -506,6 +528,103 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
return node; return node;
} }
/*
* coerce_record_to_complex
* Coerce a RECORD to a specific composite type.
*
* Currently we only support this for inputs that are RowExprs or whole-row
* Vars.
*/
static Node *
coerce_record_to_complex(ParseState *pstate, Node *node,
Oid targetTypeId,
CoercionContext ccontext,
CoercionForm cformat)
{
RowExpr *rowexpr;
TupleDesc tupdesc;
List *args = NIL;
List *newargs;
int i;
List *arg;
if (node && IsA(node, RowExpr))
{
args = ((RowExpr *) node)->args;
}
else if (node && IsA(node, Var) &&
((Var *) node)->varattno == InvalidAttrNumber)
{
RangeTblEntry *rte;
AttrNumber nfields;
AttrNumber nf;
rte = GetRTEByRangeTablePosn(pstate,
((Var *) node)->varno,
((Var *) node)->varlevelsup);
nfields = length(rte->eref->colnames);
for (nf = 1; nf <= nfields; nf++)
{
Oid vartype;
int32 vartypmod;
get_rte_attribute_type(rte, nf, &vartype, &vartypmod);
args = lappend(args,
makeVar(((Var *) node)->varno,
nf,
vartype,
vartypmod,
((Var *) node)->varlevelsup));
}
}
else
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(RECORDOID),
format_type_be(targetTypeId))));
tupdesc = lookup_rowtype_tupdesc(targetTypeId, -1);
if (length(args) != tupdesc->natts)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(RECORDOID),
format_type_be(targetTypeId)),
errdetail("Input has wrong number of columns.")));
newargs = NIL;
i = 0;
foreach(arg, args)
{
Node *expr = (Node *) lfirst(arg);
Oid exprtype = exprType(expr);
expr = coerce_to_target_type(pstate,
expr, exprtype,
tupdesc->attrs[i]->atttypid,
tupdesc->attrs[i]->atttypmod,
ccontext,
COERCE_IMPLICIT_CAST);
if (expr == NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(RECORDOID),
format_type_be(targetTypeId)),
errdetail("Cannot cast type %s to %s in column %d.",
format_type_be(exprtype),
format_type_be(tupdesc->attrs[i]->atttypid),
i + 1)));
newargs = lappend(newargs, expr);
i++;
}
rowexpr = makeNode(RowExpr);
rowexpr->args = newargs;
rowexpr->row_typeid = targetTypeId;
rowexpr->row_format = cformat;
return (Node *) rowexpr;
}
/* coerce_to_boolean() /* coerce_to_boolean()
* Coerce an argument of a construct that requires boolean input * Coerce an argument of a construct that requires boolean input
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.169 2004/04/18 18:12:58 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.170 2004/05/10 22:44:46 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -37,13 +37,19 @@ ...@@ -37,13 +37,19 @@
bool Transform_null_equals = false; bool Transform_null_equals = false;
static Node *typecast_expression(ParseState *pstate, Node *expr,
TypeName *typename);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
static Node *transformWholeRowRef(ParseState *pstate, char *schemaname, static Node *transformWholeRowRef(ParseState *pstate, char *schemaname,
char *relname); char *relname);
static Node *transformIndirection(ParseState *pstate, Node *basenode, static Node *transformIndirection(ParseState *pstate, Node *basenode,
List *indirection); List *indirection);
static Node *typecast_expression(ParseState *pstate, Node *expr,
TypeName *typename);
static Node *make_row_op(ParseState *pstate, List *opname,
Node *ltree, Node *rtree);
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
Node *ltree, Node *rtree);
static Expr *make_distinct_op(ParseState *pstate, List *opname,
Node *ltree, Node *rtree);
/* /*
...@@ -202,6 +208,9 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -202,6 +208,9 @@ transformExpr(ParseState *pstate, Node *expr)
{ {
case AEXPR_OP: case AEXPR_OP:
{ {
Node *lexpr = a->lexpr;
Node *rexpr = a->rexpr;
/* /*
* Special-case "foo = NULL" and "NULL = foo" * Special-case "foo = NULL" and "NULL = foo"
* for compatibility with standards-broken * for compatibility with standards-broken
...@@ -211,27 +220,51 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -211,27 +220,51 @@ transformExpr(ParseState *pstate, Node *expr)
if (Transform_null_equals && if (Transform_null_equals &&
length(a->name) == 1 && length(a->name) == 1 &&
strcmp(strVal(lfirst(a->name)), "=") == 0 && strcmp(strVal(lfirst(a->name)), "=") == 0 &&
(exprIsNullConstant(a->lexpr) || (exprIsNullConstant(lexpr) ||
exprIsNullConstant(a->rexpr))) exprIsNullConstant(rexpr)))
{ {
NullTest *n = makeNode(NullTest); NullTest *n = makeNode(NullTest);
n->nulltesttype = IS_NULL; n->nulltesttype = IS_NULL;
if (exprIsNullConstant(a->lexpr)) if (exprIsNullConstant(lexpr))
n->arg = (Expr *) a->rexpr; n->arg = (Expr *) rexpr;
else else
n->arg = (Expr *) a->lexpr; n->arg = (Expr *) lexpr;
result = transformExpr(pstate, result = transformExpr(pstate,
(Node *) n); (Node *) n);
} }
else if (lexpr && IsA(lexpr, RowExpr) &&
rexpr && IsA(rexpr, SubLink) &&
((SubLink *) rexpr)->subLinkType == EXPR_SUBLINK)
{
/*
* Convert "row op subselect" into a
* MULTIEXPR sublink. Formerly the grammar
* did this, but now that a row construct is
* allowed anywhere in expressions, it's
* easier to do it here.
*/
SubLink *s = (SubLink *) rexpr;
s->subLinkType = MULTIEXPR_SUBLINK;
s->lefthand = ((RowExpr *) lexpr)->args;
s->operName = a->name;
result = transformExpr(pstate, (Node *) s);
}
else if (lexpr && IsA(lexpr, RowExpr) &&
rexpr && IsA(rexpr, RowExpr))
{
/* "row op row" */
result = make_row_op(pstate, a->name,
lexpr, rexpr);
}
else else
{ {
Node *lexpr = transformExpr(pstate, /* Ordinary scalar operator */
a->lexpr); lexpr = transformExpr(pstate, lexpr);
Node *rexpr = transformExpr(pstate, rexpr = transformExpr(pstate, rexpr);
a->rexpr);
result = (Node *) make_op(pstate, result = (Node *) make_op(pstate,
a->name, a->name,
...@@ -311,25 +344,27 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -311,25 +344,27 @@ transformExpr(ParseState *pstate, Node *expr)
break; break;
case AEXPR_DISTINCT: case AEXPR_DISTINCT:
{ {
Node *lexpr = transformExpr(pstate, Node *lexpr = a->lexpr;
a->lexpr); Node *rexpr = a->rexpr;
Node *rexpr = transformExpr(pstate,
a->rexpr);
result = (Node *) make_op(pstate, if (lexpr && IsA(lexpr, RowExpr) &&
a->name, rexpr && IsA(rexpr, RowExpr))
lexpr, {
rexpr); /* "row op row" */
if (((OpExpr *) result)->opresulttype != BOOLOID) result = make_row_distinct_op(pstate, a->name,
ereport(ERROR, lexpr, rexpr);
(errcode(ERRCODE_DATATYPE_MISMATCH), }
errmsg("IS DISTINCT FROM requires = operator to yield boolean"))); else
{
/* Ordinary scalar operator */
lexpr = transformExpr(pstate, lexpr);
rexpr = transformExpr(pstate, rexpr);
/* result = (Node *) make_distinct_op(pstate,
* We rely on DistinctExpr and OpExpr being a->name,
* same struct lexpr,
*/ rexpr);
NodeSetTag(result, T_DistinctExpr); }
} }
break; break;
case AEXPR_NULLIF: case AEXPR_NULLIF:
...@@ -787,6 +822,32 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -787,6 +822,32 @@ transformExpr(ParseState *pstate, Node *expr)
break; break;
} }
case T_RowExpr:
{
RowExpr *r = (RowExpr *) expr;
RowExpr *newr = makeNode(RowExpr);
List *newargs = NIL;
List *arg;
/* Transform the field expressions */
foreach(arg, r->args)
{
Node *e = (Node *) lfirst(arg);
Node *newe;
newe = transformExpr(pstate, e);
newargs = lappend(newargs, newe);
}
newr->args = newargs;
/* Barring later casting, we consider the type RECORD */
newr->row_typeid = RECORDOID;
newr->row_format = COERCE_IMPLICIT_CAST;
result = (Node *) newr;
break;
}
case T_CoalesceExpr: case T_CoalesceExpr:
{ {
CoalesceExpr *c = (CoalesceExpr *) expr; CoalesceExpr *c = (CoalesceExpr *) expr;
...@@ -1113,14 +1174,11 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -1113,14 +1174,11 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
/* /*
* Construct a whole-row reference to represent the notation "relation.*". * Construct a whole-row reference to represent the notation "relation.*".
* *
* In simple cases, this will be a Var with varno set to the correct range * A whole-row reference is a Var with varno set to the correct range
* table entry, and varattno == 0 to signal that it references the whole * table entry, and varattno == 0 to signal that it references the whole
* tuple. (Use of zero here is unclean, since it could easily be confused * tuple. (Use of zero here is unclean, since it could easily be confused
* with error cases, but it's not worth changing now.) The vartype indicates * with error cases, but it's not worth changing now.) The vartype indicates
* a rowtype; either a named composite type, or RECORD. * a rowtype; either a named composite type, or RECORD.
*
* We also need the ability to build a row-constructor expression, but the
* infrastructure for that doesn't exist just yet.
*/ */
static Node * static Node *
transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname) transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname)
...@@ -1185,12 +1243,10 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname) ...@@ -1185,12 +1243,10 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname)
break; break;
default: default:
/* /*
* RTE is a join or subselect. For the moment we represent this * RTE is a join or subselect. We represent this as a whole-row
* as a whole-row Var of RECORD type, but this will not actually * Var of RECORD type. (Note that in most cases the Var will
* work; need a row-constructor expression instead. * be expanded to a RowExpr during planning, but that is not
* * our concern here.)
* XXX after fixing, be sure that unknown_attribute still
* does the right thing.
*/ */
result = (Node *) makeVar(vnum, result = (Node *) makeVar(vnum,
InvalidAttrNumber, InvalidAttrNumber,
...@@ -1266,8 +1322,8 @@ exprType(Node *expr) ...@@ -1266,8 +1322,8 @@ exprType(Node *expr)
if (sublink->subLinkType == EXPR_SUBLINK) if (sublink->subLinkType == EXPR_SUBLINK)
type = tent->resdom->restype; type = tent->resdom->restype;
else else
/* ARRAY_SUBLINK */
{ {
/* ARRAY_SUBLINK */
type = get_array_type(tent->resdom->restype); type = get_array_type(tent->resdom->restype);
if (!OidIsValid(type)) if (!OidIsValid(type))
ereport(ERROR, ereport(ERROR,
...@@ -1305,8 +1361,8 @@ exprType(Node *expr) ...@@ -1305,8 +1361,8 @@ exprType(Node *expr)
if (subplan->subLinkType == EXPR_SUBLINK) if (subplan->subLinkType == EXPR_SUBLINK)
type = tent->resdom->restype; type = tent->resdom->restype;
else else
/* ARRAY_SUBLINK */
{ {
/* ARRAY_SUBLINK */
type = get_array_type(tent->resdom->restype); type = get_array_type(tent->resdom->restype);
if (!OidIsValid(type)) if (!OidIsValid(type))
ereport(ERROR, ereport(ERROR,
...@@ -1340,6 +1396,9 @@ exprType(Node *expr) ...@@ -1340,6 +1396,9 @@ exprType(Node *expr)
case T_ArrayExpr: case T_ArrayExpr:
type = ((ArrayExpr *) expr)->array_typeid; type = ((ArrayExpr *) expr)->array_typeid;
break; break;
case T_RowExpr:
type = ((RowExpr *) expr)->row_typeid;
break;
case T_CoalesceExpr: case T_CoalesceExpr:
type = ((CoalesceExpr *) expr)->coalescetype; type = ((CoalesceExpr *) expr)->coalescetype;
break; break;
...@@ -1573,3 +1632,166 @@ typecast_expression(ParseState *pstate, Node *expr, TypeName *typename) ...@@ -1573,3 +1632,166 @@ typecast_expression(ParseState *pstate, Node *expr, TypeName *typename)
return expr; return expr;
} }
/*
* Transform a "row op row" construct
*/
static Node *
make_row_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree)
{
Node *result = NULL;
RowExpr *lrow,
*rrow;
List *largs,
*rargs;
List *largl,
*rargl;
char *oprname;
BoolExprType boolop;
/* Inputs are untransformed RowExprs */
lrow = (RowExpr *) transformExpr(pstate, ltree);
rrow = (RowExpr *) transformExpr(pstate, rtree);
Assert(IsA(lrow, RowExpr));
Assert(IsA(rrow, RowExpr));
largs = lrow->args;
rargs = rrow->args;
if (length(largs) != length(rargs))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unequal number of entries in row expression")));
/*
* XXX it's really wrong to generate a simple AND combination for < <=
* > >=. We probably need to invent a new runtime node type to handle
* those correctly. For the moment, though, keep on doing this ...
*/
oprname = strVal(llast(opname));
if ((strcmp(oprname, "=") == 0) ||
(strcmp(oprname, "<") == 0) ||
(strcmp(oprname, "<=") == 0) ||
(strcmp(oprname, ">") == 0) ||
(strcmp(oprname, ">=") == 0))
{
boolop = AND_EXPR;
}
else if (strcmp(oprname, "<>") == 0)
{
boolop = OR_EXPR;
}
else
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("operator %s is not supported for row expressions",
oprname)));
boolop = 0; /* keep compiler quiet */
}
/* XXX use forboth */
rargl = rargs;
foreach(largl, largs)
{
Node *larg = (Node *) lfirst(largl);
Node *rarg = (Node *) lfirst(rargl);
Node *cmp;
rargl = lnext(rargl);
cmp = (Node *) make_op(pstate, opname, larg, rarg);
cmp = coerce_to_boolean(pstate, cmp, "row comparison");
if (result == NULL)
result = cmp;
else
result = (Node *) makeBoolExpr(boolop,
makeList2(result, cmp));
}
if (result == NULL)
{
/* zero-length rows? Generate constant TRUE or FALSE */
if (boolop == AND_EXPR)
result = makeBoolConst(true, false);
else
result = makeBoolConst(false, false);
}
return result;
}
/*
* Transform a "row IS DISTINCT FROM row" construct
*/
static Node *
make_row_distinct_op(ParseState *pstate, List *opname,
Node *ltree, Node *rtree)
{
Node *result = NULL;
RowExpr *lrow,
*rrow;
List *largs,
*rargs;
List *largl,
*rargl;
/* Inputs are untransformed RowExprs */
lrow = (RowExpr *) transformExpr(pstate, ltree);
rrow = (RowExpr *) transformExpr(pstate, rtree);
Assert(IsA(lrow, RowExpr));
Assert(IsA(rrow, RowExpr));
largs = lrow->args;
rargs = rrow->args;
if (length(largs) != length(rargs))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unequal number of entries in row expression")));
/* XXX use forboth */
rargl = rargs;
foreach(largl, largs)
{
Node *larg = (Node *) lfirst(largl);
Node *rarg = (Node *) lfirst(rargl);
Node *cmp;
rargl = lnext(rargl);
cmp = (Node *) make_distinct_op(pstate, opname, larg, rarg);
if (result == NULL)
result = cmp;
else
result = (Node *) makeBoolExpr(OR_EXPR,
makeList2(result, cmp));
}
if (result == NULL)
{
/* zero-length rows? Generate constant FALSE */
result = makeBoolConst(false, false);
}
return result;
}
/*
* make the node for an IS DISTINCT FROM operator
*/
static Expr *
make_distinct_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree)
{
Expr *result;
result = make_op(pstate, opname, ltree, rtree);
if (((OpExpr *) result)->opresulttype != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("IS DISTINCT FROM requires = operator to yield boolean")));
/*
* We rely on DistinctExpr and OpExpr being
* same struct
*/
NodeSetTag(result, T_DistinctExpr);
return result;
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.116 2004/04/02 19:06:58 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.117 2004/05/10 22:44:46 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -643,6 +643,10 @@ FigureColnameInternal(Node *node, char **name) ...@@ -643,6 +643,10 @@ FigureColnameInternal(Node *node, char **name)
/* make ARRAY[] act like a function */ /* make ARRAY[] act like a function */
*name = "array"; *name = "array";
return 2; return 2;
case T_RowExpr:
/* make ROW() act like a function */
*name = "row";
return 2;
case T_CoalesceExpr: case T_CoalesceExpr:
/* make coalesce() act like a regular function */ /* make coalesce() act like a regular function */
*name = "coalesce"; *name = "coalesce";
......
...@@ -7,7 +7,7 @@ ...@@ -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.134 2004/04/01 21:28:44 tgl Exp $ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.135 2004/05/10 22:44:46 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -212,6 +212,8 @@ rewriteRuleAction(Query *parsetree, ...@@ -212,6 +212,8 @@ rewriteRuleAction(Query *parsetree,
sub_action = (Query *) ResolveNew((Node *) sub_action, sub_action = (Query *) ResolveNew((Node *) sub_action,
new_varno, new_varno,
0, 0,
rt_fetch(new_varno,
sub_action->rtable),
parsetree->targetList, parsetree->targetList,
event, event,
current_varno); current_varno);
...@@ -947,6 +949,7 @@ CopyAndAddInvertedQual(Query *parsetree, ...@@ -947,6 +949,7 @@ CopyAndAddInvertedQual(Query *parsetree,
new_qual = ResolveNew(new_qual, new_qual = ResolveNew(new_qual,
PRS2_NEW_VARNO, PRS2_NEW_VARNO,
0, 0,
rt_fetch(rt_index, parsetree->rtable),
parsetree->targetList, parsetree->targetList,
event, event,
rt_index); rt_index);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.81 2003/11/29 19:51:55 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.82 2004/05/10 22:44:46 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -850,6 +850,10 @@ AddInvertedQual(Query *parsetree, Node *qual) ...@@ -850,6 +850,10 @@ AddInvertedQual(Query *parsetree, Node *qual)
* If not, we either change the unmatched Var's varno to update_varno * If not, we either change the unmatched Var's varno to update_varno
* (when event == CMD_UPDATE) or replace it with a constant NULL. * (when event == CMD_UPDATE) or replace it with a constant NULL.
* *
* The caller must also provide target_rte, the RTE describing the target
* relation. This is needed to handle whole-row Vars referencing the target.
* We expand such Vars into RowExpr constructs.
*
* Note: the business with inserted_sublink is needed to update hasSubLinks * Note: the business with inserted_sublink is needed to update hasSubLinks
* in subqueries when the replacement adds a subquery inside a subquery. * in subqueries when the replacement adds a subquery inside a subquery.
* Messy, isn't it? We do not need to do similar pushups for hasAggs, * Messy, isn't it? We do not need to do similar pushups for hasAggs,
...@@ -861,12 +865,52 @@ typedef struct ...@@ -861,12 +865,52 @@ typedef struct
{ {
int target_varno; int target_varno;
int sublevels_up; int sublevels_up;
RangeTblEntry *target_rte;
List *targetlist; List *targetlist;
int event; int event;
int update_varno; int update_varno;
bool inserted_sublink; bool inserted_sublink;
} ResolveNew_context; } ResolveNew_context;
static Node *
resolve_one_var(Var *var, ResolveNew_context *context)
{
TargetEntry *tle;
tle = get_tle_by_resno(context->targetlist, var->varattno);
if (tle == NULL)
{
/* Failed to find column in insert/update tlist */
if (context->event == CMD_UPDATE)
{
/* For update, just change unmatched var's varno */
var = (Var *) copyObject(var);
var->varno = context->update_varno;
var->varnoold = context->update_varno;
return (Node *) var;
}
else
{
/* Otherwise replace unmatched var with a null */
return (Node *) makeNullConst(var->vartype);
}
}
else
{
/* Make a copy of the tlist item to return */
Node *n = copyObject(tle->expr);
/* Adjust varlevelsup if tlist item is from higher query */
if (var->varlevelsup > 0)
IncrementVarSublevelsUp(n, var->varlevelsup, 0);
/* Report it if we are adding a sublink to query */
if (!context->inserted_sublink)
context->inserted_sublink = checkExprHasSubLink(n);
return n;
}
}
static Node * static Node *
ResolveNew_mutator(Node *node, ResolveNew_context *context) ResolveNew_mutator(Node *node, ResolveNew_context *context)
{ {
...@@ -881,45 +925,41 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context) ...@@ -881,45 +925,41 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
if (this_varno == context->target_varno && if (this_varno == context->target_varno &&
this_varlevelsup == context->sublevels_up) this_varlevelsup == context->sublevels_up)
{ {
TargetEntry *tle;
/* band-aid: don't do the wrong thing with a whole-tuple Var */
if (var->varattno == InvalidAttrNumber) if (var->varattno == InvalidAttrNumber)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot handle whole-row reference")));
tle = get_tle_by_resno(context->targetlist, var->varattno);
if (tle == NULL)
{ {
if (context->event == CMD_UPDATE) /* Must expand whole-tuple reference into RowExpr */
RangeTblEntry *rte = context->target_rte;
RowExpr *rowexpr;
List *fields = NIL;
AttrNumber nfields = length(rte->eref->colnames);
AttrNumber nf;
for (nf = 1; nf <= nfields; nf++)
{ {
/* For update, just change unmatched var's varno */ Oid vartype;
var = (Var *) copyObject(node); int32 vartypmod;
var->varno = context->update_varno; Var *newvar;
var->varnoold = context->update_varno;
return (Node *) var; get_rte_attribute_type(rte, nf, &vartype, &vartypmod);
newvar = makeVar(this_varno,
nf,
vartype,
vartypmod,
this_varlevelsup);
fields = lappend(fields,
resolve_one_var(newvar, context));
} }
else
{ rowexpr = makeNode(RowExpr);
/* Otherwise replace unmatched var with a null */ rowexpr->args = fields;
return (Node *) makeNullConst(var->vartype); rowexpr->row_typeid = var->vartype;
} rowexpr->row_format = COERCE_IMPLICIT_CAST;
}
else return (Node *) rowexpr;
{
/* Make a copy of the tlist item to return */
Node *n = copyObject(tle->expr);
/* Adjust varlevelsup if tlist item is from higher query */
if (this_varlevelsup > 0)
IncrementVarSublevelsUp(n, this_varlevelsup, 0);
/* Report it if we are adding a sublink to query */
if (!context->inserted_sublink)
context->inserted_sublink = checkExprHasSubLink(n);
return n;
} }
/* Normal case for scalar variable */
return resolve_one_var(var, context);
} }
/* otherwise fall through to copy the var normally */ /* otherwise fall through to copy the var normally */
} }
...@@ -948,12 +988,14 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context) ...@@ -948,12 +988,14 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
Node * Node *
ResolveNew(Node *node, int target_varno, int sublevels_up, ResolveNew(Node *node, int target_varno, int sublevels_up,
RangeTblEntry *target_rte,
List *targetlist, int event, int update_varno) List *targetlist, int event, int update_varno)
{ {
ResolveNew_context context; ResolveNew_context context;
context.target_varno = target_varno; context.target_varno = target_varno;
context.sublevels_up = sublevels_up; context.sublevels_up = sublevels_up;
context.target_rte = target_rte;
context.targetlist = targetlist; context.targetlist = targetlist;
context.event = event; context.event = event;
context.update_varno = update_varno; context.update_varno = update_varno;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.165 2004/05/07 03:19:44 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.166 2004/05/10 22:44:46 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -2432,6 +2432,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) ...@@ -2432,6 +2432,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
case T_ArrayRef: case T_ArrayRef:
case T_ArrayExpr: case T_ArrayExpr:
case T_RowExpr:
case T_CoalesceExpr: case T_CoalesceExpr:
case T_NullIfExpr: case T_NullIfExpr:
case T_Aggref: case T_Aggref:
...@@ -2528,6 +2529,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) ...@@ -2528,6 +2529,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
case T_BoolExpr: /* lower precedence */ case T_BoolExpr: /* lower precedence */
case T_ArrayRef: /* other separators */ case T_ArrayRef: /* other separators */
case T_ArrayExpr: /* other separators */ case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */ case T_CoalesceExpr: /* own parentheses */
case T_NullIfExpr: /* other separators */ case T_NullIfExpr: /* other separators */
case T_Aggref: /* own parentheses */ case T_Aggref: /* own parentheses */
...@@ -2574,6 +2576,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) ...@@ -2574,6 +2576,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
} }
case T_ArrayRef: /* other separators */ case T_ArrayRef: /* other separators */
case T_ArrayExpr: /* other separators */ case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */ case T_CoalesceExpr: /* own parentheses */
case T_NullIfExpr: /* other separators */ case T_NullIfExpr: /* other separators */
case T_Aggref: /* own parentheses */ case T_Aggref: /* own parentheses */
...@@ -2942,11 +2945,9 @@ get_rule_expr(Node *node, deparse_context *context, ...@@ -2942,11 +2945,9 @@ get_rule_expr(Node *node, deparse_context *context,
* arg.fieldname, but most cases where FieldSelect is used * arg.fieldname, but most cases where FieldSelect is used
* are *not* simple. So, always use parenthesized syntax. * are *not* simple. So, always use parenthesized syntax.
*/ */
if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, '(');
appendStringInfoChar(buf, '(');
get_rule_expr_paren((Node *) fselect->arg, context, true, node); get_rule_expr_paren((Node *) fselect->arg, context, true, node);
if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')');
appendStringInfoChar(buf, ')');
appendStringInfo(buf, ".%s", quote_identifier(fieldname)); appendStringInfo(buf, ".%s", quote_identifier(fieldname));
} }
break; break;
...@@ -3051,6 +3052,33 @@ get_rule_expr(Node *node, deparse_context *context, ...@@ -3051,6 +3052,33 @@ get_rule_expr(Node *node, deparse_context *context,
} }
break; break;
case T_RowExpr:
{
RowExpr *rowexpr = (RowExpr *) node;
List *arg;
char *sep;
/*
* SQL99 allows "ROW" to be omitted when length(args) > 1,
* but for simplicity we always print it.
*/
appendStringInfo(buf, "ROW(");
sep = "";
foreach(arg, rowexpr->args)
{
Node *e = (Node *) lfirst(arg);
appendStringInfo(buf, sep);
get_rule_expr(e, context, true);
sep = ", ";
}
appendStringInfo(buf, ")");
if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
appendStringInfo(buf, "::%s",
format_type_with_typemod(rowexpr->row_typeid, -1));
}
break;
case T_CoalesceExpr: case T_CoalesceExpr:
{ {
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,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/catalog/catversion.h,v 1.228 2004/05/08 21:21:18 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.229 2004/05/10 22:44:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200405081 #define CATALOG_VERSION_NO 200405101
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -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/executor/executor.h,v 1.109 2004/04/01 21:28:46 tgl Exp $ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.110 2004/05/10 22:44:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -175,6 +175,7 @@ extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, ...@@ -175,6 +175,7 @@ extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate,
TupleDesc tupType); TupleDesc tupType);
extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid); extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid);
extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid); extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid);
extern TupleDesc ExecTypeFromExprList(List *exprList);
extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg);
typedef struct TupOutputState typedef struct TupOutputState
......
...@@ -7,7 +7,7 @@ ...@@ -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.115 2004/04/01 21:28:46 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.116 2004/05/10 22:44:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -613,6 +613,17 @@ typedef struct ArrayExprState ...@@ -613,6 +613,17 @@ typedef struct ArrayExprState
char elemalign; /* typalign of the element type */ char elemalign; /* typalign of the element type */
} ArrayExprState; } ArrayExprState;
/* ----------------
* RowExprState node
* ----------------
*/
typedef struct RowExprState
{
ExprState xprstate;
List *args; /* the arguments */
TupleDesc tupdesc; /* descriptor for result tuples */
} RowExprState;
/* ---------------- /* ----------------
* CoalesceExprState node * CoalesceExprState node
* ---------------- * ----------------
......
...@@ -7,7 +7,7 @@ ...@@ -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/makefuncs.h,v 1.48 2003/11/29 22:41:06 pgsql Exp $ * $PostgreSQL: pgsql/src/include/nodes/makefuncs.h,v 1.49 2004/05/10 22:44:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -45,6 +45,8 @@ extern Const *makeConst(Oid consttype, ...@@ -45,6 +45,8 @@ extern Const *makeConst(Oid consttype,
extern Const *makeNullConst(Oid consttype); extern Const *makeNullConst(Oid consttype);
extern Node *makeBoolConst(bool value, bool isnull);
extern Expr *makeBoolExpr(BoolExprType boolop, List *args); extern Expr *makeBoolExpr(BoolExprType boolop, List *args);
extern Alias *makeAlias(const char *aliasname, List *colnames); extern Alias *makeAlias(const char *aliasname, List *colnames);
......
...@@ -7,7 +7,7 @@ ...@@ -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.153 2004/05/05 04:48:47 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.154 2004/05/10 22:44:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -115,6 +115,7 @@ typedef enum NodeTag ...@@ -115,6 +115,7 @@ typedef enum NodeTag
T_CaseWhen, T_CaseWhen,
T_CaseTestExpr, T_CaseTestExpr,
T_ArrayExpr, T_ArrayExpr,
T_RowExpr,
T_CoalesceExpr, T_CoalesceExpr,
T_NullIfExpr, T_NullIfExpr,
T_NullTest, T_NullTest,
...@@ -145,6 +146,7 @@ typedef enum NodeTag ...@@ -145,6 +146,7 @@ typedef enum NodeTag
T_CaseExprState, T_CaseExprState,
T_CaseWhenState, T_CaseWhenState,
T_ArrayExprState, T_ArrayExprState,
T_RowExprState,
T_CoalesceExprState, T_CoalesceExprState,
T_CoerceToDomainState, T_CoerceToDomainState,
T_DomainConstraintState, T_DomainConstraintState,
......
...@@ -10,7 +10,7 @@ ...@@ -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.97 2004/04/01 21:28:46 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.98 2004/05/10 22:44:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -632,6 +632,23 @@ typedef struct ArrayExpr ...@@ -632,6 +632,23 @@ typedef struct ArrayExpr
bool multidims; /* true if elements are sub-arrays */ bool multidims; /* true if elements are sub-arrays */
} ArrayExpr; } ArrayExpr;
/*
* RowExpr - a ROW() expression
*/
typedef struct RowExpr
{
Expr xpr;
List *args; /* the fields */
Oid row_typeid; /* RECORDOID or a composite type's ID */
/*
* Note: we deliberately do NOT store a typmod. Although a typmod
* will be associated with specific RECORD types at runtime, it will
* differ for different backends, and so cannot safely be stored in
* stored parsetrees. We must assume typmod -1 for a RowExpr node.
*/
CoercionForm row_format; /* how to display this node */
} RowExpr;
/* /*
* CoalesceExpr - a COALESCE expression * CoalesceExpr - a COALESCE expression
*/ */
......
...@@ -7,7 +7,7 @@ ...@@ -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/optimizer/var.h,v 1.29 2003/11/29 22:41:07 pgsql Exp $ * $PostgreSQL: pgsql/src/include/optimizer/var.h,v 1.30 2004/05/10 22:44:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
extern Relids pull_varnos(Node *node); extern Relids pull_varnos(Node *node);
extern bool contain_var_reference(Node *node, int varno, int varattno, extern bool contain_var_reference(Node *node, int varno, int varattno,
int levelsup); int levelsup);
extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
extern bool contain_var_clause(Node *node); extern bool contain_var_clause(Node *node);
extern bool contain_vars_of_level(Node *node, int levelsup); extern bool contain_vars_of_level(Node *node, int levelsup);
extern bool contain_vars_above_level(Node *node, int levelsup); extern bool contain_vars_above_level(Node *node, int levelsup);
......
...@@ -7,7 +7,7 @@ ...@@ -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/rewrite/rewriteManip.h,v 1.34 2003/11/29 22:41:11 pgsql Exp $ * $PostgreSQL: pgsql/src/include/rewrite/rewriteManip.h,v 1.35 2004/05/10 22:44:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -38,6 +38,7 @@ extern bool checkExprHasAggs(Node *node); ...@@ -38,6 +38,7 @@ extern bool checkExprHasAggs(Node *node);
extern bool checkExprHasSubLink(Node *node); extern bool checkExprHasSubLink(Node *node);
extern Node *ResolveNew(Node *node, int target_varno, int sublevels_up, extern Node *ResolveNew(Node *node, int target_varno, int sublevels_up,
List *targetlist, int event, int update_varno); RangeTblEntry *target_rte,
List *targetlist, int event, int update_varno);
#endif /* REWRITEMANIP_H */ #endif /* REWRITEMANIP_H */
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.99 2004/04/01 21:28:46 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.100 2004/05/10 22:44:49 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -3752,6 +3752,16 @@ exec_simple_check_node(Node *node) ...@@ -3752,6 +3752,16 @@ exec_simple_check_node(Node *node)
return TRUE; return TRUE;
} }
case T_RowExpr:
{
RowExpr *expr = (RowExpr *) node;
if (!exec_simple_check_node((Node *) expr->args))
return FALSE;
return TRUE;
}
case T_CoalesceExpr: case T_CoalesceExpr:
{ {
CoalesceExpr *expr = (CoalesceExpr *) node; CoalesceExpr *expr = (CoalesceExpr *) node;
......
...@@ -218,6 +218,17 @@ SELECT hobbies_by_name('basketball'); ...@@ -218,6 +218,17 @@ SELECT hobbies_by_name('basketball');
SELECT name, overpaid(emp.*) FROM emp; SELECT name, overpaid(emp.*) FROM emp;
--
-- Try a few cases with SQL-spec row constructor expressions
--
SELECT * FROM equipment(ROW('skywalking', 'mer'));
SELECT name(equipment(ROW('skywalking', 'mer')));
SELECT *, name(equipment(h.*)) FROM hobbies_r h;
SELECT *, (equipment(CAST((h.*) AS hobbies_r))).name FROM hobbies_r h;
-- --
-- check that old-style C functions work properly with TOASTed values -- check that old-style C functions work properly with TOASTed values
-- --
......
...@@ -45,9 +45,9 @@ SELECT '' AS four, * FROM DEFAULTEXPR_TBL; ...@@ -45,9 +45,9 @@ SELECT '' AS four, * FROM DEFAULTEXPR_TBL;
-- syntax errors -- syntax errors
-- test for extraneous comma -- test for extraneous comma
CREATE TABLE error_tbl (i int DEFAULT (100, )); CREATE TABLE error_tbl (i int DEFAULT (100, ));
ERROR: syntax error at or near "," at character 43 ERROR: syntax error at or near ")" at character 45
LINE 1: CREATE TABLE error_tbl (i int DEFAULT (100, )); LINE 1: CREATE TABLE error_tbl (i int DEFAULT (100, ));
^ ^
-- this will fail because gram.y uses b_expr not a_expr for defaults, -- this will fail because gram.y uses b_expr not a_expr for defaults,
-- to avoid a shift/reduce conflict that arises from NOT NULL being -- to avoid a shift/reduce conflict that arises from NOT NULL being
-- part of the column definition syntax: -- part of the column definition syntax:
......
...@@ -686,6 +686,45 @@ SELECT name, overpaid(emp.*) FROM emp; ...@@ -686,6 +686,45 @@ SELECT name, overpaid(emp.*) FROM emp;
linda | f linda | f
(6 rows) (6 rows)
--
-- Try a few cases with SQL-spec row constructor expressions
--
SELECT * FROM equipment(ROW('skywalking', 'mer'));
name | hobby
------+------------
guts | skywalking
(1 row)
SELECT name(equipment(ROW('skywalking', 'mer')));
name
------
guts
(1 row)
SELECT *, name(equipment(h.*)) FROM hobbies_r h;
name | person | name
-------------+--------+---------------
posthacking | mike | advil
posthacking | mike | peet's coffee
posthacking | jeff | advil
posthacking | jeff | peet's coffee
basketball | joe | hightops
basketball | sally | hightops
skywalking | | guts
(7 rows)
SELECT *, (equipment(CAST((h.*) AS hobbies_r))).name FROM hobbies_r h;
name | person | name
-------------+--------+---------------
posthacking | mike | advil
posthacking | mike | peet's coffee
posthacking | jeff | advil
posthacking | jeff | peet's coffee
basketball | joe | hightops
basketball | sally | hightops
skywalking | | guts
(7 rows)
-- --
-- check that old-style C functions work properly with TOASTed values -- check that old-style C functions work properly with TOASTed values
-- --
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment