Add SQL92 string handling features (SUBSTRING, TRIM, EXTRACT).
authorThomas G. Lockhart
Mon, 1 Sep 1997 06:00:35 +0000 (06:00 +0000)
committerThomas G. Lockhart
Mon, 1 Sep 1997 06:00:35 +0000 (06:00 +0000)
Add parsing for UNION and outer JOINs.
Implement SQL92 "WITH TIME ZONE".
Allow some reserved words as identifiers and column labels.
Clean up indentation and "orphan spaces and tabs".

src/backend/parser/gram.y

index 61a81a41a261c1f827a84df5a2f57d93f03c998a..f9709ebdac1183d6235a57bc03e3f0254737585d 100644 (file)
@@ -2,15 +2,15 @@
 
 /*#define YYDEBUG 1*/
 /*-------------------------------------------------------------------------
- * 
+ *
  * gram.y--
  *    POSTGRES SQL YACC rules/actions
- * 
+ *
  * Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.40 1997/08/28 05:02:01 vadim Exp $
+ *    $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.41 1997/09/01 06:00:35 thomas Exp $
  *
  * HISTORY
  *    AUTHOR       DATE        MAJOR EVENT
@@ -72,7 +72,7 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
 
 
 %union {
-    double         dval;
+    double     dval;
     int            ival;
     char                chr;
     char       *str;
@@ -96,7 +96,7 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
     A_Indices      *aind;
     ResTarget      *target;
     ParamNo        *paramno;
-   
+
     VersionStmt        *vstmt;
     DefineStmt     *dstmt;
     PurgeStmt      *pstmt;
@@ -104,11 +104,11 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
     AppendStmt     *astmt;
 }
 
-%type    stmt, 
+%type    stmt,
    AddAttrStmt, ClosePortalStmt,
-   CopyStmt, CreateStmt, CreateSeqStmt, DefineStmt, DestroyStmt, 
+   CopyStmt, CreateStmt, CreateSeqStmt, DefineStmt, DestroyStmt,
    ExtendStmt, FetchStmt,  GrantStmt,
-   IndexStmt, MoveStmt, ListenStmt, OptimizableStmt, 
+   IndexStmt, MoveStmt, ListenStmt, OptimizableStmt,
         ProcedureStmt, PurgeStmt,
    RecipeStmt, RemoveAggrStmt, RemoveOperStmt, RemoveFuncStmt, RemoveStmt,
    RenameStmt, RevokeStmt, RuleStmt, TransactionStmt, ViewStmt, LoadStmt,
@@ -116,6 +116,13 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
    ReplaceStmt, AppendStmt, NotifyStmt, DeleteStmt, ClusterStmt,
    ExplainStmt, VariableSetStmt, VariableShowStmt, VariableResetStmt
 
+%type     txname
+%type    SubSelect
+%type     join_clause, join_type, join_outer, join_spec
+%type     join_qual
+
+%type     datetime
+
 %type     relation_name, copy_file_name, copy_delimiter, def_name,
    database_name, access_method_clause, access_method, attr_name,
    class, index_name, name, file_name, recipe_name,
@@ -142,13 +149,21 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
    expr_list, default_expr_list, attrs, res_target_list, res_target_list2,
    def_list, opt_indirection, group_clause, groupby_list
 
-%type     opt_inh_star, opt_binary, opt_instead, opt_with_copy,
+%type    union_clause, select_list
+%type    join_list
+%type     join_using
+
+%type    extract_list, position_list
+%type    substr_list, substr_from, substr_for, trim_list
+%type    interval_opts
+
+%type     opt_inh_star, opt_binary, opt_instead, opt_with_col, opt_with_copy,
        index_opt_unique, opt_verbose, opt_analyze, opt_null
 
-%type    copy_dirn, archive_type, OptArchiveType, OptArchiveLocation, 
+%type    copy_dirn, archive_type, OptArchiveType, OptArchiveLocation,
    def_type, opt_direction, remove_type, opt_column, event
 
-%type    OptLocation, opt_move_where, fetch_how_many 
+%type    OptLocation, opt_move_where, fetch_how_many
 
 %type    OptSeqList
 %type  OptSeqElem
@@ -160,7 +175,7 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
 %type  Typename, typname, opt_type
 %type  columnDef
 %type  def_elem
-%type    def_arg, columnElem, where_clause, 
+%type    def_arg, columnElem, where_clause,
        a_expr, a_expr_or_null, AexprConst,
        default_expr, default_expr_or_null,
        in_expr_nodes, not_in_expr_nodes,
@@ -178,36 +193,40 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
 
 %type    Iconst
 %type     Sconst
-%type     Id, date, var_value
+%type     Id, date, var_value, zone_value
+%type     ColId
 
 
 /*
  * If you make any token changes, remember to:
  * - use "yacc -d" and update parse.h
- *     - update the keyword table in parser/keywords.c
+ * - update the keyword table in parser/keywords.c
  */
 
 /* Keywords */
-%token ABORT_TRANS, ACL, ADD, AFTER, AGGREGATE, ALL, ALTER, ANALYZE, 
-   AND, APPEND, ARCHIVE, ARCH_STORE, AS, ASC, 
-   BACKWARD, BEFORE, BEGIN_TRANS, BETWEEN, BINARY, BY, 
-   CAST, CHANGE, CHECK, CLOSE, CLUSTER, COLUMN, COMMIT, CONSTRAINT, 
-   COPY, CREATE, CURRENT, CURSOR, DATABASE, DECLARE, DEFAULT, DELETE, 
-   DELIMITERS, DESC, DISTINCT, DO, DROP, END_TRANS,
-   EXTEND, FETCH, FOR, FORWARD, FROM, FUNCTION, GRANT, GROUP, 
-   HAVING, HEAVY, IN, INDEX, INHERITS, INSERT, INSTEAD, INTO, IS,
-   ISNULL, LANGUAGE, LIGHT, LISTEN, LOAD, MERGE, MOVE, NEW, 
-   NONE, NOT, NOTHING, NOTIFY, NOTNULL, 
-        OIDS, ON, OPERATOR, OPTION, OR, ORDER, 
-        PNULL, PRIVILEGES, PUBLIC, PURGE, P_TYPE, 
-        RENAME, REPLACE, RESET, RETRIEVE, RETURNS, REVOKE, ROLLBACK, RULE, 
-        SELECT, SET, SETOF, SHOW, STDIN, STDOUT, STORE, 
-   TABLE, TO, TRANSACTION, UNIQUE, UPDATE, USING, VACUUM, VALUES
-   VERBOSE, VERSION, VIEW, WHERE, WITH, WORK
+%token ABORT_TRANS, ACL, ADD, AFTER, AGGREGATE, ALL, ALTER, ANALYZE,
+   AND, APPEND, ARCHIVE, ARCH_STORE, AS, ASC,
+   BACKWARD, BEFORE, BEGIN_TRANS, BETWEEN, BINARY, BOTH, BY,
+   CAST, CHANGE, CHECK, CLOSE, CLUSTER, COLUMN, COMMIT, CONSTRAINT, COPY, CREATE, CROSS,
+   CURRENT, CURSOR, DATABASE, DAYINTERVAL, DECLARE, DEFAULT, DELETE, DELIMITERS, DESC,
+   DISTINCT, DO, DROP, END_TRANS, EXISTS, EXTEND, EXTRACT,
+   FETCH, FOR, FORWARD, FROM, FULL, FUNCTION, GRANT, GROUP,
+   HAVING, HEAVY, HOURINTERVAL,
+   IN, INDEX, INHERITS, INNERJOIN, INSERT, INSTEAD, INTERVAL, INTO, IS, ISNULL,
+   JOIN, LANGUAGE, LEADING, LEFT, LIGHT, LISTEN, LOAD, LOCAL,
+   MERGE, MINUTEINTERVAL, MONTHINTERVAL, MOVE,
+   NATURAL, NEW, NONE, NOT, NOTHING, NOTIFY, NOTNULL,
+   OIDS, ON, OPERATOR, OPTION, OR, ORDER, OUTERJOIN,
+   PNULL, POSITION, PRIVILEGES, PUBLIC, PURGE, P_TYPE,
+   RENAME, REPLACE, RESET, RETRIEVE, RETURNS, REVOKE, RIGHT, ROLLBACK, RULE,
+   SECONDINTERVAL, SELECT, SET, SETOF, SHOW, STDIN, STDOUT, STORE, SUBSTRING,
+   TABLE, TIME, TO, TRAILING, TRANSACTION, TRIM,
+   UNION, UNIQUE, UPDATE, USING, VACUUM, VALUES,
+   VERBOSE, VERSION, VIEW, WHERE, WITH, WORK, YEARINTERVAL, ZONE
 %token EXECUTE, RECIPE, EXPLAIN, LIKE, SEQUENCE
 
 /* Special keywords, not in the query language - see the "lex" file */
-%token    IDENT, SCONST, Op 
+%token    IDENT, SCONST, Op
 %token   ICONST, PARAM
 %token   FCONST
 
@@ -218,7 +237,7 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
 %left  OR
 %left  AND
 %right NOT
