diff options
| author | Tom Lane | 2004-05-10 22:44:49 +0000 |
|---|---|---|
| committer | Tom Lane | 2004-05-10 22:44:49 +0000 |
| commit | 2f63232d30ca64a8f2684af855230f23a701d371 (patch) | |
| tree | b7a7707d1ec9edf368780cd3f4a23755527c5884 /src/backend/parser | |
| parent | 9a939886ac782cfee3cd5fdd1c58689163ed84be (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.y | 458 | ||||
| -rw-r--r-- | src/backend/parser/parse_coerce.c | 121 | ||||
| -rw-r--r-- | src/backend/parser/parse_expr.c | 304 | ||||
| -rw-r--r-- | src/backend/parser/parse_target.c | 6 |
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"; |
