Implement CREATE TABLE ... AS SELECT borrowing code from SubSelect
authorThomas G. Lockhart <lockhart@fourpalms.org>
Tue, 23 Dec 1997 19:47:32 +0000 (19:47 +0000)
committerThomas G. Lockhart <lockhart@fourpalms.org>
Tue, 23 Dec 1997 19:47:32 +0000 (19:47 +0000)
 and from SELECT ... INTO ... support code.
Allow NOT, IS NULL, IS NOT NULL in constraints.
Define unionall boolean flag in SubSelect structure.
Implement row descriptors: (a, b, c) = (x, y, z).
Change IS TRUE, IS FALSE, etc. to expressions using "=" rather than
 function calls to istrue() or isfalse() to allow optimization.
Force type for TRUE and FALSE to bool.

src/backend/parser/gram.y

index 9420627d58cb3f8a8fdd5bc17a14de00a9c3f8e9..58cb72f3811bce7a30c7962dfb3d9d17b13bfe40 100644 (file)
@@ -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)
 {