-%right     '='
+%right '='
 %nonassoc LIKE
 %nonassoc BETWEEN
 %nonassoc IN
@@ -226,17 +245,18 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
 %nonassoc NOTNULL
 %nonassoc ISNULL
 %nonassoc IS
-%left      '+' '-'
-%left      '*' '/'
+%left  '+' '-'
+%left  '*' '/'
 %left  '|'     /* this is the relation union op, not logical or */
 %right  ':'        /* Unary Operators      */
 %left  ';'     /* end of statement or natural log    */
 %nonassoc  '<' '>'
 %right   UMINUS
 %left  '.'
-%left      '[' ']' 
+%left  '[' ']'
 %nonassoc TYPECAST
 %nonassoc REDUCE
+%left  UNION
 %%
 
 stmtblock: stmtmulti
@@ -264,23 +284,23 @@ stmt :      AddAttrStmt
    | ExtendStmt
    | ExplainStmt
    | FetchStmt
-        | GrantStmt
+   | GrantStmt
    | IndexStmt
    | MoveStmt
-        | ListenStmt
+   | ListenStmt
    | ProcedureStmt
-   | PurgeStmt         
+   | PurgeStmt
    | RecipeStmt
    | RemoveAggrStmt
    | RemoveOperStmt
    | RemoveFuncStmt
    | RemoveStmt
    | RenameStmt
-        | RevokeStmt
-   | OptimizableStmt   
-   | RuleStmt          
+   | RevokeStmt
+   | OptimizableStmt
+   | RuleStmt
    | TransactionStmt
-   | ViewStmt
+   | ViewStmt
    | LoadStmt
    | CreatedbStmt
    | DestroydbStmt
@@ -291,25 +311,43 @@ stmt :      AddAttrStmt
    ;
 
 /*****************************************************************************
- * 
+ *
  * Set PG internal variable
  *    SET var_name TO 'var_value'
- * 
+ *
  *****************************************************************************/
 
-VariableSetStmt: SET var_name TO var_value
+VariableSetStmt:  SET var_name TO var_value
        {
            VariableSetStmt *n = makeNode(VariableSetStmt);
            n->name  = $2;
            n->value = $4;
            $$ = (Node *) n;
        }
+   |  SET var_name '=' var_value
+       {
+           VariableSetStmt *n = makeNode(VariableSetStmt);
+           n->name  = $2;
+           n->value = $4;
+           $$ = (Node *) n;
+       }
+   |  SET TIME ZONE zone_value
+       {
+           VariableSetStmt *n = makeNode(VariableSetStmt);
+           n->name  = "timezone";
+           n->value = $4;
+           $$ = (Node *) n;
+       }
    ;
 
 var_value: Sconst      { $$ = $1; }
    ;
 
-VariableShowStmt: SHOW var_name
+zone_value:  Sconst        { $$ = $1; }
+   | LOCAL         { $$ = NULL; }
+   ;
+
+VariableShowStmt:  SHOW var_name
        {
            VariableShowStmt *n = makeNode(VariableShowStmt);
            n->name  = $2;
@@ -317,7 +355,7 @@ VariableShowStmt: SHOW var_name
        }
    ;
 
-VariableResetStmt: RESET var_name
+VariableResetStmt:  RESET var_name
        {
            VariableResetStmt *n = makeNode(VariableResetStmt);
            n->name  = $2;
@@ -342,13 +380,22 @@ AddAttrStmt:  ALTER TABLE relation_name opt_inh_star ADD COLUMN columnDef
        }
    ;
 
-columnDef:  Id Typename OptDefault opt_null
+/* Column definition might include WITH TIME ZONE, but only for the data types
+ *  called out in SQL92 date/time definitions. So, check explicitly for "timestamp"
+ * and "time". - thomas 1997-07-14
+ */
+columnDef:  Id Typename opt_with_col OptDefault opt_null
        {  
            $$ = makeNode(ColumnDef);
            $$->colname = $1;
            $$->typename = $2;
-           $$->defval = $3;
-           $$->is_not_null = $4;
+           $$->typename->timezone = $3;
+           $$->defval = $4;
+           $$->is_not_null = $5;
+           if ($$->typename->timezone
+            && (strcasecmp($$->typename->name, "timestamp")
+             && strcasecmp($$->typename->name, "time")))
+           elog(NOTICE,"%s does not use WITH TIME ZONE",$$->typename->name);
        }
    ;
 
@@ -457,18 +504,21 @@ default_expr_list: default_expr_or_null
 opt_null: NOT PNULL        { $$ = true; }
    | NOTNULL       { $$ = true; }
    | /* EMPTY */       { $$ = false; }
+   ;
+opt_with_col:  WITH TIME ZONE          { $$ = TRUE; }
+   |  /* EMPTY */              { $$ = FALSE; }
    ;
 
-   
 /*****************************************************************************
- * 
+ *
  * QUERY :
  *     close 
- * 
+ *
  *****************************************************************************/
 
-ClosePortalStmt:  CLOSE opt_id 
-       {  
+ClosePortalStmt:  CLOSE opt_id
+       {
            ClosePortalStmt *n = makeNode(ClosePortalStmt);
            n->portalname = $2;
            $$ = (Node *)n;
@@ -479,7 +529,7 @@ ClosePortalStmt:  CLOSE opt_id
 /*****************************************************************************
  *
  * QUERY :
- *     COPY [BINARY]  FROM/TO 
+ *     COPY [BINARY]  FROM/TO
  *              [USING DELIMITERS ]
  *
  *****************************************************************************/
@@ -497,13 +547,13 @@ CopyStmt:  COPY opt_binary relation_name opt_with_copy copy_dirn copy_file_name
        }
    ;
 
-copy_dirn:  TO 
+copy_dirn:  TO
        { $$ = TO; }
    |  FROM
        { $$ = FROM; }
    ;
 
-/* 
+/*
  * copy_file_name NULL indicates stdio is used. Whether stdin or stdout is
  * used depends on the direction. (It really doesn't make sense to copy from
  * stdout. We silently correct the "typo".  - AY 9/94
@@ -557,9 +607,9 @@ OptTableElementList:  tableElementList      { $$ = $1; }
    ;
 
 tableElementList :
-     tableElementList ',' columnDef    
+     tableElementList ',' columnDef
        { $$ = lappend($1, $3); }
-   | columnDef         
+   | columnDef
        { $$ = lcons($1, NIL); }
    ;
 
@@ -568,20 +618,20 @@ OptArchiveType:  ARCHIVE '=' archive_type     { $$ = $3; }
    | /*EMPTY*/                 { $$ = ARCH_NONE; }
    ;
 
-archive_type:  HEAVY                   { $$ = ARCH_HEAVY; }
-   | LIGHT                     { $$ = ARCH_LIGHT; }
+archive_type:  HEAVY                   { $$ = ARCH_HEAVY; }
+   | LIGHT                     { $$ = ARCH_LIGHT; }
    | NONE                      { $$ = ARCH_NONE; }
    ;
 
 OptLocation:  STORE '=' Sconst
        {  $$ = smgrin($3);  }
-   | /*EMPTY*/             
+   | /*EMPTY*/
        {  $$ = -1;  }
    ;
 
 OptArchiveLocation: ARCH_STORE '=' Sconst
        {  $$ = smgrin($3);  }
-   | /*EMPTY*/             
+   | /*EMPTY*/
        {  $$ = -1;  }
    ;
 
@@ -642,14 +692,14 @@ CreateSeqStmt:  CREATE SEQUENCE relation_name OptSeqList
        }
    ;
 
-OptSeqList:    
+OptSeqList:
        OptSeqList OptSeqElem
            { $$ = lappend($1, $2); }
-   |       { $$ = NIL; }
+   |       { $$ = NIL; }
    ;
 
 OptSeqElem:    IDENT NumConst
