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.
This commit is contained in:
parent
9a939886ac
commit
2f63232d30
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$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
|
||||
-->
|
||||
|
||||
@ -7822,13 +7822,15 @@ SELECT col1 FROM tab1
|
||||
</para>
|
||||
|
||||
<synopsis>
|
||||
(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) IN (<replaceable>subquery</replaceable>)
|
||||
<replaceable>row_constructor</replaceable> IN (<replaceable>subquery</replaceable>)
|
||||
</synopsis>
|
||||
|
||||
<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
|
||||
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.
|
||||
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
|
||||
@ -7876,13 +7878,15 @@ SELECT col1 FROM tab1
|
||||
</para>
|
||||
|
||||
<synopsis>
|
||||
(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) NOT IN (<replaceable>subquery</replaceable>)
|
||||
<replaceable>row_constructor</replaceable> NOT IN (<replaceable>subquery</replaceable>)
|
||||
</synopsis>
|
||||
|
||||
<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
|
||||
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.
|
||||
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).
|
||||
@ -7938,14 +7942,16 @@ SELECT col1 FROM tab1
|
||||
</para>
|
||||
|
||||
<synopsis>
|
||||
(<replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ...</optional>) <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</> ANY (<replaceable>subquery</replaceable>)
|
||||
<replaceable>row_constructor</replaceable> <replaceable>operator</> SOME (<replaceable>subquery</replaceable>)
|
||||
</synopsis>
|
||||
|
||||
<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
|
||||
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,
|
||||
using the given <replaceable>operator</replaceable>. Presently,
|
||||
only <literal>=</literal> and <literal><></literal> operators are allowed
|
||||
@ -8003,13 +8009,15 @@ SELECT col1 FROM tab1
|
||||
</para>
|
||||
|
||||
<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>
|
||||
|
||||
<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
|
||||
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,
|
||||
using the given <replaceable>operator</replaceable>. Presently,
|
||||
only <literal>=</literal> and <literal><></literal> operators are allowed
|
||||
@ -8041,16 +8049,17 @@ SELECT col1 FROM tab1
|
||||
</indexterm>
|
||||
|
||||
<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>
|
||||
|
||||
<para>
|
||||
The left-hand side is a list of scalar expressions. The right-hand side is
|
||||
a parenthesized subquery, which must return exactly as many columns as there
|
||||
are expressions on the left-hand side. Furthermore, the subquery cannot
|
||||
return more than one row. (If it returns zero rows, the result is taken to
|
||||
be null.) The left-hand side is evaluated and compared row-wise to the
|
||||
single subquery result row.
|
||||
The left-hand side 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 expressions in the left-hand row. Furthermore,
|
||||
the subquery cannot return more than one row. (If it returns zero rows,
|
||||
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><></literal> operators are allowed
|
||||
in row-wise comparisons.
|
||||
The result is <quote>true</> if the two rows are equal or unequal, respectively.
|
||||
@ -8223,13 +8232,14 @@ AND
|
||||
<title>Row-wise Comparison</title>
|
||||
|
||||
<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>
|
||||
|
||||
<para>
|
||||
Each side is a list of scalar expressions; the two lists must be
|
||||
of the same length. Each side is evaluated and they are compared
|
||||
row-wise.
|
||||
Each side is a row constructor,
|
||||
as described in <xref linkend="sql-syntax-row-constructors">.
|
||||
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><></literal> operators are allowed
|
||||
in row-wise comparisons.
|
||||
The result is <quote>true</> if the two rows are equal or unequal, respectively.
|
||||
@ -8242,6 +8252,29 @@ AND
|
||||
are unequal if any corresponding members are non-null and unequal;
|
||||
otherwise the result of the row comparison is unknown (null).
|
||||
</para>
|
||||
|
||||
<synopsis>
|
||||
<replaceable>row_constructor</replaceable> IS DISTINCT FROM <replaceable>row_constructor</replaceable>
|
||||
</synopsis>
|
||||
|
||||
<para>
|
||||
This construct is similar to a <literal><></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>
|
||||
</sect1>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$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">
|
||||
@ -920,6 +920,12 @@ SELECT 3 OPERATOR(pg_catalog.+) 4;
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
A row constructor.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Another value expression in parentheses, useful to group
|
||||
@ -1428,6 +1434,79 @@ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
|
||||
|
||||
</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">
|
||||
<title>Expression Evaluation Rules</title>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$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">
|
||||
@ -240,10 +240,11 @@ SELECT clean_emp();
|
||||
<title><acronym>SQL</acronym> Functions on Composite Types</title>
|
||||
|
||||
<para>
|
||||
When specifying functions with arguments of composite
|
||||
When writing functions with arguments of composite
|
||||
types, we must not only specify which
|
||||
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
|
||||
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
|
||||
@ -252,16 +253,16 @@ SELECT clean_emp();
|
||||
<screen>
|
||||
CREATE TABLE emp (
|
||||
name text,
|
||||
salary integer,
|
||||
salary numeric,
|
||||
age integer,
|
||||
cubicle point
|
||||
);
|
||||
|
||||
CREATE FUNCTION double_salary(emp) RETURNS integer AS '
|
||||
CREATE FUNCTION double_salary(emp) RETURNS numeric AS '
|
||||
SELECT $1.salary * 2 AS salary;
|
||||
' LANGUAGE SQL;
|
||||
|
||||
SELECT name, double_salary(emp) AS dream
|
||||
SELECT name, double_salary(emp.*) AS dream
|
||||
FROM emp
|
||||
WHERE emp.cubicle ~= point '(2,1)';
|
||||
|
||||
@ -274,15 +275,27 @@ SELECT name, double_salary(emp) AS dream
|
||||
<para>
|
||||
Notice the use of the syntax <literal>$1.salary</literal>
|
||||
to select one field of the argument row value. Also notice
|
||||
how the calling <command>SELECT</> command uses a table name to denote
|
||||
the entire current row of that table as a composite value. The table
|
||||
row can alternatively be referenced like this:
|
||||
how the calling <command>SELECT</> command uses <literal>*</>
|
||||
to select
|
||||
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>
|
||||
SELECT name, double_salary(emp.*) AS dream
|
||||
SELECT name, double_salary(emp) AS dream
|
||||
FROM emp
|
||||
WHERE emp.cubicle ~= point '(2,1)';
|
||||
</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>
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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 @@
|
||||
#include "executor/execdebug.h"
|
||||
#include "executor/functions.h"
|
||||
#include "executor/nodeSubplan.h"
|
||||
#include "funcapi.h"
|
||||
#include "miscadmin.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "parser/parse_expr.h"
|
||||
@ -93,6 +94,9 @@ static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
|
||||
static Datum ExecEvalArray(ArrayExprState *astate,
|
||||
ExprContext *econtext,
|
||||
bool *isNull, ExprDoneCond *isDone);
|
||||
static Datum ExecEvalRow(RowExprState *rstate,
|
||||
ExprContext *econtext,
|
||||
bool *isNull, ExprDoneCond *isDone);
|
||||
static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
|
||||
ExprContext *econtext,
|
||||
bool *isNull, ExprDoneCond *isDone);
|
||||
@ -2101,6 +2105,54 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
|
||||
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
|
||||
* ----------------------------------------------------------------
|
||||
@ -2822,6 +2874,39 @@ ExecInitExpr(Expr *node, PlanState *parent)
|
||||
state = (ExprState *) astate;
|
||||
}
|
||||
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:
|
||||
{
|
||||
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
*
|
||||
* 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 @@
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "executor/executor.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/typcache.h"
|
||||
|
||||
@ -118,6 +119,7 @@
|
||||
static TupleDesc ExecTypeFromTLInternal(List *targetList,
|
||||
bool hasoid, bool skipjunk);
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* tuple table create/delete functions
|
||||
* ----------------------------------------------------------------
|
||||
@ -595,6 +597,38 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk)
|
||||
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
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* 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)
|
||||
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
|
||||
*/
|
||||
@ -2674,6 +2689,9 @@ copyObject(void *from)
|
||||
case T_ArrayExpr:
|
||||
retval = _copyArrayExpr(from);
|
||||
break;
|
||||
case T_RowExpr:
|
||||
retval = _copyRowExpr(from);
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
retval = _copyCoalesceExpr(from);
|
||||
break;
|
||||
|
@ -18,7 +18,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* 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)
|
||||
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
|
||||
_equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b)
|
||||
{
|
||||
@ -1748,6 +1766,9 @@ equal(void *a, void *b)
|
||||
case T_ArrayExpr:
|
||||
retval = _equalArrayExpr(a, b);
|
||||
break;
|
||||
case T_RowExpr:
|
||||
retval = _equalRowExpr(a, b);
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
retval = _equalCoalesceExpr(a, b);
|
||||
break;
|
||||
|
@ -9,12 +9,13 @@
|
||||
*
|
||||
*
|
||||
* 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 "catalog/pg_type.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
@ -170,6 +171,17 @@ makeNullConst(Oid consttype)
|
||||
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 -
|
||||
* creates a BoolExpr node
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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
|
||||
* Every node type that can appear in stored rules' parsetrees *must*
|
||||
@ -828,6 +828,16 @@ _outArrayExpr(StringInfo str, ArrayExpr *node)
|
||||
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
|
||||
_outCoalesceExpr(StringInfo str, CoalesceExpr *node)
|
||||
{
|
||||
@ -1719,6 +1729,9 @@ _outNode(StringInfo str, void *obj)
|
||||
case T_ArrayExpr:
|
||||
_outArrayExpr(str, obj);
|
||||
break;
|
||||
case T_RowExpr:
|
||||
_outRowExpr(str, obj);
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
_outCoalesceExpr(str, obj);
|
||||
break;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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
|
||||
* Path and Plan nodes do not have any readfuncs support, because we
|
||||
@ -628,6 +628,21 @@ _readArrayExpr(void)
|
||||
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
|
||||
*/
|
||||
@ -978,6 +993,8 @@ parseNodeString(void)
|
||||
return_value = _readCaseTestExpr();
|
||||
else if (MATCH("ARRAY", 5))
|
||||
return_value = _readArrayExpr();
|
||||
else if (MATCH("ROW", 3))
|
||||
return_value = _readRowExpr();
|
||||
else if (MATCH("COALESCE", 8))
|
||||
return_value = _readCoalesceExpr();
|
||||
else if (MATCH("NULLIFEXPR", 10))
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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,
|
||||
bool *differentTypes);
|
||||
static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
|
||||
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,
|
||||
Index rti, Node *qual);
|
||||
RangeTblEntry *rte, Index rti, Node *qual);
|
||||
|
||||
|
||||
/*
|
||||
@ -375,7 +376,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
|
||||
if (qual_is_pushdown_safe(subquery, rti, clause, differentTypes))
|
||||
{
|
||||
/* Push it down */
|
||||
subquery_push_qual(subquery, rti, clause);
|
||||
subquery_push_qual(subquery, rte, rti, clause);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -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
|
||||
*/
|
||||
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)
|
||||
{
|
||||
/* 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
|
||||
{
|
||||
@ -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,
|
||||
* each component query gets its own copy of the qual.
|
||||
*/
|
||||
qual = ResolveNew(qual, rti, 0,
|
||||
qual = ResolveNew(qual, rti, 0, rte,
|
||||
subquery->targetList,
|
||||
CMD_SELECT, 0);
|
||||
subquery->havingQual = make_and_qual(subquery->havingQual,
|
||||
@ -816,23 +817,23 @@ subquery_push_qual(Query *subquery, Index rti, Node *qual)
|
||||
*/
|
||||
static void
|
||||
recurse_push_qual(Node *setOp, Query *topquery,
|
||||
Index rti, Node *qual)
|
||||
RangeTblEntry *rte, Index rti, Node *qual)
|
||||
{
|
||||
if (IsA(setOp, RangeTblRef))
|
||||
{
|
||||
RangeTblRef *rtr = (RangeTblRef *) setOp;
|
||||
RangeTblEntry *rte = rt_fetch(rtr->rtindex, topquery->rtable);
|
||||
Query *subquery = rte->subquery;
|
||||
RangeTblEntry *subrte = rt_fetch(rtr->rtindex, topquery->rtable);
|
||||
Query *subquery = subrte->subquery;
|
||||
|
||||
Assert(subquery != NULL);
|
||||
subquery_push_qual(subquery, rti, qual);
|
||||
subquery_push_qual(subquery, rte, rti, qual);
|
||||
}
|
||||
else if (IsA(setOp, SetOperationStmt))
|
||||
{
|
||||
SetOperationStmt *op = (SetOperationStmt *) setOp;
|
||||
|
||||
recurse_push_qual(op->larg, topquery, rti, qual);
|
||||
recurse_push_qual(op->rarg, topquery, rti, qual);
|
||||
recurse_push_qual(op->larg, topquery, rte, rti, qual);
|
||||
recurse_push_qual(op->rarg, topquery, rte, rti, qual);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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 @@
|
||||
#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
|
||||
* clause pairs in clauselist_selectivity.
|
||||
@ -486,7 +481,7 @@ clause_selectivity(Query *root,
|
||||
s1 = restriction_selectivity(root,
|
||||
BooleanEqualOperator,
|
||||
makeList2(var,
|
||||
MAKEBOOLCONST(true,
|
||||
makeBoolConst(true,
|
||||
false)),
|
||||
varRelid);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
*
|
||||
*
|
||||
* 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
|
||||
|
||||
static bool is_simple_subquery(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 void reduce_outer_joins_pass2(Node *jtnode,
|
||||
reduce_outer_joins_state *state,
|
||||
@ -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-
|
||||
* variables evaluated at the right place in the modified plan
|
||||
* 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) &&
|
||||
(!below_outer_join || has_nullable_targetlist(subquery)) &&
|
||||
!contain_whole_tuple_var((Node *) parse, varno, 0))
|
||||
if (rte->rtekind == RTE_SUBQUERY &&
|
||||
is_simple_subquery(subquery) &&
|
||||
(!below_outer_join || has_nullable_targetlist(subquery)))
|
||||
{
|
||||
int rtoffset;
|
||||
List *subtlist;
|
||||
@ -206,8 +203,7 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
|
||||
* the one above.
|
||||
*/
|
||||
if (is_simple_subquery(subquery) &&
|
||||
(!below_outer_join || has_nullable_targetlist(subquery)) &&
|
||||
!contain_whole_tuple_var((Node *) parse, varno, 0))
|
||||
(!below_outer_join || has_nullable_targetlist(subquery)))
|
||||
{
|
||||
/* good to go */
|
||||
}
|
||||
@ -247,24 +243,25 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
|
||||
subtlist = subquery->targetList;
|
||||
parse->targetList = (List *)
|
||||
ResolveNew((Node *) parse->targetList,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
resolvenew_in_jointree((Node *) parse->jointree, varno, subtlist);
|
||||
varno, 0, rte, subtlist, CMD_SELECT, 0);
|
||||
resolvenew_in_jointree((Node *) parse->jointree, varno,
|
||||
rte, subtlist);
|
||||
Assert(parse->setOperations == NULL);
|
||||
parse->havingQual =
|
||||
ResolveNew(parse->havingQual,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
varno, 0, rte, subtlist, CMD_SELECT, 0);
|
||||
parse->in_info_list = (List *)
|
||||
ResolveNew((Node *) parse->in_info_list,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
varno, 0, rte, subtlist, CMD_SELECT, 0);
|
||||
|
||||
foreach(rt, parse->rtable)
|
||||
{
|
||||
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
|
||||
RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(rt);
|
||||
|
||||
if (rte->rtekind == RTE_JOIN)
|
||||
rte->joinaliasvars = (List *)
|
||||
ResolveNew((Node *) rte->joinaliasvars,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
if (otherrte->rtekind == RTE_JOIN)
|
||||
otherrte->joinaliasvars = (List *)
|
||||
ResolveNew((Node *) otherrte->joinaliasvars,
|
||||
varno, 0, rte, subtlist, CMD_SELECT, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -479,7 +476,8 @@ has_nullable_targetlist(Query *subquery)
|
||||
* but there's no other way...
|
||||
*/
|
||||
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)
|
||||
return;
|
||||
@ -493,18 +491,18 @@ resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist)
|
||||
List *l;
|
||||
|
||||
foreach(l, f->fromlist)
|
||||
resolvenew_in_jointree(lfirst(l), varno, subtlist);
|
||||
resolvenew_in_jointree(lfirst(l), varno, rte, subtlist);
|
||||
f->quals = ResolveNew(f->quals,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
varno, 0, rte, subtlist, CMD_SELECT, 0);
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
|
||||
resolvenew_in_jointree(j->larg, varno, subtlist);
|
||||
resolvenew_in_jointree(j->rarg, varno, subtlist);
|
||||
resolvenew_in_jointree(j->larg, varno, rte, subtlist);
|
||||
resolvenew_in_jointree(j->rarg, varno, rte, subtlist);
|
||||
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
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -41,10 +41,6 @@
|
||||
#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
|
||||
{
|
||||
int nargs;
|
||||
@ -281,7 +277,7 @@ Expr *
|
||||
make_ands_explicit(List *andclauses)
|
||||
{
|
||||
if (andclauses == NIL)
|
||||
return (Expr *) MAKEBOOLCONST(true, false);
|
||||
return (Expr *) makeBoolConst(true, false);
|
||||
else if (lnext(andclauses) == NIL)
|
||||
return (Expr *) lfirst(andclauses);
|
||||
else
|
||||
@ -484,6 +480,8 @@ expression_returns_set_walker(Node *node, void *context)
|
||||
return false;
|
||||
if (IsA(node, ArrayExpr))
|
||||
return false;
|
||||
if (IsA(node, RowExpr))
|
||||
return false;
|
||||
if (IsA(node, CoalesceExpr))
|
||||
return false;
|
||||
if (IsA(node, NullIfExpr))
|
||||
@ -778,6 +776,8 @@ contain_nonstrict_functions_walker(Node *node, void *context)
|
||||
if (IsA(node, CaseWhen))
|
||||
return true;
|
||||
/* NB: ArrayExpr might someday be nonstrict */
|
||||
if (IsA(node, RowExpr))
|
||||
return true;
|
||||
if (IsA(node, CoalesceExpr))
|
||||
return true;
|
||||
if (IsA(node, NullIfExpr))
|
||||
@ -1030,6 +1030,8 @@ set_coercionform_dontcare_walker(Node *node, void *context)
|
||||
((FuncExpr *) node)->funcformat = COERCE_DONTCARE;
|
||||
if (IsA(node, RelabelType))
|
||||
((RelabelType *) node)->relabelformat = COERCE_DONTCARE;
|
||||
if (IsA(node, RowExpr))
|
||||
((RowExpr *) node)->row_format = COERCE_DONTCARE;
|
||||
if (IsA(node, CoerceToDomain))
|
||||
((CoerceToDomain *) node)->coercionformat = COERCE_DONTCARE;
|
||||
return expression_tree_walker(node, set_coercionform_dontcare_walker,
|
||||
@ -1197,11 +1199,11 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
{
|
||||
/* all nulls? then not distinct */
|
||||
if (all_null_input)
|
||||
return MAKEBOOLCONST(false, false);
|
||||
return makeBoolConst(false, false);
|
||||
|
||||
/* one null? then distinct */
|
||||
if (has_null_input)
|
||||
return MAKEBOOLCONST(true, false);
|
||||
return makeBoolConst(true, false);
|
||||
|
||||
/* otherwise try to evaluate the '=' operator */
|
||||
/* (NOT okay to try to inline it, though!) */
|
||||
@ -1272,12 +1274,12 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
newargs = simplify_or_arguments(args,
|
||||
&haveNull, &forceTrue);
|
||||
if (forceTrue)
|
||||
return MAKEBOOLCONST(true, false);
|
||||
return makeBoolConst(true, false);
|
||||
if (haveNull)
|
||||
newargs = lappend(newargs, MAKEBOOLCONST(false, true));
|
||||
newargs = lappend(newargs, makeBoolConst(false, true));
|
||||
/* If all the inputs are FALSE, result is FALSE */
|
||||
if (newargs == NIL)
|
||||
return MAKEBOOLCONST(false, false);
|
||||
return makeBoolConst(false, false);
|
||||
/* If only one nonconst-or-NULL input, it's the result */
|
||||
if (lnext(newargs) == NIL)
|
||||
return (Node *) lfirst(newargs);
|
||||
@ -1293,12 +1295,12 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
newargs = simplify_and_arguments(args,
|
||||
&haveNull, &forceFalse);
|
||||
if (forceFalse)
|
||||
return MAKEBOOLCONST(false, false);
|
||||
return makeBoolConst(false, false);
|
||||
if (haveNull)
|
||||
newargs = lappend(newargs, MAKEBOOLCONST(false, true));
|
||||
newargs = lappend(newargs, makeBoolConst(false, true));
|
||||
/* If all the inputs are TRUE, result is TRUE */
|
||||
if (newargs == NIL)
|
||||
return MAKEBOOLCONST(true, false);
|
||||
return makeBoolConst(true, false);
|
||||
/* If only one nonconst-or-NULL input, it's the result */
|
||||
if (lnext(newargs) == NIL)
|
||||
return (Node *) lfirst(newargs);
|
||||
@ -1313,9 +1315,9 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
|
||||
/* NOT NULL => NULL */
|
||||
if (const_input->constisnull)
|
||||
return MAKEBOOLCONST(false, true);
|
||||
return makeBoolConst(false, true);
|
||||
/* otherwise pretty easy */
|
||||
return MAKEBOOLCONST(!DatumGetBool(const_input->constvalue),
|
||||
return makeBoolConst(!DatumGetBool(const_input->constvalue),
|
||||
false);
|
||||
}
|
||||
else if (not_clause((Node *) lfirst(args)))
|
||||
@ -1387,7 +1389,6 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
}
|
||||
if (IsA(node, CaseExpr))
|
||||
{
|
||||
|
||||
/*----------
|
||||
* CASE expressions can be simplified if there are constant
|
||||
* condition clauses:
|
||||
@ -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
|
||||
* simple Var. (This case won't be generated directly by the
|
||||
* parser, because ParseComplexProjection short-circuits it. But
|
||||
* it can arise while simplifying functions.) If the argument
|
||||
* isn't a whole-row Var, just fall through to do generic
|
||||
* processing.
|
||||
* it can arise while simplifying functions.) Also, we can
|
||||
* optimize field selection from a RowExpr construct.
|
||||
*/
|
||||
FieldSelect *fselect = (FieldSelect *) node;
|
||||
Var *argvar = (Var *) fselect->arg;
|
||||
FieldSelect *newfselect;
|
||||
Node *arg;
|
||||
|
||||
if (argvar && IsA(argvar, Var) &&
|
||||
argvar->varattno == InvalidAttrNumber)
|
||||
arg = eval_const_expressions_mutator((Node *) fselect->arg,
|
||||
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->resulttype,
|
||||
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,
|
||||
bool has_null_input = false;
|
||||
List *arg;
|
||||
FuncExpr *newexpr;
|
||||
char result_typtype;
|
||||
|
||||
/*
|
||||
* Can't simplify if it returns a set.
|
||||
@ -1796,15 +1812,6 @@ evaluate_function(Oid funcid, Oid result_type, List *args,
|
||||
has_nonconst_input)
|
||||
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.
|
||||
*
|
||||
@ -1850,7 +1857,6 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
||||
HeapTuple func_tuple, List *active_fns)
|
||||
{
|
||||
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
|
||||
char result_typtype;
|
||||
bool polymorphic = false;
|
||||
Oid argtypes[FUNC_MAX_ARGS];
|
||||
char *src;
|
||||
@ -1877,21 +1883,6 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
||||
funcform->pronargs != length(args))
|
||||
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 */
|
||||
if (oidMember(funcid, active_fns))
|
||||
return NULL;
|
||||
@ -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
|
||||
* can finger the function that bad information came from.
|
||||
@ -2483,6 +2478,8 @@ expression_tree_walker(Node *node,
|
||||
break;
|
||||
case T_ArrayExpr:
|
||||
return walker(((ArrayExpr *) node)->elements, context);
|
||||
case T_RowExpr:
|
||||
return walker(((RowExpr *) node)->args, context);
|
||||
case T_CoalesceExpr:
|
||||
return walker(((CoalesceExpr *) node)->args, context);
|
||||
case T_NullIfExpr:
|
||||
@ -2889,6 +2886,16 @@ expression_tree_mutator(Node *node,
|
||||
return (Node *) newnode;
|
||||
}
|
||||
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:
|
||||
{
|
||||
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
* 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)
|
||||
* flatten_join_alias_vars
|
||||
* Replace Vars that reference JOIN outputs with references to the original
|
||||
* 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
|
||||
* to be applied directly to a Query node.
|
||||
@ -520,8 +510,39 @@ flatten_join_alias_vars_mutator(Node *node,
|
||||
rte = rt_fetch(var->varno, context->root->rtable);
|
||||
if (rte->rtekind != RTE_JOIN)
|
||||
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);
|
||||
/* Okay, must expand it */
|
||||
newvar = (Node *) nth(var->varattno - 1, rte->joinaliasvars);
|
||||
|
||||
/*
|
||||
|
@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* 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
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -80,11 +80,9 @@ static Node *makeStringConst(char *str, TypeName *typename);
|
||||
static Node *makeIntConst(int val);
|
||||
static Node *makeFloatConst(char *str);
|
||||
static Node *makeAConst(Value *v);
|
||||
static Node *makeRowExpr(List *opr, List *largs, List *rargs);
|
||||
static Node *makeDistinctExpr(List *largs, List *rargs);
|
||||
static Node *makeRowNullTest(NullTestType test, List *args);
|
||||
static Node *makeRowNullTest(NullTestType test, RowExpr *row);
|
||||
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 List *extractArgTypes(List *parameters);
|
||||
static SelectStmt *findLeftmostSelect(SelectStmt *node);
|
||||
@ -277,9 +275,9 @@ static void doNegateFloat(Value *v);
|
||||
%type <node> columnDef
|
||||
%type <defelt> def_elem
|
||||
%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
|
||||
%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 <list> when_clause_list
|
||||
%type <ival> sub_type
|
||||
@ -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
|
||||
* This is the heart of the expression syntax.
|
||||
@ -6045,33 +5886,57 @@ a_expr: c_expr { $$ = $1; }
|
||||
* a NOTNULL
|
||||
*/
|
||||
| a_expr ISNULL
|
||||
{
|
||||
if (IsA($1, RowExpr))
|
||||
$$ = makeRowNullTest(IS_NULL, (RowExpr *) $1);
|
||||
else
|
||||
{
|
||||
NullTest *n = makeNode(NullTest);
|
||||
n->arg = (Expr *) $1;
|
||||
n->nulltesttype = IS_NULL;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
}
|
||||
| a_expr IS NULL_P
|
||||
{
|
||||
if (IsA($1, RowExpr))
|
||||
$$ = makeRowNullTest(IS_NULL, (RowExpr *) $1);
|
||||
else
|
||||
{
|
||||
NullTest *n = makeNode(NullTest);
|
||||
n->arg = (Expr *) $1;
|
||||
n->nulltesttype = IS_NULL;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
}
|
||||
| a_expr NOTNULL
|
||||
{
|
||||
if (IsA($1, RowExpr))
|
||||
$$ = makeRowNullTest(IS_NOT_NULL, (RowExpr *) $1);
|
||||
else
|
||||
{
|
||||
NullTest *n = makeNode(NullTest);
|
||||
n->arg = (Expr *) $1;
|
||||
n->nulltesttype = IS_NOT_NULL;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
}
|
||||
| a_expr IS NOT NULL_P
|
||||
{
|
||||
if (IsA($1, RowExpr))
|
||||
$$ = makeRowNullTest(IS_NOT_NULL, (RowExpr *) $1);
|
||||
else
|
||||
{
|
||||
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
|
||||
{
|
||||
BooleanTest *b = makeNode(BooleanTest);
|
||||
@ -6115,7 +5980,9 @@ a_expr: c_expr { $$ = $1; }
|
||||
$$ = (Node *)b;
|
||||
}
|
||||
| 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
|
||||
{
|
||||
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5);
|
||||
@ -6143,6 +6010,9 @@ a_expr: c_expr { $$ = $1; }
|
||||
{
|
||||
SubLink *n = (SubLink *)$3;
|
||||
n->subLinkType = ANY_SUBLINK;
|
||||
if (IsA($1, RowExpr))
|
||||
n->lefthand = ((RowExpr *) $1)->args;
|
||||
else
|
||||
n->lefthand = makeList1($1);
|
||||
n->operName = makeList1(makeString("="));
|
||||
$$ = (Node *)n;
|
||||
@ -6171,6 +6041,9 @@ a_expr: c_expr { $$ = $1; }
|
||||
/* Make an IN node */
|
||||
SubLink *n = (SubLink *)$4;
|
||||
n->subLinkType = ANY_SUBLINK;
|
||||
if (IsA($1, RowExpr))
|
||||
n->lefthand = ((RowExpr *) $1)->args;
|
||||
else
|
||||
n->lefthand = makeList1($1);
|
||||
n->operName = makeList1(makeString("="));
|
||||
/* Stick a NOT on top */
|
||||
@ -6196,6 +6069,9 @@ a_expr: c_expr { $$ = $1; }
|
||||
{
|
||||
SubLink *n = makeNode(SubLink);
|
||||
n->subLinkType = $3;
|
||||
if (IsA($1, RowExpr))
|
||||
n->lefthand = ((RowExpr *) $1)->args;
|
||||
else
|
||||
n->lefthand = makeList1($1);
|
||||
n->operName = $2;
|
||||
n->subselect = $4;
|
||||
@ -6223,8 +6099,6 @@ a_expr: c_expr { $$ = $1; }
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("UNIQUE predicate is not yet implemented")));
|
||||
}
|
||||
| r_expr
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
/*
|
||||
@ -6277,7 +6151,9 @@ b_expr: c_expr
|
||||
| b_expr qual_Op %prec POSTFIXOP
|
||||
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL); }
|
||||
| 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
|
||||
{
|
||||
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5);
|
||||
@ -6819,12 +6695,86 @@ c_expr: columnref { $$ = (Node *) $1; }
|
||||
}
|
||||
| ARRAY array_expr
|
||||
{ $$ = $2; }
|
||||
| row
|
||||
{
|
||||
RowExpr *r = makeNode(RowExpr);
|
||||
r->args = $1;
|
||||
r->row_typeid = InvalidOid; /* not analyzed yet */
|
||||
$$ = (Node *)r;
|
||||
}
|
||||
;
|
||||
|
||||
/*
|
||||
* 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 '[' a_expr ']'
|
||||
{
|
||||
@ -7358,11 +7308,11 @@ AexprConst: Iconst
|
||||
}
|
||||
| TRUE_P
|
||||
{
|
||||
$$ = (Node *)makeBoolConst(TRUE);
|
||||
$$ = (Node *)makeBoolAConst(TRUE);
|
||||
}
|
||||
| FALSE_P
|
||||
{
|
||||
$$ = (Node *)makeBoolConst(FALSE);
|
||||
$$ = (Node *)makeBoolAConst(FALSE);
|
||||
}
|
||||
| NULL_P
|
||||
{
|
||||
@ -7892,11 +7842,11 @@ makeDefElem(char *name, Node *arg)
|
||||
return f;
|
||||
}
|
||||
|
||||
/* makeBoolConst()
|
||||
/* makeBoolAConst()
|
||||
* Create an A_Const node and initialize to a boolean constant.
|
||||
*/
|
||||
static A_Const *
|
||||
makeBoolConst(bool state)
|
||||
makeBoolAConst(bool state)
|
||||
{
|
||||
A_Const *n = makeNode(A_Const);
|
||||
n->val.type = T_String;
|
||||
@ -7905,119 +7855,41 @@ makeBoolConst(bool state)
|
||||
return n;
|
||||
}
|
||||
|
||||
/* makeRowExpr()
|
||||
* Generate separate operator nodes for a single row descriptor expression.
|
||||
* Perhaps this should go deeper in the parser someday...
|
||||
* - thomas 1997-12-22
|
||||
*/
|
||||
static Node *
|
||||
makeRowExpr(List *opr, List *largs, List *rargs)
|
||||
{
|
||||
Node *expr = NULL;
|
||||
Node *larg, *rarg;
|
||||
char *oprname;
|
||||
|
||||
if (length(largs) != length(rargs))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
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));
|
||||
|
||||
if ((strcmp(oprname, "=") == 0) ||
|
||||
(strcmp(oprname, "<") == 0) ||
|
||||
(strcmp(oprname, "<=") == 0) ||
|
||||
(strcmp(oprname, ">") == 0) ||
|
||||
(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
|
||||
expr = (Node *) makeA_Expr(AEXPR_OR, NIL, expr,
|
||||
(Node *) makeA_Expr(AEXPR_OP, opr,
|
||||
larg, rarg));
|
||||
}
|
||||
else
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("operator %s is not supported for row expressions",
|
||||
oprname)));
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* Eventually this should be eliminated in favor of making the NullTest
|
||||
* node type capable of handling it directly.
|
||||
*/
|
||||
static Node *
|
||||
makeRowNullTest(NullTestType test, List *args)
|
||||
makeRowNullTest(NullTestType test, RowExpr *row)
|
||||
{
|
||||
Node *result = NULL;
|
||||
List *arg;
|
||||
|
||||
foreach(arg, row->args)
|
||||
{
|
||||
Node *expr = NULL;
|
||||
NullTest *n;
|
||||
|
||||
if (lnext(args) != NIL)
|
||||
expr = makeRowNullTest(test, lnext(args));
|
||||
|
||||
n = makeNode(NullTest);
|
||||
n->arg = (Expr *) lfirst(args);
|
||||
n->arg = (Expr *) lfirst(arg);
|
||||
n->nulltesttype = test;
|
||||
|
||||
if (expr == NULL)
|
||||
expr = (Node *) n;
|
||||
if (result == NULL)
|
||||
result = (Node *) n;
|
||||
else if (test == IS_NOT_NULL)
|
||||
expr = (Node *) makeA_Expr(AEXPR_OR, NIL, expr, (Node *)n);
|
||||
result = (Node *) makeA_Expr(AEXPR_OR, NIL, result, (Node *)n);
|
||||
else
|
||||
expr = (Node *) makeA_Expr(AEXPR_AND, NIL, expr, (Node *)n);
|
||||
result = (Node *) makeA_Expr(AEXPR_AND, NIL, result, (Node *)n);
|
||||
}
|
||||
|
||||
return expr;
|
||||
if (result == NULL)
|
||||
{
|
||||
/* zero-length rows? Generate constant TRUE or FALSE */
|
||||
result = (Node *) makeBoolAConst(test == IS_NULL);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* makeOverlaps()
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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 @@
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/params.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_func.h"
|
||||
#include "parser/parse_relation.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "utils/typcache.h"
|
||||
|
||||
|
||||
static Node *coerce_type_typmod(Node *node,
|
||||
Oid targetTypeId, int32 targetTypMod,
|
||||
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,
|
||||
}
|
||||
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))
|
||||
{
|
||||
/*
|
||||
@ -359,6 +373,14 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
|
||||
&funcId))
|
||||
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
|
||||
*/
|
||||
@ -506,6 +528,103 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
|
||||
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 an argument of a construct that requires boolean input
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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 @@
|
||||
|
||||
bool Transform_null_equals = false;
|
||||
|
||||
static Node *typecast_expression(ParseState *pstate, Node *expr,
|
||||
TypeName *typename);
|
||||
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
|
||||
static Node *transformWholeRowRef(ParseState *pstate, char *schemaname,
|
||||
char *relname);
|
||||
static Node *transformIndirection(ParseState *pstate, Node *basenode,
|
||||
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)
|
||||
{
|
||||
case AEXPR_OP:
|
||||
{
|
||||
Node *lexpr = a->lexpr;
|
||||
Node *rexpr = a->rexpr;
|
||||
|
||||
/*
|
||||
* Special-case "foo = NULL" and "NULL = foo"
|
||||
* for compatibility with standards-broken
|
||||
@ -211,27 +220,51 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
if (Transform_null_equals &&
|
||||
length(a->name) == 1 &&
|
||||
strcmp(strVal(lfirst(a->name)), "=") == 0 &&
|
||||
(exprIsNullConstant(a->lexpr) ||
|
||||
exprIsNullConstant(a->rexpr)))
|
||||
(exprIsNullConstant(lexpr) ||
|
||||
exprIsNullConstant(rexpr)))
|
||||
{
|
||||
NullTest *n = makeNode(NullTest);
|
||||
|
||||
n->nulltesttype = IS_NULL;
|
||||
|
||||
if (exprIsNullConstant(a->lexpr))
|
||||
n->arg = (Expr *) a->rexpr;
|
||||
if (exprIsNullConstant(lexpr))
|
||||
n->arg = (Expr *) rexpr;
|
||||
else
|
||||
n->arg = (Expr *) a->lexpr;
|
||||
n->arg = (Expr *) lexpr;
|
||||
|
||||
result = transformExpr(pstate,
|
||||
(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
|
||||
{
|
||||
Node *lexpr = transformExpr(pstate,
|
||||
a->lexpr);
|
||||
Node *rexpr = transformExpr(pstate,
|
||||
a->rexpr);
|
||||
/* Ordinary scalar operator */
|
||||
lexpr = transformExpr(pstate, lexpr);
|
||||
rexpr = transformExpr(pstate, rexpr);
|
||||
|
||||
result = (Node *) make_op(pstate,
|
||||
a->name,
|
||||
@ -311,25 +344,27 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
break;
|
||||
case AEXPR_DISTINCT:
|
||||
{
|
||||
Node *lexpr = transformExpr(pstate,
|
||||
a->lexpr);
|
||||
Node *rexpr = transformExpr(pstate,
|
||||
a->rexpr);
|
||||
Node *lexpr = a->lexpr;
|
||||
Node *rexpr = a->rexpr;
|
||||
|
||||
result = (Node *) make_op(pstate,
|
||||
if (lexpr && IsA(lexpr, RowExpr) &&
|
||||
rexpr && IsA(rexpr, RowExpr))
|
||||
{
|
||||
/* "row op row" */
|
||||
result = make_row_distinct_op(pstate, a->name,
|
||||
lexpr, rexpr);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Ordinary scalar operator */
|
||||
lexpr = transformExpr(pstate, lexpr);
|
||||
rexpr = transformExpr(pstate, rexpr);
|
||||
|
||||
result = (Node *) make_distinct_op(pstate,
|
||||
a->name,
|
||||
lexpr,
|
||||
rexpr);
|
||||
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);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AEXPR_NULLIF:
|
||||
@ -787,6 +822,32 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
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:
|
||||
{
|
||||
CoalesceExpr *c = (CoalesceExpr *) expr;
|
||||
@ -1113,14 +1174,11 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
/*
|
||||
* 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
|
||||
* 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
|
||||
* 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 *
|
||||
transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname)
|
||||
@ -1185,12 +1243,10 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname)
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* RTE is a join or subselect. For the moment we represent this
|
||||
* as a whole-row Var of RECORD type, but this will not actually
|
||||
* work; need a row-constructor expression instead.
|
||||
*
|
||||
* XXX after fixing, be sure that unknown_attribute still
|
||||
* does the right thing.
|
||||
* RTE is a join or subselect. We represent this as a whole-row
|
||||
* Var of RECORD type. (Note that in most cases the Var will
|
||||
* be expanded to a RowExpr during planning, but that is not
|
||||
* our concern here.)
|
||||
*/
|
||||
result = (Node *) makeVar(vnum,
|
||||
InvalidAttrNumber,
|
||||
@ -1266,8 +1322,8 @@ exprType(Node *expr)
|
||||
if (sublink->subLinkType == EXPR_SUBLINK)
|
||||
type = tent->resdom->restype;
|
||||
else
|
||||
/* ARRAY_SUBLINK */
|
||||
{
|
||||
/* ARRAY_SUBLINK */
|
||||
type = get_array_type(tent->resdom->restype);
|
||||
if (!OidIsValid(type))
|
||||
ereport(ERROR,
|
||||
@ -1305,8 +1361,8 @@ exprType(Node *expr)
|
||||
if (subplan->subLinkType == EXPR_SUBLINK)
|
||||
type = tent->resdom->restype;
|
||||
else
|
||||
/* ARRAY_SUBLINK */
|
||||
{
|
||||
/* ARRAY_SUBLINK */
|
||||
type = get_array_type(tent->resdom->restype);
|
||||
if (!OidIsValid(type))
|
||||
ereport(ERROR,
|
||||
@ -1340,6 +1396,9 @@ exprType(Node *expr)
|
||||
case T_ArrayExpr:
|
||||
type = ((ArrayExpr *) expr)->array_typeid;
|
||||
break;
|
||||
case T_RowExpr:
|
||||
type = ((RowExpr *) expr)->row_typeid;
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
type = ((CoalesceExpr *) expr)->coalescetype;
|
||||
break;
|
||||
@ -1573,3 +1632,166 @@ typecast_expression(ParseState *pstate, Node *expr, TypeName *typename)
|
||||
|
||||
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 @@
|
||||
*
|
||||
*
|
||||
* 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)
|
||||
/* make ARRAY[] act like a function */
|
||||
*name = "array";
|
||||
return 2;
|
||||
case T_RowExpr:
|
||||
/* make ROW() act like a function */
|
||||
*name = "row";
|
||||
return 2;
|
||||
case T_CoalesceExpr:
|
||||
/* make coalesce() act like a regular function */
|
||||
*name = "coalesce";
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* 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,
|
||||
sub_action = (Query *) ResolveNew((Node *) sub_action,
|
||||
new_varno,
|
||||
0,
|
||||
rt_fetch(new_varno,
|
||||
sub_action->rtable),
|
||||
parsetree->targetList,
|
||||
event,
|
||||
current_varno);
|
||||
@ -947,6 +949,7 @@ CopyAndAddInvertedQual(Query *parsetree,
|
||||
new_qual = ResolveNew(new_qual,
|
||||
PRS2_NEW_VARNO,
|
||||
0,
|
||||
rt_fetch(rt_index, parsetree->rtable),
|
||||
parsetree->targetList,
|
||||
event,
|
||||
rt_index);
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* 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)
|
||||
* If not, we either change the unmatched Var's varno to update_varno
|
||||
* (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
|
||||
* 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,
|
||||
@ -861,6 +865,7 @@ typedef struct
|
||||
{
|
||||
int target_varno;
|
||||
int sublevels_up;
|
||||
RangeTblEntry *target_rte;
|
||||
List *targetlist;
|
||||
int event;
|
||||
int update_varno;
|
||||
@ -868,35 +873,19 @@ typedef struct
|
||||
} ResolveNew_context;
|
||||
|
||||
static Node *
|
||||
ResolveNew_mutator(Node *node, ResolveNew_context *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
if (IsA(node, Var))
|
||||
{
|
||||
Var *var = (Var *) node;
|
||||
int this_varno = (int) var->varno;
|
||||
int this_varlevelsup = (int) var->varlevelsup;
|
||||
|
||||
if (this_varno == context->target_varno &&
|
||||
this_varlevelsup == context->sublevels_up)
|
||||
resolve_one_var(Var *var, ResolveNew_context *context)
|
||||
{
|
||||
TargetEntry *tle;
|
||||
|
||||
/* band-aid: don't do the wrong thing with a whole-tuple Var */
|
||||
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)
|
||||
{
|
||||
/* Failed to find column in insert/update tlist */
|
||||
if (context->event == CMD_UPDATE)
|
||||
{
|
||||
/* For update, just change unmatched var's varno */
|
||||
var = (Var *) copyObject(node);
|
||||
var = (Var *) copyObject(var);
|
||||
var->varno = context->update_varno;
|
||||
var->varnoold = context->update_varno;
|
||||
return (Node *) var;
|
||||
@ -913,14 +902,65 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
|
||||
Node *n = copyObject(tle->expr);
|
||||
|
||||
/* Adjust varlevelsup if tlist item is from higher query */
|
||||
if (this_varlevelsup > 0)
|
||||
IncrementVarSublevelsUp(n, this_varlevelsup, 0);
|
||||
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 *
|
||||
ResolveNew_mutator(Node *node, ResolveNew_context *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
if (IsA(node, Var))
|
||||
{
|
||||
Var *var = (Var *) node;
|
||||
int this_varno = (int) var->varno;
|
||||
int this_varlevelsup = (int) var->varlevelsup;
|
||||
|
||||
if (this_varno == context->target_varno &&
|
||||
this_varlevelsup == context->sublevels_up)
|
||||
{
|
||||
if (var->varattno == InvalidAttrNumber)
|
||||
{
|
||||
/* 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++)
|
||||
{
|
||||
Oid vartype;
|
||||
int32 vartypmod;
|
||||
Var *newvar;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
rowexpr = makeNode(RowExpr);
|
||||
rowexpr->args = fields;
|
||||
rowexpr->row_typeid = var->vartype;
|
||||
rowexpr->row_format = COERCE_IMPLICIT_CAST;
|
||||
|
||||
return (Node *) rowexpr;
|
||||
}
|
||||
|
||||
/* Normal case for scalar variable */
|
||||
return resolve_one_var(var, context);
|
||||
}
|
||||
/* otherwise fall through to copy the var normally */
|
||||
}
|
||||
|
||||
@ -948,12 +988,14 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
|
||||
|
||||
Node *
|
||||
ResolveNew(Node *node, int target_varno, int sublevels_up,
|
||||
RangeTblEntry *target_rte,
|
||||
List *targetlist, int event, int update_varno)
|
||||
{
|
||||
ResolveNew_context context;
|
||||
|
||||
context.target_varno = target_varno;
|
||||
context.sublevels_up = sublevels_up;
|
||||
context.target_rte = target_rte;
|
||||
context.targetlist = targetlist;
|
||||
context.event = event;
|
||||
context.update_varno = update_varno;
|
||||
|
@ -3,7 +3,7 @@
|
||||
* back to source text
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
@ -2432,6 +2432,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
|
||||
|
||||
case T_ArrayRef:
|
||||
case T_ArrayExpr:
|
||||
case T_RowExpr:
|
||||
case T_CoalesceExpr:
|
||||
case T_NullIfExpr:
|
||||
case T_Aggref:
|
||||
@ -2528,6 +2529,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
|
||||
case T_BoolExpr: /* lower precedence */
|
||||
case T_ArrayRef: /* other separators */
|
||||
case T_ArrayExpr: /* other separators */
|
||||
case T_RowExpr: /* other separators */
|
||||
case T_CoalesceExpr: /* own parentheses */
|
||||
case T_NullIfExpr: /* other separators */
|
||||
case T_Aggref: /* own parentheses */
|
||||
@ -2574,6 +2576,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
|
||||
}
|
||||
case T_ArrayRef: /* other separators */
|
||||
case T_ArrayExpr: /* other separators */
|
||||
case T_RowExpr: /* other separators */
|
||||
case T_CoalesceExpr: /* own parentheses */
|
||||
case T_NullIfExpr: /* other separators */
|
||||
case T_Aggref: /* own parentheses */
|
||||
@ -2942,10 +2945,8 @@ get_rule_expr(Node *node, deparse_context *context,
|
||||
* arg.fieldname, but most cases where FieldSelect is used
|
||||
* are *not* simple. So, always use parenthesized syntax.
|
||||
*/
|
||||
if (!PRETTY_PAREN(context))
|
||||
appendStringInfoChar(buf, '(');
|
||||
get_rule_expr_paren((Node *) fselect->arg, context, true, node);
|
||||
if (!PRETTY_PAREN(context))
|
||||
appendStringInfoChar(buf, ')');
|
||||
appendStringInfo(buf, ".%s", quote_identifier(fieldname));
|
||||
}
|
||||
@ -3051,6 +3052,33 @@ get_rule_expr(Node *node, deparse_context *context,
|
||||
}
|
||||
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:
|
||||
{
|
||||
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
|
||||
|
@ -37,7 +37,7 @@
|
||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||
* 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 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 200405081
|
||||
#define CATALOG_VERSION_NO 200405101
|
||||
|
||||
#endif
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||
* 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,
|
||||
TupleDesc tupType);
|
||||
extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid);
|
||||
extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid);
|
||||
extern TupleDesc ExecTypeFromExprList(List *exprList);
|
||||
extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg);
|
||||
|
||||
typedef struct TupOutputState
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||
* 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
|
||||
char elemalign; /* typalign of the element type */
|
||||
} ArrayExprState;
|
||||
|
||||
/* ----------------
|
||||
* RowExprState node
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct RowExprState
|
||||
{
|
||||
ExprState xprstate;
|
||||
List *args; /* the arguments */
|
||||
TupleDesc tupdesc; /* descriptor for result tuples */
|
||||
} RowExprState;
|
||||
|
||||
/* ----------------
|
||||
* CoalesceExprState node
|
||||
* ----------------
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||
* 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,
|
||||
|
||||
extern Const *makeNullConst(Oid consttype);
|
||||
|
||||
extern Node *makeBoolConst(bool value, bool isnull);
|
||||
|
||||
extern Expr *makeBoolExpr(BoolExprType boolop, List *args);
|
||||
|
||||
extern Alias *makeAlias(const char *aliasname, List *colnames);
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||
* 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
|
||||
T_CaseWhen,
|
||||
T_CaseTestExpr,
|
||||
T_ArrayExpr,
|
||||
T_RowExpr,
|
||||
T_CoalesceExpr,
|
||||
T_NullIfExpr,
|
||||
T_NullTest,
|
||||
@ -145,6 +146,7 @@ typedef enum NodeTag
|
||||
T_CaseExprState,
|
||||
T_CaseWhenState,
|
||||
T_ArrayExprState,
|
||||
T_RowExprState,
|
||||
T_CoalesceExprState,
|
||||
T_CoerceToDomainState,
|
||||
T_DomainConstraintState,
|
||||
|
@ -10,7 +10,7 @@
|
||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||
* 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
|
||||
bool multidims; /* true if elements are sub-arrays */
|
||||
} 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
|
||||
*/
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||
* 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 @@
|
||||
extern Relids pull_varnos(Node *node);
|
||||
extern bool contain_var_reference(Node *node, int varno, int varattno,
|
||||
int levelsup);
|
||||
extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
|
||||
extern bool contain_var_clause(Node *node);
|
||||
extern bool contain_vars_of_level(Node *node, int levelsup);
|
||||
extern bool contain_vars_above_level(Node *node, int levelsup);
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||
* 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);
|
||||
extern bool checkExprHasSubLink(Node *node);
|
||||
|
||||
extern Node *ResolveNew(Node *node, int target_varno, int sublevels_up,
|
||||
RangeTblEntry *target_rte,
|
||||
List *targetlist, int event, int update_varno);
|
||||
|
||||
#endif /* REWRITEMANIP_H */
|
||||
|
@ -3,7 +3,7 @@
|
||||
* procedural language
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
@ -3752,6 +3752,16 @@ exec_simple_check_node(Node *node)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
case T_RowExpr:
|
||||
{
|
||||
RowExpr *expr = (RowExpr *) node;
|
||||
|
||||
if (!exec_simple_check_node((Node *) expr->args))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
case T_CoalesceExpr:
|
||||
{
|
||||
CoalesceExpr *expr = (CoalesceExpr *) node;
|
||||
|
@ -218,6 +218,17 @@ SELECT hobbies_by_name('basketball');
|
||||
|
||||
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
|
||||
--
|
||||
|
@ -45,7 +45,7 @@ SELECT '' AS four, * FROM DEFAULTEXPR_TBL;
|
||||
-- syntax errors
|
||||
-- test for extraneous comma
|
||||
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, ));
|
||||
^
|
||||
-- this will fail because gram.y uses b_expr not a_expr for defaults,
|
||||
|
@ -686,6 +686,45 @@ SELECT name, overpaid(emp.*) FROM emp;
|
||||
linda | f
|
||||
(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
|
||||
--
|
||||
|
Loading…
x
Reference in New Issue
Block a user