diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 9420627d58cb3f8a8fdd5bc17a14de00a9c3f8e9..58cb72f3811bce7a30c7962dfb3d9d17b13bfe40 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.79 1997/12/16 15:50:54 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.80 1997/12/23 19:47:32 thomas Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -63,6 +63,8 @@ extern List *parsetree; static char *xlateSqlType(char *); static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr); +static Node *makeRowExpr(char *opr, List *largs, List *rargs); +void mapTargetColumns(List *source, List *target); static List *makeConstantList( A_Const *node); static char *FlattenStringList(List *list); static char *fmtId(char *rawid); @@ -111,7 +113,7 @@ Oid param_type(int t); /* used in parse_expr.c */ %type <node> stmt, AddAttrStmt, ClosePortalStmt, - CopyStmt, CreateStmt, CreateSeqStmt, DefineStmt, DestroyStmt, + CopyStmt, CreateStmt, CreateAsStmt, CreateSeqStmt, DefineStmt, DestroyStmt, ExtendStmt, FetchStmt, GrantStmt, CreateTrigStmt, DropTrigStmt, CreatePLangStmt, DropPLangStmt, IndexStmt, ListenStmt, OptimizableStmt, @@ -190,6 +192,10 @@ Oid param_type(int t); /* used in parse_expr.c */ a_expr, a_expr_or_null, AexprConst, in_expr, in_expr_nodes, not_in_expr, not_in_expr_nodes, having_clause +%type <list> row_descriptor, row_list +%type <node> row_expr +%type <list> OptCreateAs, CreateAsList +%type <node> CreateAsElement %type <value> NumConst %type <attr> event_object, attr %type <sortgroupby> groupby @@ -340,6 +346,7 @@ stmt : AddAttrStmt | ClosePortalStmt | CopyStmt | CreateStmt + | CreateAsStmt | CreateSeqStmt | CreatePLangStmt | CreateTrigStmt @@ -984,10 +991,20 @@ constraint_expr: AexprConst { $$ = nconc( $1, lcons( makeString( "AND"), $3)); } | constraint_expr OR constraint_expr { $$ = nconc( $1, lcons( makeString( "OR"), $3)); } + | NOT constraint_expr + { $$ = lcons( makeString( "NOT"), $2); } | Op constraint_expr { $$ = lcons( makeString( $1), $2); } | constraint_expr Op { $$ = lappend( $1, makeString( $2)); } + | constraint_expr ISNULL + { $$ = lappend( $1, makeString( "IS NULL")); } + | constraint_expr IS NULL_P + { $$ = lappend( $1, makeString( "IS NULL")); } + | constraint_expr NOTNULL + { $$ = lappend( $1, makeString( "IS NOT NULL")); } + | constraint_expr IS NOT NULL_P + { $$ = lappend( $1, makeString( "IS NOT NULL")); } | constraint_expr IS TRUE_P { $$ = lappend( $1, makeString( "IS TRUE")); } | constraint_expr IS FALSE_P @@ -1030,6 +1047,45 @@ OptArchiveType: ARCHIVE '=' NONE { } | /*EMPTY*/ { } ; +CreateAsStmt: CREATE TABLE relation_name OptCreateAs AS SubSelect + { + RetrieveStmt *n = makeNode(RetrieveStmt); + SubSelect *s = (SubSelect *)$6; + n->unique = s->unique; + n->targetList = s->targetList; + if ($4 != NIL) + mapTargetColumns($4, n->targetList); + n->into = $3; + n->fromClause = s->fromClause; + n->whereClause = s->whereClause; + n->groupClause = s->groupClause; + n->havingClause = s->havingClause; + n->unionClause = NULL; + n->sortClause = NULL; + $$ = (Node *)n; + } + ; + +OptCreateAs: '(' CreateAsList ')' { $$ = $2; } + | /*EMPTY*/ { $$ = NULL; } + ; + +CreateAsList: CreateAsList ',' CreateAsElement { $$ = lappend($1, $3); } + | CreateAsElement { $$ = lcons($1, NIL); } + ; + +CreateAsElement: ColId + { + ColumnDef *n = makeNode(ColumnDef); + n->colname = $1; + n->typename = NULL; + n->defval = NULL; + n->is_not_null = FALSE; + n->constraints = NULL; + $$ = (Node *)n; + } + ; + /***************************************************************************** * @@ -2227,18 +2283,28 @@ RetrieveStmt: SELECT opt_unique res_target_list2 n->whereClause = $6; n->groupClause = $7; n->havingClause = $8; - n->selectClause = $9; + n->unionClause = $9; n->sortClause = $10; $$ = (Node *)n; } ; -union_clause: UNION opt_union select_list { $$ = $3; } - | /*EMPTY*/ { $$ = NIL; } +union_clause: UNION opt_union select_list + { + SubSelect *n = lfirst($3); + n->unionall = $2; + $$ = $3; + } + | /*EMPTY*/ + { $$ = NIL; } ; select_list: select_list UNION opt_union SubSelect - { $$ = lappend($1, $4); } + { + SubSelect *n = (SubSelect *)$4; + n->unionall = $3; + $$ = lappend($1, $4); + } | SubSelect { $$ = lcons($1, NIL); } ; @@ -2249,6 +2315,7 @@ SubSelect: SELECT opt_unique res_target_list2 { SubSelect *n = makeNode(SubSelect); n->unique = $2; + n->unionall = FALSE; n->targetList = $3; n->fromClause = $4; n->whereClause = $5; @@ -2259,9 +2326,9 @@ SubSelect: SELECT opt_unique res_target_list2 ; result: INTO TABLE relation_name - { $$= $3; } + { $$= $3; } | /*EMPTY*/ - { $$ = NULL; } + { $$ = NULL; } ; opt_union: ALL { $$ = TRUE; } @@ -2490,7 +2557,7 @@ relation_expr: relation_name } | relation_name '*' %prec '=' { - /* inheiritance query */ + /* inheritance query */ $$ = makeNode(RelExpr); $$->relname = $1; $$->inh = TRUE; @@ -2786,12 +2853,61 @@ a_expr_or_null: a_expr n->val.type = T_Null; $$ = (Node *)n; } + ; + +/* Expressions using row descriptors + * Define row_descriptor to allow yacc to break the reduce/reduce conflict + * with singleton expressions. + */ +row_expr: '(' row_descriptor ')' IN '(' SubSelect ')' + { + $$ = NULL; + } + | '(' row_descriptor ')' NOT IN '(' SubSelect ')' + { + $$ = NULL; + } + | '(' row_descriptor ')' '=' '(' row_descriptor ')' + { + $$ = makeRowExpr("=", $2, $6); + } + | '(' row_descriptor ')' '<' '(' row_descriptor ')' + { + $$ = makeRowExpr("<", $2, $6); + } + | '(' row_descriptor ')' '>' '(' row_descriptor ')' + { + $$ = makeRowExpr("<", $2, $6); + } + | '(' row_descriptor ')' Op '(' row_descriptor ')' + { + $$ = makeRowExpr($4, $2, $6); + } + ; + +row_descriptor: row_list ',' a_expr + { + $$ = lappend($1, $3); + } + ; + +row_list: row_list ',' a_expr + { + $$ = lappend($1, $3); + } + | a_expr + { + $$ = lcons($1, NIL); + } + ; a_expr: attr opt_indirection { $1->indirection = $2; $$ = (Node *)$1; } + | row_expr + { $$ = $1; } | AexprConst { $$ = $1; } | '-' a_expr %prec UMINUS @@ -3052,33 +3168,46 @@ a_expr: attr opt_indirection { $$ = makeA_Expr(NOTNULL, NULL, $1, NULL); } | a_expr IS NOT NULL_P { $$ = makeA_Expr(NOTNULL, NULL, $1, NULL); } + /* IS TRUE, IS FALSE, etc used to be function calls + * but let's make them expressions to allow the optimizer + * a chance to eliminate them if a_expr is a constant string. + * - thomas 1997-12-22 + */ | a_expr IS TRUE_P { - FuncCall *n = makeNode(FuncCall); - n->funcname = "istrue"; - n->args = lcons($1,NIL); - $$ = (Node *)n; + A_Const *n = makeNode(A_Const); + n->val.type = T_String; + n->val.val.str = "t"; + n->typename = makeNode(TypeName); + n->typename->name = xlateSqlType("bool"); + $$ = makeA_Expr(OP, "=", $1,(Node *)n); } - | a_expr IS FALSE_P + | a_expr IS NOT FALSE_P { - FuncCall *n = makeNode(FuncCall); - n->funcname = "isfalse"; - n->args = lcons($1,NIL); - $$ = (Node *)n; + A_Const *n = makeNode(A_Const); + n->val.type = T_String; + n->val.val.str = "t"; + n->typename = makeNode(TypeName); + n->typename->name = xlateSqlType("bool"); + $$ = makeA_Expr(OP, "=", $1,(Node *)n); } - | a_expr IS NOT TRUE_P + | a_expr IS FALSE_P { - FuncCall *n = makeNode(FuncCall); - n->funcname = "isfalse"; - n->args = lcons($1,NIL); - $$ = (Node *)n; + A_Const *n = makeNode(A_Const); + n->val.type = T_String; + n->val.val.str = "f"; + n->typename = makeNode(TypeName); + n->typename->name = xlateSqlType("bool"); + $$ = makeA_Expr(OP, "=", $1,(Node *)n); } - | a_expr IS NOT FALSE_P + | a_expr IS NOT TRUE_P { - FuncCall *n = makeNode(FuncCall); - n->funcname = "istrue"; - n->args = lcons($1,NIL); - $$ = (Node *)n; + A_Const *n = makeNode(A_Const); + n->val.type = T_String; + n->val.val.str = "f"; + n->typename = makeNode(TypeName); + n->typename->name = xlateSqlType("bool"); + $$ = makeA_Expr(OP, "=", $1,(Node *)n); } | a_expr BETWEEN AexprConst AND AexprConst { @@ -3547,6 +3676,8 @@ AexprConst: Iconst A_Const *n = makeNode(A_Const); n->val.type = T_String; n->val.val.str = "t"; + n->typename = makeNode(TypeName); + n->typename->name = xlateSqlType("bool"); $$ = (Node *)n; } | FALSE_P @@ -3554,6 +3685,8 @@ AexprConst: Iconst A_Const *n = makeNode(A_Const); n->val.type = T_String; n->val.val.str = "f"; + n->typename = makeNode(TypeName); + n->typename->name = xlateSqlType("bool"); $$ = (Node *)n; } ; @@ -3654,7 +3787,8 @@ SpecialRuleRelation: CURRENT %% -static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr) +static Node * +makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr) { A_Expr *a = makeNode(A_Expr); a->oper = oper; @@ -3664,6 +3798,90 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr) return (Node *)a; } +/* 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(char *opr, List *largs, List *rargs) +{ + Node *expr = NULL; + Node *larg, *rarg; + + if (length(largs) != length(rargs)) + elog(WARN,"Unequal number of entries in row expression",NULL); + + if (lnext(largs) != NIL) + expr = makeRowExpr(opr,lnext(largs),lnext(rargs)); + + larg = lfirst(largs); + rarg = lfirst(rargs); + + if ((strcmp(opr, "=") == 0) + || (strcmp(opr, "<") == 0) + || (strcmp(opr, "<=") == 0) + || (strcmp(opr, ">") == 0) + || (strcmp(opr, ">=") == 0)) + { + if (expr == NULL) + expr = makeA_Expr(OP, opr, larg, rarg); + else + expr = makeA_Expr(AND, NULL, expr, makeA_Expr(OP, opr, larg, rarg)); + } + else if (strcmp(opr, "<>") == 0) + { + if (expr == NULL) + expr = makeA_Expr(OP, opr, larg, rarg); + else + expr = makeA_Expr(OR, NULL, expr, makeA_Expr(OP, opr, larg, rarg)); + } + else + { + elog(WARN,"Operator '%s' not implemented for row expressions",opr); + } + +#if FALSE + while ((largs != NIL) && (rargs != NIL)) + { + larg = lfirst(largs); + rarg = lfirst(rargs); + + if (expr == NULL) + expr = makeA_Expr(OP, opr, larg, rarg); + else + expr = makeA_Expr(AND, NULL, expr, makeA_Expr(OP, opr, larg, rarg)); + + largs = lnext(largs); + rargs = lnext(rargs); + } + pprint(expr); +#endif + + return expr; +} /* makeRowExpr() */ + +void +mapTargetColumns(List *src, List *dst) +{ + ColumnDef *s; + ResTarget *d; + + if (length(src) != length(dst)) + elog(WARN,"CREATE TABLE/AS SELECT has mismatched column count",NULL); + + while ((src != NIL) && (dst != NIL)) + { + s = (ColumnDef *)lfirst(src); + d = (ResTarget *)lfirst(dst); + + d->name = s->colname; + + src = lnext(src); + dst = lnext(dst); + } + + return; +} /* mapTargetColumns() */ static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr) {