-       { 
+       {
            $$ = makeNode(DefElem);
            $$->defname = $1;
            $$->arg = (Node *)$2;
@@ -665,7 +715,7 @@ OptSeqElem: IDENT NumConst
 
 /*****************************************************************************
  *
- *     QUERY :
+ * QUERY :
  *     define (type,operator,aggregate)
  *
  *****************************************************************************/
@@ -677,7 +727,7 @@ DefineStmt:  CREATE def_type def_rest
        }
    ;
 
-def_rest:  def_name definition 
+def_rest:  def_name definition
        {
            $$ = makeNode(DefineStmt);
            $$->defname = $1;
@@ -685,12 +735,12 @@ def_rest:  def_name definition
        }
    ;
 
-def_type:  OPERATOR                { $$ = OPERATOR; }
-   |  Type                 { $$ = P_TYPE; }
+def_type:  OPERATOR                { $$ = OPERATOR; }
+   |  Type                 { $$ = P_TYPE; }
    |  AGGREGATE                { $$ = AGGREGATE; }
    ;
 
-def_name:  Id  |  MathOp |  Op
+def_name:  Id  |  MathOp |  Op
    ;
 
 
@@ -705,7 +755,7 @@ def_list:  def_elem
    ;
 
 def_elem:  def_name '=' def_arg
-       { 
+       {
            $$ = makeNode(DefElem);
            $$->defname = $1;
            $$->arg = (Node *)$3;
@@ -728,33 +778,33 @@ def_elem:  def_name '=' def_arg
 def_arg:  Id           {  $$ = (Node *)makeString($1); }
    | all_Op        {  $$ = (Node *)makeString($1); }
    | NumConst      {  $$ = (Node *)$1; /* already a Value */ }
-       | Sconst        {  $$ = (Node *)makeString($1); }
-   | SETOF Id      { 
+   | Sconst        {  $$ = (Node *)makeString($1); }
+   | SETOF Id      {
                   TypeName *n = makeNode(TypeName);
                   n->name = $2;
                   n->setof = TRUE;
                   n->arrayBounds = NULL;
                   $$ = (Node *)n;
-               }               
+               }
    ;
 
 
 /*****************************************************************************
  *
- * QUERY:  
+ * QUERY:
  *     destroy  [,  ..  ]
  *
  *****************************************************************************/
 
 DestroyStmt:   DROP TABLE relation_name_list
-       { 
+       {
            DestroyStmt *n = makeNode(DestroyStmt);
            n->relNames = $3;
            n->sequence = false;
            $$ = (Node *)n;
        }
    |   DROP SEQUENCE relation_name_list
-       { 
+       {
            DestroyStmt *n = makeNode(DestroyStmt);
            n->relNames = $3;
            n->sequence = true;
@@ -785,7 +835,7 @@ opt_direction:  FORWARD             { $$ = FORWARD; }
    | /*EMPTY*/             { $$ = FORWARD; /* default */ }
    ;
 
-fetch_how_many:  Iconst            
+fetch_how_many:  Iconst
           { $$ = $1;
         if ($1 <= 0) elog(WARN,"Please specify nonnegative count for fetch"); }
    |  ALL              { $$ = 0; /* 0 means fetch all tuples*/}
@@ -800,70 +850,70 @@ fetch_how_many:  Iconst
  *****************************************************************************/
 
 GrantStmt: GRANT privileges ON relation_name_list TO grantee opt_with_grant
-           {
-       $$ = (Node*)makeAclStmt($2,$4,$6,'+');
-       free($2);
-       free($6);
-           }
-           ;
+       {
+           $$ = (Node*)makeAclStmt($2,$4,$6,'+');
+           free($2);
+           free($6);
+       }
+   ;
 
 privileges:  ALL PRIVILEGES
        {
         $$ = aclmakepriv("rwaR",0);
        }
-           | ALL
+   | ALL
        {
         $$ = aclmakepriv("rwaR",0);
        }
-           | operation_commalist {
+   | operation_commalist {
        $$ = $1;
        }
-           ;
+   ;
 
 operation_commalist: operation {
            $$ = aclmakepriv("",$1);
            }
-                   | operation_commalist ',' operation
+   | operation_commalist ',' operation
            {
                $$ = aclmakepriv($1,$3);
                free($1);
            }
-                   ;
+   ;
 
 operation:    SELECT  {
        $$ = ACL_MODE_RD_CHR;
        }
-            | INSERT {
+   | INSERT {
        $$ = ACL_MODE_AP_CHR;
        }
-            | UPDATE {
+   | UPDATE {
        $$ = ACL_MODE_WR_CHR;
        }
-            | DELETE {
+   | DELETE {
        $$ = ACL_MODE_WR_CHR;
        }
        | RULE {
        $$ = ACL_MODE_RU_CHR;
        }
-            ;
+   ;
 
-grantee:      PUBLIC {
+grantee:  PUBLIC {
        $$ = aclmakeuser("A","");
        }
-       | GROUP Id {
+   | GROUP Id {
        $$ = aclmakeuser("G",$2);
        }
-            | Id {
+   | Id {
        $$ = aclmakeuser("U",$1);
-       }
-            ;
+       }
+   ;
 
 opt_with_grant : /* empty */
-            |   WITH GRANT OPTION 
-                {
-                    yyerror("WITH GRANT OPTION is not supported.  Only relation owners can set privileges");
-                }
-            ;
+   |   WITH GRANT OPTION
+       {
+           yyerror("WITH GRANT OPTION is not supported.  Only relation owners can set privileges");
+       }
+   ;
 /*****************************************************************************
  *
  * QUERY:
@@ -872,12 +922,12 @@ opt_with_grant : /* empty */
  *****************************************************************************/
 
 RevokeStmt: REVOKE privileges ON relation_name_list FROM grantee
-              {
-       $$ = (Node*)makeAclStmt($2,$4,$6,'-');
-       free($2);
-       free($6);
-              }
-            ;
+       {
+           $$ = (Node*)makeAclStmt($2,$4,$6,'-');
+           free($2);
+           free($6);
+       }
+   ;
 
 /*****************************************************************************
  *
@@ -887,7 +937,7 @@ RevokeStmt: REVOKE privileges ON relation_name_list FROM grantee
  *****************************************************************************/
 
 MoveStmt:  MOVE opt_direction opt_move_where opt_portal_name
-       { 
+       {
            MoveStmt *n = makeNode(MoveStmt);
            n->direction = $2;
            n->to = FALSE;
@@ -896,7 +946,7 @@ MoveStmt:  MOVE opt_direction opt_move_where opt_portal_name
            $$ = (Node *)n;
        }
    |  MOVE opt_direction TO Iconst opt_portal_name
-       { 
+       {
            MoveStmt *n = makeNode(MoveStmt);
            n->direction = $2;
            n->to = TRUE;
@@ -970,11 +1020,11 @@ ExtendStmt:  EXTEND INDEX index_name where_clause
 /*****************************************************************************
  *
  * QUERY:
- *     execute recipe  
+ *     execute recipe 
  *
  *****************************************************************************/
 
-RecipeStmt:  EXECUTE RECIPE recipe_name 
+RecipeStmt:  EXECUTE RECIPE recipe_name
        {
            RecipeStmt *n;
            if (!IsTransactionBlock())
@@ -991,7 +1041,7 @@ RecipeStmt:  EXECUTE RECIPE recipe_name
  *
  * QUERY:
  *              define function 
- *                     (language = , returntype =  
+ *                     (language = , returntype = 
  *                      [, arch_pct = ]
  *                      [, disk_pct = ]
  *                      [, byte_pct = ]
@@ -1003,9 +1053,9 @@ RecipeStmt:  EXECUTE RECIPE recipe_name
  *
  *****************************************************************************/
 
-ProcedureStmt:  CREATE FUNCTION def_name def_args 
+ProcedureStmt:  CREATE FUNCTION def_name def_args
           RETURNS def_arg opt_with AS Sconst LANGUAGE Sconst
-                {
+       {
            ProcedureStmt *n = makeNode(ProcedureStmt);
            n->funcname = $3;
            n->defArgs = $4;
@@ -1021,10 +1071,10 @@ opt_with:  WITH definition          { $$ = $2; }
    ;
 
 def_args:  '(' def_name_list ')'       { $$ = $2; }
-        |  '(' ')'             { $$ = NIL; }
+   |  '(' ')'              { $$ = NIL; }
    ;
 
-def_name_list:     name_list;  
+def_name_list: name_list;
 
 
 /*****************************************************************************
@@ -1032,43 +1082,43 @@ def_name_list:  name_list;
  * QUERY:
  *     purge  [before ] [after ]
  *       or
- *     purge   [after][before 
- * 
+ *     purge   [after ] [before ]
+ *
  *****************************************************************************/
 
 PurgeStmt:  PURGE relation_name purge_quals
-       { 
+       {
            $3->relname = $2;
            $$ = (Node *)$3;
        }
    ;
 
 purge_quals:  before_clause
-       { 
+       {
            $$ = makeNode(PurgeStmt);
            $$->beforeDate = $1;
            $$->afterDate = NULL;
        }
    |  after_clause
-       { 
+       {
            $$ = makeNode(PurgeStmt);
            $$->beforeDate = NULL;
            $$->afterDate = $1;
        }
    |  before_clause after_clause
-       { 
+       {
            $$ = makeNode(PurgeStmt);
            $$->beforeDate = $1;
            $$->afterDate = $2;
        }
    |  after_clause before_clause
-       { 
+       {
            $$ = makeNode(PurgeStmt);
            $$->beforeDate = $2;
            $$->afterDate = $1;
        }
    |  /*EMPTY*/
-       { 
+       {
            $$ = makeNode(PurgeStmt);
            $$->beforeDate = NULL;
            $$->afterDate = NULL;
@@ -1105,11 +1155,11 @@ RemoveStmt:  DROP remove_type name
        }
    ;
 
-remove_type:  Type                 {  $$ = P_TYPE; }
-   |  INDEX                {  $$ = INDEX; }
-   |  RULE                 {  $$ = RULE; }
+remove_type:  Type             {  $$ = P_TYPE; }
+   |  INDEX                {  $$ = INDEX; }
+   |  RULE                 {  $$ = RULE; }
    |  VIEW                 {  $$ = VIEW; }
-   ;
+   ;
 
 RemoveAggrStmt:  DROP AGGREGATE name aggr_argtype
        {
@@ -1125,16 +1175,16 @@ aggr_argtype:  name             { $$ = $1; }
    ;
 
 RemoveFuncStmt:  DROP FUNCTION name '(' func_argtypes ')'
-                {
+       {
            RemoveFuncStmt *n = makeNode(RemoveFuncStmt);
            n->funcname = $3;
            n->args = $5;
            $$ = (Node *)n;
            }
-          ;
+   ;
 
 func_argtypes:  name_list          { $$ = $1; }
-        |  /*EMPTY*/               { $$ = NIL; }
+   |  /*EMPTY*/                { $$ = NIL; }
    ;
 
 RemoveOperStmt:  DROP OPERATOR all_Op '(' oper_argtypes ')'
@@ -1144,21 +1194,21 @@ RemoveOperStmt:  DROP OPERATOR all_Op '(' oper_argtypes ')'
            n->args = $5;
            $$ = (Node *)n;
        }
-        ;
+   ;
 
 all_Op: Op | MathOp;
 
-MathOp:    '+'         { $$ = "+"; }
-   |  '-'      { $$ = "-"; }
-   |  '*'      { $$ = "*"; }
-   |  '/'      { $$ = "/"; }
-   |  '<'      { $$ = "<"; }
-   |  '>'      { $$ = ">"; }
-   |  '='      { $$ = "="; }
+MathOp:    '+'     { $$ = "+"; }
+   |  '-'      { $$ = "-"; }
+   |  '*'      { $$ = "*"; }
+   |  '/'      { $$ = "/"; }
+   |  '<'      { $$ = "<"; }
+   |  '>'      { $$ = ">"; }
+   |  '='      { $$ = "="; }
    ;
 
-oper_argtypes:  name   
-       { 
+oper_argtypes:  name
+       {
           elog(WARN, "parser: argument type missing (use NONE for unary operators)");
        }
    | name ',' name
@@ -1171,15 +1221,15 @@ oper_argtypes:  name
 
 /*****************************************************************************
  *
- * QUERY:   
+ * QUERY:
  *     rename  in  [*] to 
  *     rename  to 
- * 
+ *
  *****************************************************************************/
 
-RenameStmt:  ALTER TABLE relation_name opt_inh_star 
+RenameStmt:  ALTER TABLE relation_name opt_inh_star
          RENAME opt_column opt_name TO name
-       { 
+       {
            RenameStmt *n = makeNode(RenameStmt);
            n->relname = $3;
            n->inh = $4;
@@ -1199,15 +1249,15 @@ opt_column:  COLUMN         { $$ = COLUMN; }
 
 
 /*****************************************************************************
- *  
- * QUERY:  Define Rewrite Rule , Define Tuple Rule 
+ *
+ * QUERY:  Define Rewrite Rule , Define Tuple Rule
  *     Define Rule 
  *
  *      only rewrite rule is supported -- ay 9/94
- *  
+ *
  *****************************************************************************/
 
-RuleStmt:  CREATE RULE name AS 
+RuleStmt:  CREATE RULE name AS
       { QueryIsRule=TRUE; }
       ON event TO event_object where_clause
       DO opt_instead OptStmtList
@@ -1224,26 +1274,26 @@ RuleStmt:  CREATE RULE name AS
    ;
 
 OptStmtList:  NOTHING          { $$ = NIL; }
-   | OptimizableStmt       { $$ = lcons($1, NIL); }    
+   | OptimizableStmt       { $$ = lcons($1, NIL); }
    | '[' OptStmtBlock ']'      { $$ = $2; }
-        ;
+   ;
 
 OptStmtBlock:  OptStmtMulti
-               {  $$ = $1; }
+       {  $$ = $1; }
    |  OptimizableStmt
        { $$ = lcons($1, NIL); }
    ;
-   
+
 OptStmtMulti:  OptStmtMulti OptimizableStmt ';'
-               {  $$ = lappend($1, $2); }
+       {  $$ = lappend($1, $2); }
    |  OptStmtMulti OptimizableStmt
-               {  $$ = lappend($1, $2); }
+       {  $$ = lappend($1, $2); }
    |  OptimizableStmt ';'
        { $$ = lcons($1, NIL); }
    ;
-   
+
 event_object: relation_name '.' attr_name
-       { 
+       {
            $$ = makeNode(Attr);
            $$->relname = $1;
            $$->paramNo = NULL;
@@ -1258,12 +1308,12 @@ event_object: relation_name '.' attr_name
            $$->attrs = NIL;
            $$->indirection = NIL;
        }
-   ;
+   ;
 
 /* change me to select, update, etc. some day */
-event:     SELECT              { $$ = CMD_SELECT; }
-   | UPDATE            { $$ = CMD_UPDATE; }
-   | DELETE            { $$ = CMD_DELETE; }
+event: SELECT              { $$ = CMD_SELECT; }
+   | UPDATE            { $$ = CMD_UPDATE; }
+   | DELETE            { $$ = CMD_DELETE; }
    | INSERT            { $$ = CMD_INSERT; }
     ;
 
@@ -1280,7 +1330,7 @@ opt_instead:  INSTEAD         { $$ = TRUE; }
  *
  *****************************************************************************/
 
-NotifyStmt: NOTIFY relation_name 
+NotifyStmt: NOTIFY relation_name
        {
            NotifyStmt *n = makeNode(NotifyStmt);
            n->relname = $2;
@@ -1288,7 +1338,7 @@ NotifyStmt: NOTIFY relation_name
        }
    ;
 
-ListenStmt: LISTEN relation_name 
+ListenStmt: LISTEN relation_name
        {
            ListenStmt *n = makeNode(ListenStmt);
            n->relname = $2;
@@ -1307,74 +1357,74 @@ ListenStmt: LISTEN relation_name
  *     (BEGIN)
  * end transaction
  *     (END)
- * 
+ *
  *****************************************************************************/
 
 TransactionStmt:  ABORT_TRANS TRANSACTION
-       { 
-           TransactionStmt *n = makeNode(TransactionStmt); 
-           n->command = ABORT_TRANS; 
+       {
+           TransactionStmt *n = makeNode(TransactionStmt);
+           n->command = ABORT_TRANS;
            $$ = (Node *)n;
        }
    | BEGIN_TRANS TRANSACTION
-       { 
-           TransactionStmt *n = makeNode(TransactionStmt); 
+       {
+           TransactionStmt *n = makeNode(TransactionStmt);
            n->command = BEGIN_TRANS;
            $$ = (Node *)n;
        }
    | BEGIN_TRANS WORK
        {
-           TransactionStmt *n = makeNode(TransactionStmt); 
+           TransactionStmt *n = makeNode(TransactionStmt);
            n->command = BEGIN_TRANS;
            $$ = (Node *)n;
        }
    | COMMIT WORK
        {
-           TransactionStmt *n = makeNode(TransactionStmt); 
+           TransactionStmt *n = makeNode(TransactionStmt);
            n->command = END_TRANS;
            $$ = (Node *)n;
        }
    | END_TRANS TRANSACTION
-       { 
-           TransactionStmt *n = makeNode(TransactionStmt); 
+       {
+           TransactionStmt *n = makeNode(TransactionStmt);
            n->command = END_TRANS;
            $$ = (Node *)n;
        }
    | ROLLBACK WORK
        {
-           TransactionStmt *n = makeNode(TransactionStmt); 
+           TransactionStmt *n = makeNode(TransactionStmt);
            n->command = ABORT_TRANS;
            $$ = (Node *)n;
        }
 
    | ABORT_TRANS
-       { 
-           TransactionStmt *n = makeNode(TransactionStmt); 
-           n->command = ABORT_TRANS; 
+       {
+           TransactionStmt *n = makeNode(TransactionStmt);
+           n->command = ABORT_TRANS;
            $$ = (Node *)n;
        }
    | BEGIN_TRANS
-       { 
-           TransactionStmt *n = makeNode(TransactionStmt); 
+       {
+           TransactionStmt *n = makeNode(TransactionStmt);
            n->command = BEGIN_TRANS;
            $$ = (Node *)n;
        }
    | COMMIT
        {
-           TransactionStmt *n = makeNode(TransactionStmt); 
+           TransactionStmt *n = makeNode(TransactionStmt);
            n->command = END_TRANS;
            $$ = (Node *)n;
        }
 
    | END_TRANS
-       { 
-           TransactionStmt *n = makeNode(TransactionStmt); 
+       {
+           TransactionStmt *n = makeNode(TransactionStmt);
            n->command = END_TRANS;
            $$ = (Node *)n;
        }
    | ROLLBACK
        {
-           TransactionStmt *n = makeNode(TransactionStmt); 
+           TransactionStmt *n = makeNode(TransactionStmt);
            n->command = ABORT_TRANS;
            $$ = (Node *)n;
        }
@@ -1389,7 +1439,7 @@ TransactionStmt:  ABORT_TRANS TRANSACTION
  *****************************************************************************/
 
 ViewStmt:  CREATE VIEW name AS RetrieveStmt
-       { 
+       {
            ViewStmt *n = makeNode(ViewStmt);
            n->viewname = $3;
            n->query = (Query *)$5;
@@ -1406,12 +1456,12 @@ ViewStmt:  CREATE VIEW name AS RetrieveStmt
  *****************************************************************************/
 
 LoadStmt: LOAD file_name
-                { 
+       {
            LoadStmt *n = makeNode(LoadStmt);
            n->filename = $2;
            $$ = (Node *)n;
        }
-        ;
+   ;
 
 
 /*****************************************************************************
@@ -1422,12 +1472,12 @@ LoadStmt: LOAD file_name
  *****************************************************************************/
 
 CreatedbStmt:  CREATE DATABASE database_name
-                {
+       {
            CreatedbStmt *n = makeNode(CreatedbStmt);
            n->dbname = $3;
            $$ = (Node *)n;
        }
-        ;
+   ;
 
 
 /*****************************************************************************
@@ -1438,12 +1488,12 @@ CreatedbStmt:  CREATE DATABASE database_name
  *****************************************************************************/
 
 DestroydbStmt:  DROP DATABASE database_name
-                {
+       {
            DestroydbStmt *n = makeNode(DestroydbStmt);
            n->dbname = $3;
            $$ = (Node *)n;
        }
-        ;
+   ;
 
 
 /*****************************************************************************
@@ -1453,7 +1503,7 @@ DestroydbStmt:  DROP DATABASE database_name
  *
  *****************************************************************************/
 
-ClusterStmt:  CLUSTER index_name ON relation_name 
+ClusterStmt:  CLUSTER index_name ON relation_name
        {
           ClusterStmt *n = makeNode(ClusterStmt);
           n->relname = $4;
@@ -1470,26 +1520,26 @@ ClusterStmt:  CLUSTER index_name ON relation_name
  *****************************************************************************/
 
 VacuumStmt:  VACUUM opt_verbose opt_analyze
-               {
-                   VacuumStmt *n = makeNode(VacuumStmt);
-                   n->verbose = $2;
-                   n->analyze = $3;
-                   n->vacrel = NULL;
-                   n->va_spec = NIL;
-                   $$ = (Node *)n;
-               }
-         | VACUUM opt_verbose relation_name opt_analyze opt_va_list
-               {
-                   VacuumStmt *n = makeNode(VacuumStmt);
-                   n->verbose = $2;
-                   n->analyze = $4;
-                   n->vacrel = $3;
-                   n->va_spec = $5;
-                   if ( $5 != NIL && !$4 )
-                       elog (WARN, "parser: syntax error at or near \"(\"");
-                   $$ = (Node *)n;
-               }
-       ;
+       {
+           VacuumStmt *n = makeNode(VacuumStmt);
+           n->verbose = $2;
+           n->analyze = $3;
+           n->vacrel = NULL;
+           n->va_spec = NIL;
+           $$ = (Node *)n;
+       }
+   | VACUUM opt_verbose relation_name opt_analyze opt_va_list
+       {
+           VacuumStmt *n = makeNode(VacuumStmt);
+           n->verbose = $2;
+           n->analyze = $4;
+           n->vacrel = $3;
+           n->va_spec = $5;
+           if ( $5 != NIL && !$4 )
+           elog (WARN, "parser: syntax error at or near \"(\"");
+           $$ = (Node *)n;
+       }
+   ;
 
 opt_verbose:  VERBOSE          { $$ = TRUE; }
    | /* EMPTY */           { $$ = FALSE; }
@@ -1503,14 +1553,14 @@ opt_va_list: '(' va_list ')'
        { $$ = $2; }
    | /* EMPTY */
        { $$ = NIL; }
-   ;   
+   ;
 
 va_list: name
        { $$=lcons($1,NIL); }
    | va_list ',' name
        { $$=lappend($1,$3); }
-   ;   
-       
+   ;
+
 /*****************************************************************************
  *
  * QUERY:
@@ -1539,11 +1589,11 @@ ExplainStmt:  EXPLAIN opt_verbose OptimizableStmt
  *****************************************************************************/
 
 OptimizableStmt:  RetrieveStmt
-   | CursorStmt    
+   | CursorStmt
    | ReplaceStmt
    | AppendStmt
-        | NotifyStmt
-        | DeleteStmt           /* by default all are $$=$1 */
+   | NotifyStmt
+   | DeleteStmt            /* by default all are $$=$1 */
    ;
 
 
@@ -1551,7 +1601,7 @@ OptimizableStmt:  RetrieveStmt
  *
  * QUERY:
  *     INSERT STATEMENTS
- *  
+ *
  *****************************************************************************/
 
 AppendStmt:  INSERT INTO relation_name opt_column_list insert_rest
@@ -1559,7 +1609,7 @@ AppendStmt:  INSERT INTO relation_name opt_column_list insert_rest
            $5->relname = $3;
            $5->cols = $4;
            $$ = (Node *)$5;
-                }
+       }
    ;
 
 insert_rest: VALUES '(' res_target_list2 ')'
@@ -1582,7 +1632,7 @@ opt_column_list: '(' columnList ')'       { $$ = $2; }
    | /*EMPTY*/             { $$ = NIL; }
    ;
 
-columnList:    
+columnList:
      columnList ',' columnElem
        { $$ = lappend($1, $3); }
    | columnElem
@@ -1590,7 +1640,7 @@ columnList:
    ;
 
 columnElem: Id opt_indirection
-       {
+       {
            Ident *id = makeNode(Ident);
            id->name = $1;
            id->indirection = $2;
@@ -1604,16 +1654,16 @@ columnElem: Id opt_indirection
  *     DELETE STATEMENTS
  *
  *****************************************************************************/
-   
+
 DeleteStmt:  DELETE FROM relation_name
         where_clause
-                {
+       {
            DeleteStmt *n = makeNode(DeleteStmt);
            n->relname = $3;
            n->whereClause = $4;
            $$ = (Node *)n;
        }
-        ;
+   ;
 
 
 /*****************************************************************************
@@ -1623,20 +1673,20 @@ DeleteStmt:  DELETE FROM relation_name
  *
  *****************************************************************************/
 
-ReplaceStmt:  UPDATE relation_name 
+ReplaceStmt:  UPDATE relation_name
          SET res_target_list
          from_clause
          where_clause
-                {
+       {
            ReplaceStmt *n = makeNode(ReplaceStmt);
            n->relname = $2;
            n->targetList = $4;
            n->fromClause = $5;
            n->whereClause = $6;
            $$ = (Node *)n;
-                }
-        ;
-        
+       }
+   ;
+
 
 /*****************************************************************************
  *
@@ -1645,8 +1695,8 @@ ReplaceStmt:  UPDATE relation_name
  *
  *****************************************************************************/
 
-CursorStmt:  DECLARE name opt_binary CURSOR FOR 
-        SELECT opt_unique res_target_list2 
+CursorStmt:  DECLARE name opt_binary CURSOR FOR
+        SELECT opt_unique res_target_list2
         from_clause where_clause group_clause sort_clause
        {
            CursorStmt *n = makeNode(CursorStmt);
@@ -1656,7 +1706,7 @@ CursorStmt:  DECLARE name opt_binary CURSOR FOR
             *  15 august 1991 -- since 3.0 postgres does locking
             *  right, we discovered that portals were violating
             *  locking protocol.  portal locks cannot span xacts.
-            *  as a short-term fix, we installed the check here. 
+            *  as a short-term fix, we installed the check here.
             *              -- mao
             */
            if (!IsTransactionBlock())
@@ -1682,11 +1732,12 @@ CursorStmt:  DECLARE name opt_binary CURSOR FOR
  *
  *****************************************************************************/
 
+/******************************************************************************
 RetrieveStmt:  SELECT opt_unique res_target_list2
-          result from_clause where_clause 
+          result from_clause where_clause
           group_clause having_clause
           sort_clause
-       {
+       {
            RetrieveStmt *n = makeNode(RetrieveStmt);
            n->unique = $2;
            n->targetList = $3;
@@ -1700,6 +1751,69 @@ RetrieveStmt:  SELECT opt_unique res_target_list2
        }
    ;
 
+RetrieveStmt:  Select UNION select_list sort_clause
+   | Select sort_clause
+Select:  SELECT opt_unique res_target_list2
+          result from_clause where_clause
+          group_clause having_clause
+       {
+           Select *n = makeNode(Select);
+           n->unique = $2;
+           n->targetList = $3;
+           n->into = $4;
+           n->fromClause = $5;
+           n->whereClause = $6;
+           n->groupClause = $7;
+           n->havingClause = $8;
+           $$ = (Node *)n;
+       }
+   ;
+******************************************************************************/
+
+RetrieveStmt:  SELECT opt_unique res_target_list2
+          result from_clause where_clause
+          group_clause having_clause
+          union_clause sort_clause
+       {
+           RetrieveStmt *n = makeNode(RetrieveStmt);
+           n->unique = $2;
+           n->targetList = $3;
+           n->into = $4;
+           n->fromClause = $5;
+           n->whereClause = $6;
+           n->groupClause = $7;
+           n->havingClause = $8;
+           n->selectClause = $9;
+           n->sortClause = $10;
+           $$ = (Node *)n;
+       }
+   ;
+
+union_clause:  UNION select_list       { $$ = $2; }
+   |  /*EMPTY*/                { $$ = NIL; }
+   ;
+
+select_list:  select_list UNION SubSelect
+       { $$ = lappend($1, $3); }
+   | SubSelect
+       { $$ = lcons($1, NIL); }
+   ;
+
+SubSelect:  SELECT opt_unique res_target_list2
+          result from_clause where_clause
+          group_clause having_clause
+       {
+           SubSelect *n = makeNode(SubSelect);
+           n->unique = $2;
+           n->targetList = $3;
+           n->fromClause = $5;
+           n->whereClause = $6;
+           n->groupClause = $7;
+           n->havingClause = $8;
+           $$ = (Node *)n;
+       }
+   ;
+
 result:  INTO TABLE relation_name
        {  $$= $3;  /* should check for archive level */  }
    | /*EMPTY*/
@@ -1711,7 +1825,7 @@ opt_unique:  DISTINCT     { $$ = "*"; }
    | /*EMPTY*/     { $$ = NULL;}
    ;
 
-sort_clause:  ORDER BY sortby_list         { $$ = $3; }
+sort_clause:  ORDER BY sortby_list     { $$ = $3; }
    |  /*EMPTY*/                { $$ = NIL; }
    ;
 
@@ -1722,7 +1836,7 @@ sortby_list:  sortby
    ;
 
 sortby:  Id OptUseOp
-       { 
+       {
            $$ = makeNode(SortGroupBy);
            $$->resno = 0;
            $$->range = NULL;
@@ -1782,13 +1896,13 @@ index_elem:  attr_name opt_type opt_class
            $$->name = $1;
            $$->args = NIL;
            $$->class = $3;
-               $$->tname = $2;
+               $$->tname = $2;
        }
    ;
 
 opt_type: ':' Typename                          { $$ = $2;}
-        |  /*EMPTY*/                            { $$ = NULL;}
-        ;
+   |  /*EMPTY*/                            { $$ = NULL;}
+   ;
 
 opt_class:  class
    |  WITH class               { $$ = $2; }
@@ -1799,9 +1913,9 @@ opt_class:  class
  *  jimmy bell-style recursive queries aren't supported in the
  *  current system.
  *
- *  ...however, recursive addattr and rename supported.  make special 
+ *  ...however, recursive addattr and rename supported.  make special
  *  cases for these.
- * 
+ *
  *  XXX i believe '*' should be the default behavior, but...
  */
 opt_inh_star: '*'          { $$ = TRUE; }
@@ -1810,11 +1924,11 @@ opt_inh_star: '*'           { $$ = TRUE; }
 
 relation_name_list:    name_list ;
 
-name_list: name            
+name_list: name
        { $$=lcons(makeString($1),NIL); }
-   | name_list ',' name        
+   | name_list ',' name
        { $$=lappend($1,makeString($3)); }
-   ;   
+   ;
 
 group_clause: GROUP BY groupby_list        { $$ = $3; }
    | /*EMPTY*/             { $$ = NIL; }
@@ -1855,19 +1969,26 @@ having_clause: HAVING a_expr            { $$ = $2; }
    ;
 
 /*****************************************************************************
- *  
+ *
  *  clauses common to all Optimizable Stmts:
- *     from_clause     -
- *      where_clause   -
- * 
+ * from_clause -
+ *      where_clause   -
+ *
  *****************************************************************************/
 
-from_clause:  FROM from_list           { $$ = $2; }
+from_clause:  FROM '(' relation_expr join_clause relation_expr join_spec ')'
+       {
+           $$ = NIL;
+           elog(WARN,"JOIN not yet implemented",NULL);
+       }
+   | FROM from_list            { $$ = $2; }
    | /*EMPTY*/             { $$ = NIL; }
    ;
 
 from_list:  from_list ',' from_val
        { $$ = lappend($1, $3); }
+   |  from_val CROSS JOIN from_val
+       { elog(WARN,"CROSS JOIN not yet implemented",NULL); }
    |  from_val
        { $$ = lcons($1, NIL); }
    ;
@@ -1877,7 +1998,7 @@ from_val:  relation_expr AS var_name
            $$ = makeNode(RangeVar);
            $$->relExpr = $1;
            $$->name = $3;
-       }   
+       }
    | relation_expr var_name
        {
            $$ = makeNode(RangeVar);
@@ -1892,12 +2013,76 @@ from_val:  relation_expr AS var_name
        }
    ;
 
+join_clause:  join_qual join_type JOIN
+       {
+           $$ = NULL;
+       }
+   ;
+
+join_qual:  NATURAL            { $$ = TRUE; }
+   | /*EMPTY*/             { $$ = FALSE; }
+   ;
+
+join_type:  FULL join_outer
+       { elog(WARN,"FULL OUTER JOIN not yet implemented",NULL); }
+   | LEFT join_outer
+       { elog(WARN,"LEFT OUTER JOIN not yet implemented",NULL); }
+   | RIGHT join_outer
+       { elog(WARN,"RIGHT OUTER JOIN not yet implemented",NULL); }
+   | join_outer
+       { elog(WARN,"OUTER JOIN not yet implemented",NULL); }
+   | INNERJOIN
+       { elog(WARN,"INNER JOIN not yet implemented",NULL); }
+   | UNION
+       { elog(WARN,"UNION JOIN not yet implemented",NULL); }
+   | /*EMPTY*/             { $$ = NULL;  /* no qualifiers */ }
+   ;
+
+join_outer:  OUTERJOIN         { $$ = NULL; }
+   | /*EMPTY*/         { $$ = NULL;  /* no qualifiers */ }
+   ;
+
+join_spec:  ON '(' a_expr ')'      { $$ = NULL; }
+   | USING '(' join_list ')'   { $$ = NULL; }
+   | /*EMPTY*/         { $$ = NULL;  /* no qualifiers */ }
+   ;
+
+join_list: join_using              { $$ = lcons($1, NIL); }
+   | join_list ',' join_using      { $$ = lappend($1, $3); }
+   ;
+
+join_using:  Id
+       {
+           $$ = makeNode(SortGroupBy);
+           $$->resno = 0;
+           $$->range = NULL;
+           $$->name = $1;
+           $$->useOp = NULL;
+       }
+   | Id '.' Id
+       {
+           $$ = makeNode(SortGroupBy);
+           $$->resno = 0;
+           $$->range = $1;
+           $$->name = $3;
+           $$->useOp = NULL;
+       }
+   | Iconst
+       {
+           $$ = makeNode(SortGroupBy);
+           $$->resno = $1;
+           $$->range = NULL;
+           $$->name = NULL;
+           $$->useOp = NULL;
+       }
+   ;
+
 where_clause:  WHERE a_expr        { $$ = $2; }
-   | /*EMPTY*/             { $$ = NULL;  /* no qualifiers */ } 
+   | /*EMPTY*/             { $$ = NULL;  /* no qualifiers */ }
    ;
 
 relation_expr:  relation_name
-       { 
+       {
            /* normal relations */
            $$ = makeNode(RelExpr);
            $$->relname = $1;
@@ -1905,15 +2090,15 @@ relation_expr:  relation_name
            $$->timeRange = NULL;
        }
    | relation_name '*'       %prec '='
-       { 
+       {
            /* inheiritance query */
            $$ = makeNode(RelExpr);
            $$->relname = $1;
            $$->inh = TRUE;
            $$->timeRange = NULL;
        }
-   | relation_name time_range 
-       { 
+   | relation_name time_range
+       {
            /* time-qualified query */
            $$ = makeNode(RelExpr);
            $$->relname = $1;
@@ -1922,20 +2107,20 @@ relation_expr:  relation_name
        }
    ;
 
-     
+
 time_range:  '[' opt_range_start ',' opt_range_end ']'
-           { 
+       {
            $$ = makeNode(TimeRange);
            $$->startDate = $2;
            $$->endDate = $4;
        }
    | '[' date ']'
-       { 
+       {
            $$ = makeNode(TimeRange);
            $$->startDate = $2;
            $$->endDate = NULL;
        }
-        ;
+   ;
 
 opt_range_start:  date
    |  /*EMPTY*/                { $$ = "epoch"; }
@@ -1949,19 +2134,19 @@ opt_array_bounds:  '[' ']' nest_array_bounds
        {  $$ = lcons(makeInteger(-1), $3); }
    | '[' Iconst ']' nest_array_bounds
        {  $$ = lcons(makeInteger($2), $4); }
-   | /* EMPTY */               
+   | /* EMPTY */
        {  $$ = NIL; }
    ;
 
 nest_array_bounds:  '[' ']' nest_array_bounds
        {  $$ = lcons(makeInteger(-1), $3); }
-   | '[' Iconst ']' nest_array_bounds 
+   | '[' Iconst ']' nest_array_bounds
        {  $$ = lcons(makeInteger($2), $4); }
    | /*EMPTY*/
        {  $$ = NIL; }
    ;
 
-typname:  name  
+typname:  txname
        {
            char *tname = xlateSqlType($1);
            $$ = makeNode(TypeName);
@@ -1971,66 +2156,101 @@ typname:  name
             * it as a set.
             */
            if (!strcmp(saved_relname, tname)) {
-           /* This attr is the same type as the relation 
+           /* This attr is the same type as the relation
             * being defined. The classic example: create
             * emp(name=text,mgr=emp)
             */
            $$->setof = TRUE;
            }else if (get_typrelid((Type)type(tname))
                != InvalidOid) {
-            /* (Eventually add in here that the set can only 
+            /* (Eventually add in here that the set can only
              * contain one element.)
              */
            $$->setof = TRUE;
-               } else {
+           } else {
            $$->setof = FALSE;
            }
        }
-        | SETOF name
+   | SETOF txname
        {
+           char *tname = xlateSqlType($2);
            $$ = makeNode(TypeName);
-           $$->name = $2;
+           $$->name = tname;
            $$->setof = TRUE;
        }
-        ;
+   ;
 
-Typename:  typname opt_array_bounds        
-       { 
+txname:  Id                { $$ = $1; }
+   | TIME              { $$ = "time"; }
+   | INTERVAL interval_opts    { $$ = "interval"; }
+   ;
+
+interval_opts:  YEARINTERVAL           { $$ = lcons("year", NIL); }
+   | MONTHINTERVAL             { $$ = NIL; }
+   | DAYINTERVAL               { $$ = NIL; }
+   | HOURINTERVAL              { $$ = NIL; }
+   | MINUTEINTERVAL            { $$ = NIL; }
+   | SECONDINTERVAL            { $$ = NIL; }
+   | YEARINTERVAL TO MONTHINTERVAL     { $$ = NIL; }
+   | DAYINTERVAL TO HOURINTERVAL       { $$ = NIL; }
+   | DAYINTERVAL TO MINUTEINTERVAL     { $$ = NIL; }
+   | DAYINTERVAL TO SECONDINTERVAL     { $$ = NIL; }
+   | HOURINTERVAL TO MINUTEINTERVAL    { $$ = NIL; }
+   | HOURINTERVAL TO SECONDINTERVAL    { $$ = NIL; }
+   | /* EMPTY */               { $$ = NIL; }
+   ;
+
+Typename:  typname opt_array_bounds
+       {
            $$ = $1;
            $$->arrayBounds = $2;
        }
-   | name '(' Iconst ')'
+   | txname '(' Iconst ')'
        {
            /*
-            * The following implements char() and varchar().
-            * We do it here instead of the 'typname:' production
-            * because we don't want to allow arrays of varchar().
-            * I haven't thought about whether that will work or not.
-             *                             - ay 6/95
+            * This block gets hit when the parser is passed a query
+            * which contains only spaces (e.g. from psql type "  \g").
+            * Let's check explicitly for a zero-length argument
+            * here, and do nothing if so. This seems to fix the problem.
+            * - thomas 1997-07-13
             */
-           $$ = makeNode(TypeName);
-           if (!strcasecmp($1, "char")) {
-           $$->name = "bpchar"; /*  strdup("bpchar"); */
-           } else if (!strcasecmp($1, "varchar")) {
-           $$->name = "varchar"; /* strdup("varchar"); */
-           } else {
-           yyerror("parse error");
-           }
-           if ($3 < 1) {
-           elog(WARN, "length for '%s' type must be at least 1",
-                $1);
-           } else if ($3 > 4096) {
-           /* we can store a char() of length up to the size
-              of a page (8KB) - page headers and friends but
-              just to be safe here...  - ay 6/95 */
-           elog(WARN, "length for '%s' type cannot exceed 4096",
+           if (strlen($1) > 0) {
+
+           /*
+            * The following implements char() and varchar().
+            * We do it here instead of the 'typname:' production
+            * because we don't want to allow arrays of varchar().
+            * I haven't thought about whether that will work or not.
+            *                             - ay 6/95
+            */
+           $$ = makeNode(TypeName);
+           if (!strcasecmp($1, "char")) {
+               $$->name = "bpchar"; /*  strdup("bpchar"); */
+           } else if (!strcasecmp($1, "varchar")) {
+               $$->name = "varchar"; /* strdup("varchar"); */
+           } else {
+               yyerror("parse error");
+           }
+           if ($3 < 1) {
+               elog(WARN, "length for '%s' type must be at least 1",
+               $1);
+           } else if ($3 > 4096) {
+               /* we can store a char() of length up to the size
+                * of a page (8KB) - page headers and friends but
+                * just to be safe here...  - ay 6/95
+                * XXX note this hardcoded limit - thomas 1997-07-13
+                */
+               elog(WARN, "length for '%s' type cannot exceed 4096",
                 $1);
+           }
+           /* we actually implement this sort of like a varlen, so
+            * the first 4 bytes is the length. (the difference
+            * between this and "text" is that we blank-pad and
+            * truncate where necessary
+            */
+           $$->typlen = 4 + $3;
+
            }
-           /* we actually implement this sort of like a varlen, so
-              the first 4 bytes is the length. (the difference
-              between this and "text" is that we blank-pad and 
-              truncate where necessary */
-           $$->typlen = 4 + $3;
        }
    ;
 
@@ -2044,12 +2264,12 @@ Typename:  typname opt_array_bounds
 a_expr_or_null: a_expr
        { $$ = $1;}
    | Pnull
-       {   
+       {
            A_Const *n = makeNode(A_Const);
            n->val.type = T_Null;
            $$ = (Node *)n;
        }
-       
+
 a_expr:  attr opt_indirection
        {
            $1->indirection = $2;
@@ -2080,7 +2300,7 @@ a_expr:  attr opt_indirection
    | '|' a_expr
        {   $$ = makeA_Expr(OP, "|", NULL, $2); }
    | AexprConst TYPECAST Typename
-       { 
+       {
            /* AexprConst can be either A_Const or ParamNo */
            if (nodeTag($1) == T_A_Const) {
            ((A_Const *)$1)->typename = $3;
@@ -2124,7 +2344,7 @@ a_expr:  attr opt_indirection
            Ident *star = makeNode(Ident);
 
            /* cheap hack for aggregate (eg. count) */
-           star->name = "oid"; 
+           star->name = "oid";
            n->funcname = $1;
            n->args = lcons(star, NIL);
            $$ = (Node *)n;
@@ -2136,6 +2356,66 @@ a_expr:  attr opt_indirection
            n->args = NIL;
            $$ = (Node *)n;
        }
+   /* We probably need to define an "exists" node,
+    *  since the optimizer could choose to find only one match.
+    * Perhaps the first implementation could just check for
+    *  count(*) > 0? - thomas 1997-07-19
+    */
+   | EXISTS '(' SubSelect ')'
+       {
+           elog(WARN,"EXISTS not yet supported",NULL);
+           $$ = $3;
+       }
+   | EXTRACT '(' extract_list ')'
+       {
+           FuncCall *n = makeNode(FuncCall);
+           n->funcname = "date_part";
+           n->args = $3;
+           $$ = (Node *)n;
+       }
+   | POSITION '(' position_list ')'
+       {
+           FuncCall *n = makeNode(FuncCall);
+           n->funcname = "strpos";
+           n->args = $3;
+           $$ = (Node *)n;
+       }
+   | SUBSTRING '(' substr_list ')'
+       {
+           FuncCall *n = makeNode(FuncCall);
+           n->funcname = "substr";
+           n->args = $3;
+           $$ = (Node *)n;
+       }
+   /* various trim expressions are defined in SQL92 - thomas 1997-07-19 */
+   | TRIM '(' BOTH trim_list ')'
+       {
+           FuncCall *n = makeNode(FuncCall);
+           n->funcname = "btrim";
+           n->args = $4;
+           $$ = (Node *)n;
+       }
+   | TRIM '(' LEADING trim_list ')'
+       {
+           FuncCall *n = makeNode(FuncCall);
+           n->funcname = "ltrim";
+           n->args = $4;
+           $$ = (Node *)n;
+       }
+   | TRIM '(' TRAILING trim_list ')'
+       {
+           FuncCall *n = makeNode(FuncCall);
+           n->funcname = "rtrim";
+           n->args = $4;
+           $$ = (Node *)n;
+       }
+   | TRIM '(' trim_list ')'
+       {
+           FuncCall *n = makeNode(FuncCall);
+           n->funcname = "btrim";
+           n->args = $3;
+           $$ = (Node *)n;
+       }
    | name '(' expr_list ')'
        {
            FuncCall *n = makeNode(FuncCall);
@@ -2154,7 +2434,7 @@ a_expr:  attr opt_indirection
    | a_expr BETWEEN AexprConst AND AexprConst
        {   $$ = makeA_Expr(AND, NULL,
            makeA_Expr(OP, ">=", $1, $3),
-           makeA_Expr(OP, "<=", $1,$5));
+           makeA_Expr(OP, "<=", $1, $5));
        }
    | a_expr NOT BETWEEN AexprConst AND AexprConst
        {   $$ = makeA_Expr(OR, NULL,
@@ -2173,28 +2453,80 @@ a_expr:  attr opt_indirection
        {   $$ = makeA_Expr(NOT, NULL, NULL, $2); }
    ;
 
-opt_indirection:  '[' a_expr ']' opt_indirection 
+opt_indirection:  '[' a_expr ']' opt_indirection
        {
            A_Indices *ai = makeNode(A_Indices);
            ai->lidx = NULL;
            ai->uidx = $2;
            $$ = lcons(ai, $4);
        }
-   | '[' a_expr ':' a_expr ']' opt_indirection 
+   | '[' a_expr ':' a_expr ']' opt_indirection
        {
            A_Indices *ai = makeNode(A_Indices);
            ai->lidx = $2;
            ai->uidx = $4;
            $$ = lcons(ai, $6);
        }
-   | /* EMPTY */           
+   | /* EMPTY */
        {   $$ = NIL; }
    ;
-   
+
 expr_list: a_expr_or_null
        { $$ = lcons($1, NIL); }
    |  expr_list ',' a_expr_or_null
        { $$ = lappend($1, $3); }
+   |  expr_list USING a_expr
+       { $$ = lappend($1, $3); }
+   ;
+
+extract_list: datetime FROM a_expr
+       {
+           A_Const *n = makeNode(A_Const);
+           n->val.type = T_String;
+           n->val.val.str = $1;
+printf( "string is %s\n", $1);
+           $$ = lappend(lcons((Node *)n,NIL), $3);
+       }
+   | /* EMPTY */
+       {   $$ = NIL; }
+   ;
+
+position_list: a_expr IN expr_list
+       {
+           $$ = lappend($3, $1);
+       }
+   | /* EMPTY */
+       {   $$ = NIL; }
+   ;
+
+substr_list: expr_list substr_from substr_for
+       {
+           $$ = $1;
+           if ($2 != NULL) $$ = lappend($$, $2);
+           if ($3 != NULL) $$ = lappend($$, $3);
+       }
+   | /* EMPTY */
+       {   $$ = NIL; }
+   ;
+
+substr_from: FROM expr_list
+       {   $$ = $2; }
+   | /* EMPTY */
+       {   $$ = NIL; }
+   ;
+
+substr_for: FOR expr_list
+       {   $$ = $2; }
+   | /* EMPTY */
+       {   $$ = NIL; }
+   ;
+
+trim_list: a_expr FROM expr_list
+       { $$ = lappend($3, $1); }
+   |  FROM expr_list
+       { $$ = $2; }
+   |  expr_list
+       { $$ = $1; }
    ;
 
 in_expr_nodes: AexprConst
@@ -2214,7 +2546,7 @@ not_in_expr_nodes: AexprConst
    ;
 
 attr:  relation_name '.' attrs
-           {
+       {
            $$ = makeNode(Attr);
            $$->relname = $1;
            $$->paramNo = NULL;
@@ -2231,14 +2563,21 @@ attr:  relation_name '.' attrs
        }
    ;
 
-attrs:    attr_name                        
+attrs:    attr_name
        { $$ = lcons(makeString($1), NIL); }
-   | attrs '.' attr_name             
+   | attrs '.' attr_name
        { $$ = lappend($1, makeString($3)); }
    | attrs '.' '*'
        { $$ = lappend($1, makeString("*")); }
    ;
 
+datetime:  YEARINTERVAL                { $$ = "year"; }
+   | MONTHINTERVAL             { $$ = "month"; }
+   | DAYINTERVAL               { $$ = "day"; }
+   | HOURINTERVAL              { $$ = "hour"; }
+   | MINUTEINTERVAL            { $$ = "minute"; }
+   | SECONDINTERVAL            { $$ = "second"; }
+   ;
 
 /*****************************************************************************
  *
@@ -2246,9 +2585,9 @@ attrs:    attr_name
  *
  *****************************************************************************/
 
-res_target_list:  res_target_list ',' res_target_el    
+res_target_list:  res_target_list ',' res_target_el
        {   $$ = lappend($1,$3);  }
-   | res_target_el             
+   | res_target_el
        {   $$ = lcons($1, NIL);  }
    | '*'
        {
@@ -2280,7 +2619,7 @@ res_target_el: Id opt_indirection '=' a_expr_or_null
            $$->val = (Node *)$1;
        }
    | relation_name '.' '*'
-       {   
+       {
            Attr *att = makeNode(Attr);
            att->relname = $1;
            att->paramNo = NULL;
@@ -2291,23 +2630,22 @@ res_target_el: Id opt_indirection '=' a_expr_or_null
            $$->indirection = NULL;
            $$->val = (Node *)att;
        }
-   ;        
+   ;
 
 /*
 ** target list for select.
 ** should get rid of the other but is still needed by the defunct retrieve into
 ** and update (uses a subset)
 */
-res_target_list2:
-     res_target_list2 ',' res_target_el2   
+res_target_list2: res_target_list2 ',' res_target_el2
        {   $$ = lappend($1, $3);  }
-   | res_target_el2            
+   | res_target_el2
        {   $$ = lcons($1, NIL);  }
    ;
 
 /* AS is not optional because shift/red conflict with unary ops */
-res_target_el2: a_expr AS Id 
-       {
+res_target_el2: a_expr AS ColId
+       {
            $$ = makeNode(ResTarget);
            $$->name = $3;
            $$->indirection = NULL;
@@ -2351,30 +2689,30 @@ opt_id:  Id                 { $$ = $1; }
    ;
 
 relation_name:  SpecialRuleRelation
-           {
-                   $$ = $1;
-                   strNcpy(saved_relname, $1, NAMEDATALEN-1);
+       {
+           $$ = $1;
+           strNcpy(saved_relname, $1, NAMEDATALEN-1);
            }
-   | Id
-       {
+   | ColId
+       {
            /* disallow refs to magic system tables */
-           if (strcmp(LogRelationName, $1) == 0
-              || strcmp(VariableRelationName, $1) == 0
-              || strcmp(TimeRelationName, $1) == 0
-              || strcmp(MagicRelationName, $1) == 0) {
+           if (strcmp(LogRelationName, $1) == 0
+              || strcmp(VariableRelationName, $1) == 0
+              || strcmp(TimeRelationName, $1) == 0
+              || strcmp(MagicRelationName, $1) == 0) {
            elog(WARN, "%s cannot be accessed by users", $1);
            } else {
            $$ = $1;
            }
-                    strNcpy(saved_relname, $1, NAMEDATALEN-1);
+           strNcpy(saved_relname, $1, NAMEDATALEN-1);
        }
    ;
 
 database_name:     Id      { $$ = $1; };
-access_method:         Id      { $$ = $1; };
-attr_name:         Id      { $$ = $1; };
-class:             Id      { $$ = $1; };
-index_name:        Id      { $$ = $1; };
+access_method:     Id      { $$ = $1; };
+attr_name:     ColId       { $$ = $1; };
+class:         Id      { $$ = $1; };
+index_name:        Id      { $$ = $1; };
 var_name:      Id      { $$ = $1; };
 name:          Id      { $$ = $1; };
 
@@ -2383,21 +2721,21 @@ file_name:      Sconst      { $$ = $1; };
 recipe_name:       Id      { $$ = $1; };
 
 AexprConst:  Iconst
-       {  
+       {
            A_Const *n = makeNode(A_Const);
            n->val.type = T_Integer;
            n->val.val.ival = $1;
            $$ = (Node *)n;
        }
    | FCONST
-       {  
+       {
            A_Const *n = makeNode(A_Const);
            n->val.type = T_Float;
            n->val.val.dval = $1;
            $$ = (Node *)n;
        }
    | Sconst
-       {  
+       {
            A_Const *n = makeNode(A_Const);
            n->val.type = T_String;
            n->val.val.str = $1;
@@ -2415,27 +2753,31 @@ ParamNo:  PARAM
    ;
 
 NumConst:  Iconst          { $$ = makeInteger($1); }
-   |  FCONST           { $$ = makeFloat($1); }
+   |  FCONST           { $$ = makeFloat($1); }
    ;
 
 Iconst:  ICONST                { $$ = $1; };
 Sconst:     SCONST             { $$ = $1; };
 
-Id:  IDENT                     { $$ = $1; };
+Id:  IDENT             { $$ = $1; };
+
+ColId:  Id             { $$ = $1; }
+   | datetime          { $$ = $1; }
+   ;
 
 SpecialRuleRelation:  CURRENT
-       { 
+       {
            if (QueryIsRule)
            $$ = "*CURRENT*";
-           else 
+           else
            elog(WARN,"CURRENT used in non-rule query");
        }
    | NEW
-       { 
+       {
            if (QueryIsRule)
            $$ = "*NEW*";
-           else 
-           elog(WARN,"NEW used in non-rule query"); 
+           else
+           elog(WARN,"NEW used in non-rule query");
        }
    ;
 
@@ -2466,6 +2808,8 @@ xlateSqlType(char *name)
     else if (!strcasecmp(name, "float") ||
         !strcasecmp(name, "real"))
    return "float8";
+    else if (!strcasecmp(name, "interval"))
+   return "timespan";
     else
    return name;
 }
@@ -2475,7 +2819,7 @@ void parser_init(Oid *typev, int nargs)
     QueryIsRule = false;
     saved_relname[0]= '\0';
     saved_In_Expr = NULL;
-    
+
     param_type_init(typev, nargs);
 }