mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
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:
@@ -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
|
||||
*/
|
||||
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 *expr = NULL;
|
||||
NullTest *n;
|
||||
Node *result = NULL;
|
||||
List *arg;
|
||||
|
||||
if (lnext(args) != NIL)
|
||||
expr = makeRowNullTest(test, lnext(args));
|
||||
foreach(arg, row->args)
|
||||
{
|
||||
NullTest *n;
|
||||
|
||||
n = makeNode(NullTest);
|
||||
n->arg = (Expr *) lfirst(args);
|
||||
n->nulltesttype = test;
|
||||
n = makeNode(NullTest);
|
||||
n->arg = (Expr *) lfirst(arg);
|
||||
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);
|
||||
if (result == NULL)
|
||||
result = (Node *) n;
|
||||
else if (test == IS_NOT_NULL)
|
||||
result = (Node *) makeA_Expr(AEXPR_OR, NIL, result, (Node *)n);
|
||||
else
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user