summaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
authorTom Lane2004-05-10 22:44:49 +0000
committerTom Lane2004-05-10 22:44:49 +0000
commit2f63232d30ca64a8f2684af855230f23a701d371 (patch)
treeb7a7707d1ec9edf368780cd3f4a23755527c5884 /src/backend/parser
parent9a939886ac782cfee3cd5fdd1c58689163ed84be (diff)
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.
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/gram.y458
-rw-r--r--src/backend/parser/parse_coerce.c121
-rw-r--r--src/backend/parser/parse_expr.c304
-rw-r--r--src/backend/parser/parse_target.c6
4 files changed, 553 insertions, 336 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 64825893bc1..b9becf91dbb 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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.
@@ -6046,31 +5887,55 @@ a_expr: c_expr { $$ = $1; }
*/
| a_expr ISNULL
{
- NullTest *n = makeNode(NullTest);
- n->arg = (Expr *) $1;
- n->nulltesttype = IS_NULL;
- $$ = (Node *)n;
+ 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
{
- NullTest *n = makeNode(NullTest);
- n->arg = (Expr *) $1;
- n->nulltesttype = IS_NULL;
- $$ = (Node *)n;
+ 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
{
- NullTest *n = makeNode(NullTest);
- n->arg = (Expr *) $1;
- n->nulltesttype = IS_NOT_NULL;
- $$ = (Node *)n;
+ 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
{
- NullTest *n = makeNode(NullTest);
- n->arg = (Expr *) $1;
- n->nulltesttype = IS_NOT_NULL;
- $$ = (Node *)n;
+ 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
{
@@ -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,7 +6010,10 @@ a_expr: c_expr { $$ = $1; }
{
SubLink *n = (SubLink *)$3;
n->subLinkType = ANY_SUBLINK;
- n->lefthand = makeList1($1);
+ if (IsA($1, RowExpr))
+ n->lefthand = ((RowExpr *) $1)->args;
+ else
+ n->lefthand = makeList1($1);
n->operName = makeList1(makeString("="));
$$ = (Node *)n;
}
@@ -6171,7 +6041,10 @@ a_expr: c_expr { $$ = $1; }
/* Make an IN node */
SubLink *n = (SubLink *)$4;
n->subLinkType = ANY_SUBLINK;
- n->lefthand = makeList1($1);
+ if (IsA($1, RowExpr))
+ n->lefthand = ((RowExpr *) $1)->args;
+ else
+ n->lefthand = makeList1($1);
n->operName = makeList1(makeString("="));
/* Stick a NOT on top */
$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, (Node *) n);
@@ -6196,7 +6069,10 @@ a_expr: c_expr { $$ = $1; }
{
SubLink *n = makeNode(SubLink);
n->subLinkType = $3;
- n->lefthand = makeList1($1);
+ if (IsA($1, RowExpr))
+ n->lefthand = ((RowExpr *) $1)->args;
+ else
+ n->lefthand = makeList1($1);
n->operName = $2;
n->subselect = $4;
$$ = (Node *)n;
@@ -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
+/* 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 *
-makeRowExpr(List *opr, List *largs, List *rargs)
+makeRowNullTest(NullTestType test, RowExpr *row)
{
- Node *expr = NULL;
- Node *larg, *rarg;
- char *oprname;
+ Node *result = NULL;
+ List *arg;
- 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);
+ foreach(arg, row->args)
+ {
+ NullTest *n;
- oprname = strVal(llast(opr));
+ n = makeNode(NullTest);
+ n->arg = (Expr *) lfirst(arg);
+ n->nulltesttype = test;
- 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);
+ if (result == NULL)
+ result = (Node *) n;
+ else if (test == IS_NOT_NULL)
+ result = (Node *) makeA_Expr(AEXPR_OR, NIL, result, (Node *)n);
else
- expr = (Node *) makeA_Expr(AEXPR_OR, NIL, expr,
- (Node *) makeA_Expr(AEXPR_OP, opr,
- larg, rarg));
+ result = (Node *) makeA_Expr(AEXPR_AND, NIL, result, (Node *)n);
}
- else
+
+ if (result == NULL)
{
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("operator %s is not supported for row expressions",
- oprname)));
+ /* zero-length rows? Generate constant TRUE or FALSE */
+ result = (Node *) makeBoolAConst(test == IS_NULL);
}
- 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.
- */
-static Node *
-makeRowNullTest(NullTestType test, List *args)
-{
- Node *expr = NULL;
- NullTest *n;
-
- if (lnext(args) != NIL)
- expr = makeRowNullTest(test, lnext(args));
-
- n = makeNode(NullTest);
- n->arg = (Expr *) lfirst(args);
- n->nulltesttype = test;
-
- if (expr == NULL)
- expr = (Node *) n;
- else if (test == IS_NOT_NULL)
- expr = (Node *) makeA_Expr(AEXPR_OR, NIL, expr, (Node *)n);
- else
- expr = (Node *) makeA_Expr(AEXPR_AND, NIL, expr, (Node *)n);
-
- return expr;
+ return result;
}
/* makeOverlaps()
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index a5bff31c89f..c7a8c3c83bb 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -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))
{
/*
@@ -360,6 +374,14 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
continue;
/*
+ * If input is RECORD and target is a composite type, assume
+ * we can coerce (may need tighter checking here)
+ */
+ if (inputTypeId == RECORDOID &&
+ ISCOMPLEX(targetTypeId))
+ continue;
+
+ /*
* If input is a class type that inherits from target, accept
*/
if (typeInheritsFrom(inputTypeId, targetTypeId))
@@ -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
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 2747ec3ed43..d4a8cdcc8b6 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -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,
- a->name,
- lexpr,
- rexpr);
- if (((OpExpr *) result)->opresulttype != BOOLOID)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("IS DISTINCT FROM requires = operator to yield boolean")));
+ 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);
- /*
- * We rely on DistinctExpr and OpExpr being
- * same struct
- */
- NodeSetTag(result, T_DistinctExpr);
+ result = (Node *) make_distinct_op(pstate,
+ a->name,
+ lexpr,
+ rexpr);
+ }
}
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;
+}
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index c9c44ac2389..fbee22b37d3 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -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";