Re: [HACKERS] Re: subselects

Поиск
Список
Период
Сортировка
От Thomas G. Lockhart
Тема Re: [HACKERS] Re: subselects
Дата
Msg-id 34B7D21C.57EAFF12@alumni.caltech.edu
обсуждение исходный текст
Ответ на Re: subselects  (Bruce Momjian <maillist@candle.pha.pa.us>)
Ответы Re: [HACKERS] Re: subselects  (Bruce Momjian <maillist@candle.pha.pa.us>)
Список pgsql-hackers
> I would like to have something done in parser near Jan 17 to get
> subqueries working by Feb 1.

Here are some changes to gram.y and to keywords.c which start to pass through
subselect constructs. I won't commit until/unless you have a chance to look at it and
agree that this is something close to the right direction to head.

                                                                      - Tom

postgres=> create table x (i int);
CREATE
postgres=> insert into x values (1);
INSERT 18121 1
postgres=> select i from x where i = 1;
i
-
1
(1 row)

postgres=> select i from x where i in (select i from x);
ERROR:  transformExpr: does not know how to transform node 604
postgres=> select i from x where (i, 1) in (select i, 1 from x);
ERROR:  transformExpr: does not know how to transform node 501
postgres=>
%{ /* -*-text-*- */

/*#define YYDEBUG 1*/
/*-------------------------------------------------------------------------
 *
 * gram.y--
 *      POSTGRES SQL YACC rules/actions
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *      $Header: /usr/local/cvsroot/pgsql/src/backend/parser/gram.y,v 1.88 1998/01/10 04:29:50 momjian Exp $
 *
 * HISTORY
 *      AUTHOR            DATE            MAJOR EVENT
 *      Andrew Yu            Sept, 1994        POSTQUEL to SQL conversion
 *      Andrew Yu            Oct, 1994        lispy code conversion
 *
 * NOTES
 *      CAPITALS are used to represent terminal symbols.
 *      non-capitals are used to represent non-terminals.
 *      SQL92-specific syntax is separated from plain SQL/Postgres syntax
 *      to help isolate the non-extensible portions of the parser.
 *
 *      if you use list, make sure the datum is a node so that the printing
 *      routines work
 *
 * WARNING
 *      sometimes we assign constants to makeStrings. Make sure we don't free
 *      those.
 *
 *-------------------------------------------------------------------------
 */
#include <string.h>
#include <ctype.h>

#include "postgres.h"
#include "nodes/parsenodes.h"
#include "nodes/print.h"
#include "parser/gramparse.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/palloc.h"
#include "catalog/catname.h"
#include "utils/elog.h"
#include "access/xact.h"

static char saved_relname[NAMEDATALEN];  /* need this for complex attributes */
static bool QueryIsRule = FALSE;
static Node *saved_In_Expr;
static Oid    *param_type_info;
static int    pfunc_num_args;
extern List *parsetree;


/*
 * If you need access to certain yacc-generated variables and find that
 * they're static by default, uncomment the next line.  (this is not a
 * problem, yet.)
 */
/*#define __YYSCLASS*/

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);
static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr);
static void param_type_init(Oid *typev, int nargs);

Oid    param_type(int t); /* used in parse_expr.c */

/* old versions of flex define this as a macro */
#if defined(yywrap)
#undef yywrap
#endif /* yywrap */
%}


%union
{
    double                dval;
    int                    ival;
    char                chr;
    char                *str;
    bool                boolean;
    bool*                pboolean;    /* for pg_user privileges */
    List                *list;
    Node                *node;
    Value                *value;

    Attr                *attr;

    TypeName            *typnam;
    DefElem                *defelt;
    ParamString            *param;
    SortGroupBy            *sortgroupby;
    IndexElem            *ielem;
    RangeVar            *range;
    RelExpr                *relexp;
    A_Indices            *aind;
    ResTarget            *target;
    ParamNo                *paramno;

    VersionStmt            *vstmt;
    DefineStmt            *dstmt;
    RuleStmt            *rstmt;
    InsertStmt            *astmt;
}

%type <node>    stmt,
        AddAttrStmt, ClosePortalStmt,
        CopyStmt, CreateStmt, CreateAsStmt, CreateSeqStmt, DefineStmt, DestroyStmt,
        ExtendStmt, FetchStmt,    GrantStmt, CreateTrigStmt, DropTrigStmt,
        CreatePLangStmt, DropPLangStmt,
        IndexStmt, ListenStmt, OptimizableStmt,
        ProcedureStmt,     RecipeStmt, RemoveAggrStmt, RemoveOperStmt,
        RemoveFuncStmt, RemoveStmt,
        RenameStmt, RevokeStmt, RuleStmt, TransactionStmt, ViewStmt, LoadStmt,
        CreatedbStmt, DestroydbStmt, VacuumStmt, CursorStmt, SubSelect,
        UpdateStmt, InsertStmt, SelectStmt, NotifyStmt, DeleteStmt, ClusterStmt,
        ExplainStmt, VariableSetStmt, VariableShowStmt, VariableResetStmt,
        CreateUserStmt, AlterUserStmt, DropUserStmt

%type <rtstmt>

%type <str>        opt_database, location

%type <pboolean> user_createdb_clause, user_createuser_clause
%type <str>   user_passwd_clause
%type <str>   user_valid_clause
%type <list>  user_group_list, user_group_clause

%type <str>        join_expr, join_outer, join_spec
%type <boolean> TriggerActionTime, TriggerForSpec, PLangTrusted

%type <str>        TriggerEvents, TriggerFuncArg

%type <str>        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, aggr_argtype

%type <str>        opt_id, opt_portal_name,
        all_Op, MathOp, opt_name, opt_unique,
        result, OptUseOp, opt_class, SpecialRuleRelation

%type <str>        privileges, operation_commalist, grantee
%type <chr>        operation, TriggerOneEvent

%type <list>    stmtblock, stmtmulti,
        relation_name_list, OptTableElementList,
        OptInherit, definition,
        opt_with, def_args, def_name_list, func_argtypes,
        oper_argtypes, OptStmtList, OptStmtBlock, OptStmtMulti,
        opt_column_list, columnList, opt_va_list, va_list,
        sort_clause, sortby_list, index_params, index_list, name_list,
        from_clause, from_list, opt_array_bounds, nest_array_bounds,
        expr_list, attrs, res_target_list, res_target_list2,
        def_list, opt_indirection, group_clause, groupby_list, TriggerFuncArgs

%type <list>    union_clause, select_list
%type <list>    join_list
%type <sortgroupby>
                join_using
%type <boolean>    opt_union

%type <node>    position_expr
%type <list>    extract_list, position_list
%type <list>    substr_list, substr_from, substr_for, trim_list
%type <list>    opt_interval

%type <boolean> opt_inh_star, opt_binary, opt_instead, opt_with_copy,
                index_opt_unique, opt_verbose, opt_analyze

%type <ival>    copy_dirn, def_type, opt_direction, remove_type,
                opt_column, event

%type <ival>    fetch_how_many

%type <list>    OptSeqList
%type <defelt>    OptSeqElem

%type <dstmt>    def_rest
%type <astmt>    insert_rest

%type <node>    OptTableElement, ConstraintElem
%type <node>    columnDef, alter_clause
%type <defelt>    def_elem
%type <node>    def_arg, columnElem, where_clause,
                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 <str>        RowOp, row_opt
%type <list>    OptCreateAs, CreateAsList
%type <node>    CreateAsElement
%type <value>    NumConst
%type <attr>    event_object, attr
%type <sortgroupby>        groupby
%type <sortgroupby>        sortby
%type <ielem>    index_elem, func_index
%type <range>    from_val
%type <relexp>    relation_expr
%type <target>    res_target_el, res_target_el2
%type <paramno> ParamNo

%type <typnam>    Typename, opt_type, Array, Generic, Character, Datetime, Numeric
%type <str>        generic, character, datetime
%type <str>        opt_charset, opt_collate
%type <str>        opt_float, opt_numeric, opt_decimal
%type <boolean>    opt_varying, opt_timezone

%type <ival>    Iconst
%type <str>        Sconst
%type <str>        Id, var_value, zone_value
%type <str>        ColId, ColLabel

%type <node>    TableConstraint
%type <list>    constraint_list, constraint_expr
%type <list>    default_list, default_expr
%type <list>    ColQualList, ColQualifier
%type <node>    ColConstraint, ColConstraintElem
%type <list>    key_actions, key_action
%type <str>        key_match, key_reference

/*
 * If you make any token changes, remember to:
 *        - use "yacc -d" and update parse.h
 *        - update the keyword table in parser/keywords.c
 */

/* Reserved word tokens
 * SQL92 syntax has many type-specific constructs.
 * So, go ahead and make these types reserved words,
 *  and call-out the syntax explicitly.
 * This gets annoying when trying to also retain Postgres' nice
 *  type-extensible features, but we don't really have a choice.
 * - thomas 1997-10-11
 */

/* Keywords (in SQL92 reserved words) */
%token    ACTION, ADD, ALL, ALTER, AND, ANY, AS, ASC,
        BEGIN_TRANS, BETWEEN, BOTH, BY,
        CASCADE, CAST, CHAR, CHARACTER, CHECK, CLOSE, COLLATE, COLUMN, COMMIT,
        CONSTRAINT, CREATE, CROSS, CURRENT, CURRENT_DATE, CURRENT_TIME,
        CURRENT_TIMESTAMP, CURRENT_USER, CURSOR,
        DAY_P, DECIMAL, DECLARE, DEFAULT, DELETE, DESC, DISTINCT, DOUBLE, DROP,
        END_TRANS, EXECUTE, EXISTS, EXTRACT,
        FETCH, FLOAT, FOR, FOREIGN, FROM, FULL,
        GRANT, GROUP, HAVING, HOUR_P,
        IN, INNER_P, INSERT, INTERVAL, INTO, IS,
        JOIN, KEY, LANGUAGE, LEADING, LEFT, LIKE, LOCAL,
        MATCH, MINUTE_P, MONTH_P,
        NATIONAL, NATURAL, NCHAR, NO, NOT, NOTIFY, NULL_P, NUMERIC,
        ON, OPTION, OR, ORDER, OUTER_P,
        PARTIAL, POSITION, PRECISION, PRIMARY, PRIVILEGES, PROCEDURE, PUBLIC,
        REFERENCES, REVOKE, RIGHT, ROLLBACK,
        SECOND_P, SELECT, SET, SOME, SUBSTRING,
        TABLE, TIME, TIMESTAMP, TO, TRAILING, TRANSACTION, TRIM,
        UNION, UNIQUE, UPDATE, USING,
        VALUES, VARCHAR, VARYING, VERBOSE, VERSION, VIEW,
        WHERE, WITH, WORK, YEAR_P, ZONE

/* Keywords (in SQL3 reserved words) */
%token    FALSE_P, TRIGGER, TRUE_P

/* Keywords (in SQL92 non-reserved words) */
%token    TYPE_P

/* Keywords for Postgres support (not in SQL92 reserved words) */
%token    ABORT_TRANS, ACL, AFTER, AGGREGATE, ANALYZE,
        APPEND, BACKWARD, BEFORE, BINARY, CHANGE, CLUSTER, COPY,
        DATABASE, DELIMITERS, DO, EXPLAIN, EXTEND,
        FORWARD, FUNCTION, HANDLER,
        INDEX, INHERITS, INSTEAD, ISNULL,
        LANCOMPILER, LISTEN, LOAD, LOCATION, MERGE, MOVE,
        NEW, NONE, NOTHING, NOTNULL, OIDS, OPERATOR, PROCEDURAL,
        RECIPE, RENAME, REPLACE, RESET, RETRIEVE, RETURNS, RULE,
        SEQUENCE, SETOF, SHOW, STDIN, STDOUT, TRUSTED,
        VACUUM, VERBOSE, VERSION

/* Keywords (obsolete; retain temporarily for parser - thomas 1997-12-04) */
%token    ARCHIVE

/*
 * Tokens for pg_passwd support.  The CREATEDB and CREATEUSER tokens should go away
 * when some sort of pg_privileges relation is introduced.
 *
 *                                    Todd A. Brandys
 */
%token    USER, PASSWORD, CREATEDB, NOCREATEDB, CREATEUSER, NOCREATEUSER, VALID, UNTIL

/* Special keywords, not in the query language - see the "lex" file */
%token <str>    IDENT, SCONST, Op
%token <ival>    ICONST, PARAM
%token <dval>    FCONST

/* these are not real. they are here so that they get generated as #define's*/
%token            OP

/* precedence */
%left        OR
%left        AND
%right        NOT
%right        '='
%nonassoc    '<' '>'
%nonassoc    LIKE
%nonassoc    BETWEEN
%nonassoc    IN
%nonassoc    Op                /* multi-character ops and user-defined operators */
%nonassoc    NOTNULL
%nonassoc    ISNULL
%nonassoc    IS
%left        '+' '-'
%left        '*' '/'
%left        '|'                /* this is the relation union op, not logical or */
/* Unary Operators */
%right        ':'
%left        ';'                /* end of statement or natural log */
%right        UMINUS
%left        '.'
%left        '[' ']'
%nonassoc    TYPECAST
%nonassoc    REDUCE
%left        UNION
%%

stmtblock:  stmtmulti
                { parsetree = $1; }
        | stmt
                { parsetree = lcons($1,NIL); }
        ;

stmtmulti:  stmtmulti stmt ';'
                { $$ = lappend($1, $2); }
        | stmtmulti stmt
                { $$ = lappend($1, $2); }
        | stmt ';'
                { $$ = lcons($1,NIL); }
        ;

stmt :      AddAttrStmt
        | AlterUserStmt
        | ClosePortalStmt
        | CopyStmt
        | CreateStmt
        | CreateAsStmt
        | CreateSeqStmt
        | CreatePLangStmt
        | CreateTrigStmt
        | CreateUserStmt
        | ClusterStmt
        | DefineStmt
        | DestroyStmt
        | DropPLangStmt
        | DropTrigStmt
        | DropUserStmt
        | ExtendStmt
        | ExplainStmt
        | FetchStmt
        | GrantStmt
        | IndexStmt
        | ListenStmt
        | ProcedureStmt
        | RecipeStmt
        | RemoveAggrStmt
        | RemoveOperStmt
        | RemoveFuncStmt
        | RemoveStmt
        | RenameStmt
        | RevokeStmt
        | OptimizableStmt
        | RuleStmt
        | TransactionStmt
        | ViewStmt
        | LoadStmt
        | CreatedbStmt
        | DestroydbStmt
        | VacuumStmt
        | VariableSetStmt
        | VariableShowStmt
        | VariableResetStmt
        ;

/*****************************************************************************
 *
 * Create a new Postgres DBMS user
 *
 *
 *****************************************************************************/

CreateUserStmt:  CREATE USER Id user_passwd_clause user_createdb_clause
            user_createuser_clause user_group_clause user_valid_clause
                {
                    CreateUserStmt *n = makeNode(CreateUserStmt);
                    n->user = $3;
                    n->password = $4;
                    n->createdb = $5;
                    n->createuser = $6;
                    n->groupElts = $7;
                    n->validUntil = $8;
                    $$ = (Node *)n;
                }
        ;

/*****************************************************************************
 *
 * Alter a postresql DBMS user
 *
 *
 *****************************************************************************/

AlterUserStmt:  ALTER USER Id user_passwd_clause user_createdb_clause
            user_createuser_clause user_group_clause user_valid_clause
                {
                    AlterUserStmt *n = makeNode(AlterUserStmt);
                    n->user = $3;
                    n->password = $4;
                    n->createdb = $5;
                    n->createuser = $6;
                    n->groupElts = $7;
                    n->validUntil = $8;
                    $$ = (Node *)n;
                }
        ;

/*****************************************************************************
 *
 * Drop a postresql DBMS user
 *
 *
 *****************************************************************************/

DropUserStmt:  DROP USER Id
                {
                    DropUserStmt *n = makeNode(DropUserStmt);
                    n->user = $3;
                    $$ = (Node *)n;
                }
        ;

user_passwd_clause:  WITH PASSWORD Id            { $$ = $3; }
            | /*EMPTY*/                            { $$ = NULL; }
        ;

user_createdb_clause:  CREATEDB
                {
                    bool*  b;
                    $$ = (b = (bool*)palloc(sizeof(bool)));
                    *b = true;
                }
            | NOCREATEDB
                {
                    bool*  b;
                    $$ = (b = (bool*)palloc(sizeof(bool)));
                    *b = false;
                }
            | /*EMPTY*/                            { $$ = NULL; }
        ;

user_createuser_clause:  CREATEUSER
                {
                    bool*  b;
                    $$ = (b = (bool*)palloc(sizeof(bool)));
                    *b = true;
                }
            | NOCREATEUSER
                {
                    bool*  b;
                    $$ = (b = (bool*)palloc(sizeof(bool)));
                    *b = false;
                }
            | /*EMPTY*/                            { $$ = NULL; }
        ;

user_group_list:  user_group_list ',' Id
                {
                    $$ = lcons((void*)makeString($3), $1);
                }
            | Id
                {
                    $$ = lcons((void*)makeString($1), NIL);
                }
        ;

user_group_clause:  IN GROUP user_group_list    { $$ = $3; }
            | /*EMPTY*/                            { $$ = NULL; }
        ;

user_valid_clause:  VALID UNTIL SCONST            { $$ = $3; }
            | /*EMPTY*/                            { $$ = NULL; }
        ;

/*****************************************************************************
 *
 * Set PG internal variable
 *      SET name TO 'var_value'
 * Include SQL92 syntax (thomas 1997-10-22):
 *    SET TIME ZONE 'var_value'
 *
 *****************************************************************************/

VariableSetStmt:  SET ColId TO var_value
                {
                    VariableSetStmt *n = makeNode(VariableSetStmt);
                    n->name  = $2;
                    n->value = $4;
                    $$ = (Node *) n;
                }
        | SET ColId '=' 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; }
        | DEFAULT            { $$ = NULL; }
        ;

zone_value:  Sconst            { $$ = $1; }
        | DEFAULT            { $$ = NULL; }
        | LOCAL                { $$ = "default"; }
        ;

VariableShowStmt:  SHOW ColId
                {
                    VariableShowStmt *n = makeNode(VariableShowStmt);
                    n->name  = $2;
                    $$ = (Node *) n;
                }
        | SHOW TIME ZONE
                {
                    VariableShowStmt *n = makeNode(VariableShowStmt);
                    n->name  = "timezone";
                    $$ = (Node *) n;
                }
        ;

VariableResetStmt:    RESET ColId
                {
                    VariableResetStmt *n = makeNode(VariableResetStmt);
                    n->name  = $2;
                    $$ = (Node *) n;
                }
        | RESET TIME ZONE
                {
                    VariableResetStmt *n = makeNode(VariableResetStmt);
                    n->name  = "timezone";
                    $$ = (Node *) n;
                }
        ;


/*****************************************************************************
 *
 *        QUERY :
 *                addattr ( attr1 = type1 .. attrn = typen ) to <relname> [*]
 *
 *****************************************************************************/

AddAttrStmt:  ALTER TABLE relation_name opt_inh_star alter_clause
                {
                    AddAttrStmt *n = makeNode(AddAttrStmt);
                    n->relname = $3;
                    n->inh = $4;
                    n->colDef = $5;
                    $$ = (Node *)n;
                }
        ;

alter_clause:  ADD opt_column columnDef
                {
                    $$ = $3;
                }
            | ADD '(' OptTableElementList ')'
                {
                    Node *lp = lfirst($3);

                    if (length($3) != 1)
                        elog(ERROR,"ALTER TABLE/ADD() allows one column only");
                    $$ = lp;
                }
            | DROP opt_column ColId
                {    elog(ERROR,"ALTER TABLE/DROP COLUMN not yet implemented"); }
            | ALTER opt_column ColId SET DEFAULT default_expr
                {    elog(ERROR,"ALTER TABLE/ALTER COLUMN/SET DEFAULT not yet implemented"); }
            | ALTER opt_column ColId DROP DEFAULT
                {    elog(ERROR,"ALTER TABLE/ALTER COLUMN/DROP DEFAULT not yet implemented"); }
            | ADD ConstraintElem
                {    elog(ERROR,"ALTER TABLE/ADD CONSTRAINT not yet implemented"); }
        ;


/*****************************************************************************
 *
 *        QUERY :
 *                close <optname>
 *
 *****************************************************************************/

ClosePortalStmt:  CLOSE opt_id
                {
                    ClosePortalStmt *n = makeNode(ClosePortalStmt);
                    n->portalname = $2;
                    $$ = (Node *)n;
                }
        ;


/*****************************************************************************
 *
 *        QUERY :
 *                COPY [BINARY] <relname> FROM/TO
 *                [USING DELIMITERS <delimiter>]
 *
 *****************************************************************************/

CopyStmt:  COPY opt_binary relation_name opt_with_copy copy_dirn copy_file_name copy_delimiter
                {
                    CopyStmt *n = makeNode(CopyStmt);
                    n->binary = $2;
                    n->relname = $3;
                    n->oids = $4;
                    n->direction = $5;
                    n->filename = $6;
                    n->delimiter = $7;
                    $$ = (Node *)n;
                }
        ;

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
 */
copy_file_name:  Sconst                            { $$ = $1; }
        | STDIN                                    { $$ = NULL; }
        | STDOUT                                { $$ = NULL; }
        ;

opt_binary:  BINARY                                { $$ = TRUE; }
        | /*EMPTY*/                                { $$ = FALSE; }
        ;

opt_with_copy:    WITH OIDS                        { $$ = TRUE; }
        | /*EMPTY*/                                { $$ = FALSE; }
        ;

/*
 * the default copy delimiter is tab but the user can configure it
 */
copy_delimiter:  USING DELIMITERS Sconst        { $$ = $3; }
        | /*EMPTY*/                                { $$ = "\t"; }
        ;


/*****************************************************************************
 *
 *        QUERY :
 *                CREATE relname
 *
 *****************************************************************************/

CreateStmt:  CREATE TABLE relation_name '(' OptTableElementList ')'
                OptInherit OptArchiveType
                {
                    CreateStmt *n = makeNode(CreateStmt);
                    n->relname = $3;
                    n->tableElts = $5;
                    n->inhRelnames = $7;
                    n->constraints = NIL;
                    $$ = (Node *)n;
                }
        ;

OptTableElementList:  OptTableElementList ',' OptTableElement
                                                { $$ = lappend($1, $3); }
            | OptTableElement                    { $$ = lcons($1, NIL); }
            | /*EMPTY*/                            { $$ = NULL; }
        ;

OptTableElement:  columnDef                        { $$ = $1; }
            | TableConstraint                    { $$ = $1; }
        ;

columnDef:  ColId Typename ColQualifier
                {
                    ColumnDef *n = makeNode(ColumnDef);
                    n->colname = $1;
                    n->typename = $2;
                    n->defval = NULL;
                    n->is_not_null = FALSE;
                    n->constraints = $3;
                    $$ = (Node *)n;
                }
        ;

ColQualifier:  ColQualList                        { $$ = $1; }
            | /*EMPTY*/                            { $$ = NULL; }
        ;

ColQualList:  ColQualList ColConstraint            { $$ = lappend($1,$2); }
            | ColConstraint                        { $$ = lcons($1, NIL); }
        ;

ColConstraint:
        CONSTRAINT name ColConstraintElem
                {
                        Constraint *n = (Constraint *)$3;
                        n->name = fmtId($2);
                        $$ = $3;
                }
        | ColConstraintElem
                { $$ = $1; }
        ;

ColConstraintElem:  CHECK '(' constraint_expr ')'
                {
                    Constraint *n = makeNode(Constraint);
                    n->contype = CONSTR_CHECK;
                    n->name = NULL;
                    n->def = FlattenStringList($3);
                    n->keys = NULL;
                    $$ = (Node *)n;
                }
            | DEFAULT default_expr
                {
                    Constraint *n = makeNode(Constraint);
                    n->contype = CONSTR_DEFAULT;
                    n->name = NULL;
                    n->def = FlattenStringList($2);
                    n->keys = NULL;
                    $$ = (Node *)n;
                }
            | NOT NULL_P
                {
                    Constraint *n = makeNode(Constraint);
                    n->contype = CONSTR_NOTNULL;
                    n->name = NULL;
                    n->def = NULL;
                    n->keys = NULL;
                    $$ = (Node *)n;
                }
            | UNIQUE
                {
                    Constraint *n = makeNode(Constraint);
                    n->contype = CONSTR_UNIQUE;
                    n->name = NULL;
                    n->def = NULL;
                    n->keys = NULL;
                    $$ = (Node *)n;
                }
            | PRIMARY KEY
                {
                    Constraint *n = makeNode(Constraint);
                    n->contype = CONSTR_PRIMARY;
                    n->name = NULL;
                    n->def = NULL;
                    n->keys = NULL;
                    $$ = (Node *)n;
                }
            | REFERENCES ColId opt_column_list key_match key_actions
                {
                    elog(NOTICE,"CREATE TABLE/FOREIGN KEY clause ignored; not yet implemented");
                    $$ = NULL;
                }
        ;

default_list:  default_list ',' default_expr
                {
                    $$ = lappend($1,makeString(","));
                    $$ = nconc($$, $3);
                }
            | default_expr
                {
                    $$ = $1;
                }
        ;

default_expr:  AexprConst
                {    $$ = makeConstantList((A_Const *) $1); }
            | NULL_P
                {    $$ = lcons( makeString("NULL"), NIL); }
            | '-' default_expr %prec UMINUS
                {    $$ = lcons( makeString( "-"), $2); }
            | default_expr '+' default_expr
                {    $$ = nconc( $1, lcons( makeString( "+"), $3)); }
            | default_expr '-' default_expr
                {    $$ = nconc( $1, lcons( makeString( "-"), $3)); }
            | default_expr '/' default_expr
                {    $$ = nconc( $1, lcons( makeString( "/"), $3)); }
            | default_expr '*' default_expr
                {    $$ = nconc( $1, lcons( makeString( "*"), $3)); }
            | default_expr '=' default_expr
                {    elog(ERROR,"boolean expressions not supported in DEFAULT"); }
            | default_expr '<' default_expr
                {    elog(ERROR,"boolean expressions not supported in DEFAULT"); }
            | default_expr '>' default_expr
                {    elog(ERROR,"boolean expressions not supported in DEFAULT"); }
            | ':' default_expr
                {    $$ = lcons( makeString( ":"), $2); }
            | ';' default_expr
                {    $$ = lcons( makeString( ";"), $2); }
            | '|' default_expr
                {    $$ = lcons( makeString( "|"), $2); }
            | default_expr TYPECAST Typename
                {
                    $3->name = fmtId($3->name);
                    $$ = nconc( lcons( makeString( "CAST"), $1), makeList( makeString("AS"), $3, -1));
                }
            | CAST default_expr AS Typename
                {
                    $4->name = fmtId($4->name);
                    $$ = nconc( lcons( makeString( "CAST"), $2), makeList( makeString("AS"), $4, -1));
                }
            | '(' default_expr ')'
                {    $$ = lappend( lcons( makeString( "("), $2), makeString( ")")); }
            | name '(' ')'
                {
                    $$ = makeList( makeString($1), makeString("("), -1);
                    $$ = lappend( $$, makeString(")"));
                }
            | name '(' default_list ')'
                {
                    $$ = makeList( makeString($1), makeString("("), -1);
                    $$ = nconc( $$, $3);
                    $$ = lappend( $$, makeString(")"));
                }
            | default_expr Op default_expr
                {
                    if (!strcmp("<=", $2) || !strcmp(">=", $2))
                        elog(ERROR,"boolean expressions not supported in DEFAULT");
                    $$ = nconc( $1, lcons( makeString( $2), $3));
                }
            | Op default_expr
                {    $$ = lcons( makeString( $1), $2); }
            | default_expr Op
                {    $$ = lappend( $1, makeString( $2)); }
            /* XXX - thomas 1997-10-07 v6.2 function-specific code to be changed */
            | CURRENT_DATE
                {    $$ = lcons( makeString( "date( 'current'::datetime + '0 sec')"), NIL); }
            | CURRENT_TIME
                {    $$ = lcons( makeString( "'now'::time"), NIL); }
            | CURRENT_TIME '(' Iconst ')'
                {
                    if ($3 != 0)
                        elog(NOTICE,"CURRENT_TIME(%d) precision not implemented; zero used instead",$3);
                    $$ = lcons( makeString( "'now'::time"), NIL);
                }
            | CURRENT_TIMESTAMP
                {    $$ = lcons( makeString( "now()"), NIL); }
            | CURRENT_TIMESTAMP '(' Iconst ')'
                {
                    if ($3 != 0)
                        elog(NOTICE,"CURRENT_TIMESTAMP(%d) precision not implemented; zero used instead",$3);
                    $$ = lcons( makeString( "now()"), NIL);
                }
            | CURRENT_USER
                {    $$ = lcons( makeString( "CURRENT_USER"), NIL); }
        ;

/* ConstraintElem specifies constraint syntax which is not embedded into
 *  a column definition. ColConstraintElem specifies the embedded form.
 * - thomas 1997-12-03
 */
TableConstraint:  CONSTRAINT name ConstraintElem
                {
                        Constraint *n = (Constraint *)$3;
                        n->name = fmtId($2);
                        $$ = $3;
                }
        | ConstraintElem
                { $$ = $1; }
        ;

ConstraintElem:  CHECK '(' constraint_expr ')'
                {
                    Constraint *n = makeNode(Constraint);
                    n->contype = CONSTR_CHECK;
                    n->name = NULL;
                    n->def = FlattenStringList($3);
                    $$ = (Node *)n;
                }
        | UNIQUE '(' columnList ')'
                {
                    Constraint *n = makeNode(Constraint);
                    n->contype = CONSTR_UNIQUE;
                    n->name = NULL;
                    n->def = NULL;
                    n->keys = $3;
                    $$ = (Node *)n;
                }
        | PRIMARY KEY '(' columnList ')'
                {
                    Constraint *n = makeNode(Constraint);
                    n->contype = CONSTR_PRIMARY;
                    n->name = NULL;
                    n->def = NULL;
                    n->keys = $4;
                    $$ = (Node *)n;
                }
        | FOREIGN KEY '(' columnList ')' REFERENCES ColId opt_column_list key_match key_actions
                {    elog(NOTICE,"CREATE TABLE/FOREIGN KEY clause ignored; not yet implemented"); }
        ;

constraint_list:  constraint_list ',' constraint_expr
                {
                    $$ = lappend($1,makeString(","));
                    $$ = nconc($$, $3);
                }
            | constraint_expr
                {
                    $$ = $1;
                }
        ;

constraint_expr:  AexprConst
                {    $$ = makeConstantList((A_Const *) $1); }
            | NULL_P
                {    $$ = lcons( makeString("NULL"), NIL); }
            | ColId
                {
                    $$ = lcons( makeString(fmtId($1)), NIL);
                }
            | '-' constraint_expr %prec UMINUS
                {    $$ = lcons( makeString( "-"), $2); }
            | constraint_expr '+' constraint_expr
                {    $$ = nconc( $1, lcons( makeString( "+"), $3)); }
            | constraint_expr '-' constraint_expr
                {    $$ = nconc( $1, lcons( makeString( "-"), $3)); }
            | constraint_expr '/' constraint_expr
                {    $$ = nconc( $1, lcons( makeString( "/"), $3)); }
            | constraint_expr '*' constraint_expr
                {    $$ = nconc( $1, lcons( makeString( "*"), $3)); }
            | constraint_expr '=' constraint_expr
                {    $$ = nconc( $1, lcons( makeString( "="), $3)); }
            | constraint_expr '<' constraint_expr
                {    $$ = nconc( $1, lcons( makeString( "<"), $3)); }
            | constraint_expr '>' constraint_expr
                {    $$ = nconc( $1, lcons( makeString( ">"), $3)); }
            | ':' constraint_expr
                {    $$ = lcons( makeString( ":"), $2); }
            | ';' constraint_expr
                {    $$ = lcons( makeString( ";"), $2); }
            | '|' constraint_expr
                {    $$ = lcons( makeString( "|"), $2); }
            | constraint_expr TYPECAST Typename
                {
                    $3->name = fmtId($3->name);
                    $$ = nconc( lcons( makeString( "CAST"), $1), makeList( makeString("AS"), $3, -1));
                }
            | CAST constraint_expr AS Typename
                {
                    $4->name = fmtId($4->name);
                    $$ = nconc( lcons( makeString( "CAST"), $2), makeList( makeString("AS"), $4, -1));
                }
            | '(' constraint_expr ')'
                {    $$ = lappend( lcons( makeString( "("), $2), makeString( ")")); }
            | name '(' ')'
                {
                    $$ = makeList( makeString($1), makeString("("), -1);
                    $$ = lappend( $$, makeString(")"));
                }
            | name '(' constraint_list ')'
                {
                    $$ = makeList( makeString($1), makeString("("), -1);
                    $$ = nconc( $$, $3);
                    $$ = lappend( $$, makeString(")"));
                }
            | constraint_expr Op constraint_expr
                {    $$ = nconc( $1, lcons( makeString( $2), $3)); }
            | constraint_expr AND constraint_expr
                {    $$ = 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
                {    $$ = lappend( $1, makeString( "IS FALSE")); }
            | constraint_expr IS NOT TRUE_P
                {    $$ = lappend( $1, makeString( "IS NOT TRUE")); }
            | constraint_expr IS NOT FALSE_P
                {    $$ = lappend( $1, makeString( "IS NOT FALSE")); }
        ;

key_match:  MATCH FULL                    { $$ = NULL; }
        | MATCH PARTIAL                    { $$ = NULL; }
        | /*EMPTY*/                        { $$ = NULL; }
        ;

key_actions:  key_action key_action        { $$ = NIL; }
        | key_action                    { $$ = NIL; }
        | /*EMPTY*/                        { $$ = NIL; }
        ;

key_action:  ON DELETE key_reference    { $$ = NIL; }
        | ON UPDATE key_reference        { $$ = NIL; }
        ;

key_reference:  NO ACTION                { $$ = NULL; }
        | CASCADE                        { $$ = NULL; }
        | SET DEFAULT                    { $$ = NULL; }
        | SET NULL_P                    { $$ = NULL; }
        ;

OptInherit:  INHERITS '(' relation_name_list ')'        { $$ = $3; }
        | /*EMPTY*/                                        { $$ = NIL; }
        ;

/*
 *    "ARCHIVE" keyword was removed in 6.3, but we keep it for now
 *  so people can upgrade with old pg_dump scripts. - momjian 1997-11-20(?)
 */
OptArchiveType:  ARCHIVE '=' NONE                        { }
        | /*EMPTY*/                                        { }
        ;

CreateAsStmt:  CREATE TABLE relation_name OptCreateAs AS SubSelect
                {
                    SelectStmt *n = (SelectStmt *)$6;
                    if ($4 != NIL)
                        mapTargetColumns($4, n->targetList);
                    n->into = $3;
                    $$ = (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;
                }
        ;


/*****************************************************************************
 *
 *        QUERY :
 *                CREATE SEQUENCE seqname
 *
 *****************************************************************************/

CreateSeqStmt:    CREATE SEQUENCE relation_name OptSeqList
                {
                    CreateSeqStmt *n = makeNode(CreateSeqStmt);
                    n->seqname = $3;
                    n->options = $4;
                    $$ = (Node *)n;
                }
        ;

OptSeqList:
                OptSeqList OptSeqElem
                { $$ = lappend($1, $2); }
        |        { $$ = NIL; }
        ;

OptSeqElem:        IDENT NumConst
                {
                    $$ = makeNode(DefElem);
                    $$->defname = $1;
                    $$->arg = (Node *)$2;
                }
        |        IDENT
                {
                    $$ = makeNode(DefElem);
                    $$->defname = $1;
                    $$->arg = (Node *)NULL;
                }
        ;

/*****************************************************************************
 *
 *        QUERIES :
 *                CREATE PROCEDURAL LANGUAGE ...
 *                DROP PROCEDURAL LANGUAGE ...
 *
 *****************************************************************************/

CreatePLangStmt:  CREATE PLangTrusted PROCEDURAL LANGUAGE Sconst
            HANDLER def_name LANCOMPILER Sconst
            {
                CreatePLangStmt *n = makeNode(CreatePLangStmt);
                n->plname = $5;
                n->plhandler = $7;
                n->plcompiler = $9;
                n->pltrusted = $2;
                $$ = (Node *)n;
            }
        ;

PLangTrusted:        TRUSTED { $$ = TRUE; }
            |    { $$ = FALSE; }

DropPLangStmt:  DROP PROCEDURAL LANGUAGE Sconst
            {
                DropPLangStmt *n = makeNode(DropPLangStmt);
                n->plname = $4;
                $$ = (Node *)n;
            }
        ;

/*****************************************************************************
 *
 *        QUERIES :
 *                CREATE TRIGGER ...
 *                DROP TRIGGER ...
 *
 *****************************************************************************/

CreateTrigStmt:  CREATE TRIGGER name TriggerActionTime TriggerEvents ON
                relation_name TriggerForSpec EXECUTE PROCEDURE
                name '(' TriggerFuncArgs ')'
                {
                    CreateTrigStmt *n = makeNode(CreateTrigStmt);
                    n->trigname = $3;
                    n->relname = $7;
                    n->funcname = $11;
                    n->args = $13;
                    n->before = $4;
                    n->row = $8;
                    memcpy (n->actions, $5, 4);
                    $$ = (Node *)n;
                }
        ;

TriggerActionTime:        BEFORE    { $$ = TRUE; }
                |        AFTER    { $$ = FALSE; }
        ;

TriggerEvents:    TriggerOneEvent
                    {
                            char *e = palloc (4);
                            e[0] = $1; e[1] = 0; $$ = e;
                    }
                | TriggerOneEvent OR TriggerOneEvent
                    {
                            char *e = palloc (4);
                            e[0] = $1; e[1] = $3; e[2] = 0; $$ = e;
                    }
                | TriggerOneEvent OR TriggerOneEvent OR TriggerOneEvent
                    {
                            char *e = palloc (4);
                            e[0] = $1; e[1] = $3; e[2] = $5; e[3] = 0;
                            $$ = e;
                    }
        ;

TriggerOneEvent:        INSERT    { $$ = 'i'; }
                |        DELETE    { $$ = 'd'; }
                |        UPDATE    { $$ = 'u'; }
        ;

TriggerForSpec:  FOR name name
                {
                        if ( strcmp ($2, "each") != 0 )
                                elog(ERROR,"parser: syntax error near %s",$2);
                        if ( strcmp ($3, "row") == 0 )
                                $$ = TRUE;
                        else if ( strcmp ($3, "statement") == 0 )
                                $$ = FALSE;
                        else
                                elog(ERROR,"parser: syntax error near %s",$3);
                }
        ;

TriggerFuncArgs:  TriggerFuncArg
                { $$ = lcons($1, NIL); }
        | TriggerFuncArgs ',' TriggerFuncArg
                { $$ = lappend($1, $3); }
        | /*EMPTY*/
                { $$ = NIL; }
        ;

TriggerFuncArg:  ICONST
                    {
                        char *s = (char *) palloc (256);
                        sprintf (s, "%d", $1);
                        $$ = s;
                    }
                | FCONST
                    {
                        char *s = (char *) palloc (256);
                        sprintf (s, "%g", $1);
                        $$ = s;
                    }
                | Sconst        {  $$ = $1; }
                | IDENT            {  $$ = $1; }
        ;

DropTrigStmt:    DROP TRIGGER name ON relation_name
                {
                    DropTrigStmt *n = makeNode(DropTrigStmt);
                    n->trigname = $3;
                    n->relname = $5;
                    $$ = (Node *) n;
                }
        ;


/*****************************************************************************
 *
 *        QUERY :
 *                define (type,operator,aggregate)
 *
 *****************************************************************************/

DefineStmt:  CREATE def_type def_rest
                {
                    $3->defType = $2;
                    $$ = (Node *)$3;
                }
        ;

def_rest:  def_name definition
                {
                    $$ = makeNode(DefineStmt);
                    $$->defname = $1;
                    $$->definition = $2;
                }
        ;

def_type:  OPERATOR                            { $$ = OPERATOR; }
        | TYPE_P                            { $$ = TYPE_P; }
        | AGGREGATE                            { $$ = AGGREGATE; }
        ;

def_name:  PROCEDURE                        { $$ = "procedure"; }
        | JOIN                                { $$ = "join"; }
        | ColId                                { $$ = $1; }
        | MathOp                            { $$ = $1; }
        | Op                                { $$ = $1; }
        ;

definition:  '(' def_list ')'                { $$ = $2; }
        ;

def_list:  def_elem                            { $$ = lcons($1, NIL); }
        | def_list ',' def_elem                { $$ = lappend($1, $3); }
        ;

def_elem:  def_name '=' def_arg
                {
                    $$ = makeNode(DefElem);
                    $$->defname = $1;
                    $$->arg = (Node *)$3;
                }
        | def_name
                {
                    $$ = makeNode(DefElem);
                    $$->defname = $1;
                    $$->arg = (Node *)NULL;
                }
        | DEFAULT '=' def_arg
                {
                    $$ = makeNode(DefElem);
                    $$->defname = "default";
                    $$->arg = (Node *)$3;
                }
        ;

def_arg:  ColId                            {  $$ = (Node *)makeString($1); }
        | all_Op                        {  $$ = (Node *)makeString($1); }
        | NumConst                        {  $$ = (Node *)$1; /* already a Value */ }
        | Sconst                        {  $$ = (Node *)makeString($1); }
        | SETOF ColId
                {
                    TypeName *n = makeNode(TypeName);
                    n->name = $2;
                    n->setof = TRUE;
                    n->arrayBounds = NULL;
                    $$ = (Node *)n;
                }
        | DOUBLE                        {  $$ = (Node *)makeString("double"); }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                destroy <relname1> [, <relname2> .. <relnameN> ]
 *
 *****************************************************************************/

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;
                    $$ = (Node *)n;
                }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *            fetch/move [forward | backward] [number | all ] [ in <portalname> ]
 *
 *****************************************************************************/

FetchStmt:    FETCH opt_direction fetch_how_many opt_portal_name
                {
                    FetchStmt *n = makeNode(FetchStmt);
                    n->direction = $2;
                    n->howMany = $3;
                    n->portalname = $4;
                    n->ismove = false;
                    $$ = (Node *)n;
                }
        |    MOVE opt_direction fetch_how_many opt_portal_name
                {
                    FetchStmt *n = makeNode(FetchStmt);
                    n->direction = $2;
                    n->howMany = $3;
                    n->portalname = $4;
                    n->ismove = TRUE;
                    $$ = (Node *)n;
                }
        ;

opt_direction:    FORWARD                            { $$ = FORWARD; }
        | BACKWARD                                { $$ = BACKWARD; }
        | /*EMPTY*/                                { $$ = FORWARD; /* default */ }
        ;

fetch_how_many:  Iconst
               { $$ = $1;
                 if ($1 <= 0) elog(ERROR,"Please specify nonnegative count for fetch"); }
        | ALL                            { $$ = 0; /* 0 means fetch all tuples*/ }
        | /*EMPTY*/                        { $$ = 1; /*default*/ }
        ;

opt_portal_name:  IN name                { $$ = $2; }
        | /*EMPTY*/                        { $$ = NULL; }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                GRANT [privileges] ON [relation_name_list] TO [GROUP] grantee
 *
 *****************************************************************************/

GrantStmt:  GRANT privileges ON relation_name_list TO grantee opt_with_grant
                {
                    $$ = (Node*)makeAclStmt($2,$4,$6,'+');
                    free($2);
                    free($6);
                }
        ;

privileges:  ALL PRIVILEGES
                {
                 $$ = aclmakepriv("rwaR",0);
                }
        | ALL
                {
                 $$ = aclmakepriv("rwaR",0);
                }
        | operation_commalist
                {
                 $$ = $1;
                }
        ;

operation_commalist:  operation
                {
                        $$ = aclmakepriv("",$1);
                }
        | operation_commalist ',' operation
                {
                        $$ = aclmakepriv($1,$3);
                        free($1);
                }
        ;

operation:  SELECT
                {
                        $$ = ACL_MODE_RD_CHR;
                }
        | INSERT
                {
                        $$ = ACL_MODE_AP_CHR;
                }
        | UPDATE
                {
                        $$ = ACL_MODE_WR_CHR;
                }
        | DELETE
                {
                        $$ = ACL_MODE_WR_CHR;
                }
        | RULE
                {
                        $$ = ACL_MODE_RU_CHR;
                }
        ;

grantee:  PUBLIC
                {
                        $$ = aclmakeuser("A","");
                }
        | GROUP ColId
                {
                        $$ = aclmakeuser("G",$2);
                }
        | ColId
                {
                        $$ = aclmakeuser("U",$1);
                }
        ;

opt_with_grant:  WITH GRANT OPTION
                {
                    yyerror("WITH GRANT OPTION is not supported.  Only relation owners can set privileges");
                 }
        | /*EMPTY*/
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                REVOKE [privileges] ON [relation_name] FROM [user]
 *
 *****************************************************************************/

RevokeStmt:  REVOKE privileges ON relation_name_list FROM grantee
                {
                    $$ = (Node*)makeAclStmt($2,$4,$6,'-');
                    free($2);
                    free($6);
                }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                create index <indexname> on <relname>
 *                  using <access> "(" (<col> with <op>)+ ")" [with
 *                  <target_list>]
 *
 *    [where <qual>] is not supported anymore
 *****************************************************************************/

IndexStmt:    CREATE index_opt_unique INDEX index_name ON relation_name
            access_method_clause '(' index_params ')' opt_with
                {
                    /* should check that access_method is valid,
                       etc ... but doesn't */
                    IndexStmt *n = makeNode(IndexStmt);
                    n->unique = $2;
                    n->idxname = $4;
                    n->relname = $6;
                    n->accessMethod = $7;
                    n->indexParams = $9;
                    n->withClause = $11;
                    n->whereClause = NULL;
                    $$ = (Node *)n;
                }
        ;

index_opt_unique:  UNIQUE                        { $$ = TRUE; }
        | /*EMPTY*/                                { $$ = FALSE; }
        ;

access_method_clause:  USING access_method        { $$ = $2; }
        | /*EMPTY*/                                { $$ = "btree"; }
        ;

index_params:  index_list                        { $$ = $1; }
        | func_index                            { $$ = lcons($1,NIL); }
        ;

index_list:  index_list ',' index_elem            { $$ = lappend($1, $3); }
        | index_elem                            { $$ = lcons($1, NIL); }
        ;

func_index:  name '(' name_list ')' opt_type opt_class
                {
                    $$ = makeNode(IndexElem);
                    $$->name = $1;
                    $$->args = $3;
                    $$->class = $6;
                    $$->tname = $5;
                }
          ;

index_elem:  attr_name opt_type opt_class
                {
                    $$ = makeNode(IndexElem);
                    $$->name = $1;
                    $$->args = NIL;
                    $$->class = $3;
                    $$->tname = $2;
                }
        ;

opt_type:  ':' Typename                            { $$ = $2; }
        | FOR Typename                            { $$ = $2; }
        | /*EMPTY*/                                { $$ = NULL; }
        ;

/* opt_class "WITH class" conflicts with preceeding opt_type
 *  for Typename of "TIMESTAMP WITH TIME ZONE"
 * So, remove "WITH class" from the syntax. OK??
 * - thomas 1997-10-12
 *        | WITH class                            { $$ = $2; }
 */
opt_class:  class                                { $$ = $1; }
        | USING class                            { $$ = $2; }
        | /*EMPTY*/                                { $$ = NULL; }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                extend index <indexname> [where <qual>]
 *
 *****************************************************************************/

ExtendStmt:  EXTEND INDEX index_name where_clause
                {
                    ExtendStmt *n = makeNode(ExtendStmt);
                    n->idxname = $3;
                    n->whereClause = $4;
                    $$ = (Node *)n;
                }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                execute recipe <recipeName>
 *
 *****************************************************************************/

RecipeStmt:  EXECUTE RECIPE recipe_name
                {
                    RecipeStmt *n;
                    if (!IsTransactionBlock())
                        elog(ERROR,"EXECUTE RECIPE may only be used in begin/end transaction blocks");

                    n = makeNode(RecipeStmt);
                    n->recipeName = $3;
                    $$ = (Node *)n;
                }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                define function <fname>
 *                       (language = <lang>, returntype = <typename>
 *                        [, arch_pct = <percentage | pre-defined>]
 *                        [, disk_pct = <percentage | pre-defined>]
 *                        [, byte_pct = <percentage | pre-defined>]
 *                        [, perbyte_cpu = <int | pre-defined>]
 *                        [, percall_cpu = <int | pre-defined>]
 *                        [, iscachable])
 *                        [arg is (<type-1> { , <type-n>})]
 *                        as <filename or code in language as appropriate>
 *
 *****************************************************************************/

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;
                    n->returnType = (Node *)$6;
                    n->withClause = $7;
                    n->as = $9;
                    n->language = $11;
                    $$ = (Node *)n;
                };

opt_with:  WITH definition                        { $$ = $2; }
        | /*EMPTY*/                            { $$ = NIL; }
        ;

def_args:  '(' def_name_list ')'                { $$ = $2; }
        | '(' ')'                                { $$ = NIL; }
        ;

def_name_list:    name_list;

/*****************************************************************************
 *
 *        QUERY:
 *
 *        remove function <funcname>
 *                (REMOVE FUNCTION "funcname" (arg1, arg2, ...))
 *        remove aggregate <aggname>
 *                (REMOVE AGGREGATE "aggname" "aggtype")
 *        remove operator <opname>
 *                (REMOVE OPERATOR "opname" (leftoperand_typ rightoperand_typ))
 *        remove type <typename>
 *                (REMOVE TYPE "typename")
 *        remove rule <rulename>
 *                (REMOVE RULE "rulename")
 *
 *****************************************************************************/

RemoveStmt:  DROP remove_type name
                {
                    RemoveStmt *n = makeNode(RemoveStmt);
                    n->removeType = $2;
                    n->name = $3;
                    $$ = (Node *)n;
                }
        ;

remove_type:  TYPE_P                            {  $$ = TYPE_P; }
        | INDEX                                    {  $$ = INDEX; }
        | RULE                                    {  $$ = RULE; }
        | VIEW                                    {  $$ = VIEW; }
        ;

RemoveAggrStmt:  DROP AGGREGATE name aggr_argtype
                {
                        RemoveAggrStmt *n = makeNode(RemoveAggrStmt);
                        n->aggname = $3;
                        n->aggtype = $4;
                        $$ = (Node *)n;
                }
        ;

aggr_argtype:  name                                { $$ = $1; }
        | '*'                                    { $$ = NULL; }
        ;

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; }
        ;

RemoveOperStmt:  DROP OPERATOR all_Op '(' oper_argtypes ')'
                {
                    RemoveOperStmt *n = makeNode(RemoveOperStmt);
                    n->opname = $3;
                    n->args = $5;
                    $$ = (Node *)n;
                }
        ;

all_Op:  Op | MathOp;

MathOp:    '+'                { $$ = "+"; }
        | '-'            { $$ = "-"; }
        | '*'            { $$ = "*"; }
        | '/'            { $$ = "/"; }
        | '<'            { $$ = "<"; }
        | '>'            { $$ = ">"; }
        | '='            { $$ = "="; }
        ;

oper_argtypes:    name
                {
                   elog(ERROR,"parser: argument type missing (use NONE for unary operators)");
                }
        | name ',' name
                { $$ = makeList(makeString($1), makeString($3), -1); }
        | NONE ',' name            /* left unary */
                { $$ = makeList(NULL, makeString($3), -1); }
        | name ',' NONE            /* right unary */
                { $$ = makeList(makeString($1), NULL, -1); }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                rename <attrname1> in <relname> [*] to <attrname2>
 *                rename <relname1> to <relname2>
 *
 *****************************************************************************/

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;
                    n->column = $7;
                    n->newname = $9;
                    $$ = (Node *)n;
                }
        ;

opt_name:  name                            { $$ = $1; }
        | /*EMPTY*/                        { $$ = NULL; }
        ;

opt_column:  COLUMN                        { $$ = COLUMN; }
        | /*EMPTY*/                        { $$ = 0; }
        ;


/*****************************************************************************
 *
 *        QUERY:    Define Rewrite Rule , Define Tuple Rule
 *                Define Rule <old rules >
 *
 *        only rewrite rule is supported -- ay 9/94
 *
 *****************************************************************************/

RuleStmt:  CREATE RULE name AS
           { QueryIsRule=TRUE; }
           ON event TO event_object where_clause
           DO opt_instead OptStmtList
                {
                    RuleStmt *n = makeNode(RuleStmt);
                    n->rulename = $3;
                    n->event = $7;
                    n->object = $9;
                    n->whereClause = $10;
                    n->instead = $12;
                    n->actions = $13;
                    $$ = (Node *)n;
                }
        ;

OptStmtList:  NOTHING                    { $$ = NIL; }
        | OptimizableStmt                { $$ = lcons($1, NIL); }
        | '[' OptStmtBlock ']'            { $$ = $2; }
        ;

OptStmtBlock:  OptStmtMulti
                {  $$ = $1; }
        | OptimizableStmt
                { $$ = lcons($1, NIL); }
        ;

OptStmtMulti:  OptStmtMulti OptimizableStmt ';'
                {  $$ = lappend($1, $2); }
        | OptStmtMulti OptimizableStmt
                {  $$ = lappend($1, $2); }
        | OptimizableStmt ';'
                { $$ = lcons($1, NIL); }
        ;

event_object:  relation_name '.' attr_name
                {
                    $$ = makeNode(Attr);
                    $$->relname = $1;
                    $$->paramNo = NULL;
                    $$->attrs = lcons(makeString($3), NIL);
                    $$->indirection = NIL;
                }
        | relation_name
                {
                    $$ = makeNode(Attr);
                    $$->relname = $1;
                    $$->paramNo = NULL;
                    $$->attrs = NIL;
                    $$->indirection = NIL;
                }
        ;

/* change me to select, update, etc. some day */
event:    SELECT                            { $$ = CMD_SELECT; }
        | UPDATE                        { $$ = CMD_UPDATE; }
        | DELETE                        { $$ = CMD_DELETE; }
        | INSERT                        { $$ = CMD_INSERT; }
         ;

opt_instead:  INSTEAD                    { $$ = TRUE; }
        | /*EMPTY*/                    { $$ = FALSE; }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                NOTIFY <relation_name>    can appear both in rule bodies and
 *                as a query-level command
 *
 *****************************************************************************/

NotifyStmt:  NOTIFY relation_name
                {
                    NotifyStmt *n = makeNode(NotifyStmt);
                    n->relname = $2;
                    $$ = (Node *)n;
                }
        ;

ListenStmt:  LISTEN relation_name
                {
                    ListenStmt *n = makeNode(ListenStmt);
                    n->relname = $2;
                    $$ = (Node *)n;
                }
;


/*****************************************************************************
 *
 *        Transactions:
 *
 *        abort transaction
 *                (ABORT)
 *        begin transaction
 *                (BEGIN)
 *        end transaction
 *                (END)
 *
 *****************************************************************************/

TransactionStmt:  ABORT_TRANS TRANSACTION
                {
                    TransactionStmt *n = makeNode(TransactionStmt);
                    n->command = ABORT_TRANS;
                    $$ = (Node *)n;
                }
        | BEGIN_TRANS TRANSACTION
                {
                    TransactionStmt *n = makeNode(TransactionStmt);
                    n->command = BEGIN_TRANS;
                    $$ = (Node *)n;
                }
        | BEGIN_TRANS WORK
                {
                    TransactionStmt *n = makeNode(TransactionStmt);
                    n->command = BEGIN_TRANS;
                    $$ = (Node *)n;
                }
        | COMMIT WORK
                {
                    TransactionStmt *n = makeNode(TransactionStmt);
                    n->command = END_TRANS;
                    $$ = (Node *)n;
                }
        | END_TRANS TRANSACTION
                {
                    TransactionStmt *n = makeNode(TransactionStmt);
                    n->command = END_TRANS;
                    $$ = (Node *)n;
                }
        | ROLLBACK WORK
                {
                    TransactionStmt *n = makeNode(TransactionStmt);
                    n->command = ABORT_TRANS;
                    $$ = (Node *)n;
                }

        | ABORT_TRANS
                {
                    TransactionStmt *n = makeNode(TransactionStmt);
                    n->command = ABORT_TRANS;
                    $$ = (Node *)n;
                }
        | BEGIN_TRANS
                {
                    TransactionStmt *n = makeNode(TransactionStmt);
                    n->command = BEGIN_TRANS;
                    $$ = (Node *)n;
                }
        | COMMIT
                {
                    TransactionStmt *n = makeNode(TransactionStmt);
                    n->command = END_TRANS;
                    $$ = (Node *)n;
                }

        | END_TRANS
                {
                    TransactionStmt *n = makeNode(TransactionStmt);
                    n->command = END_TRANS;
                    $$ = (Node *)n;
                }
        | ROLLBACK
                {
                    TransactionStmt *n = makeNode(TransactionStmt);
                    n->command = ABORT_TRANS;
                    $$ = (Node *)n;
                }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                define view <viewname> '('target-list ')' [where <quals> ]
 *
 *****************************************************************************/

ViewStmt:  CREATE VIEW name AS SelectStmt
                {
                    ViewStmt *n = makeNode(ViewStmt);
                    n->viewname = $3;
                    n->query = (Query *)$5;
                    if (((SelectStmt *)n->query)->unionClause != NULL)
                        elog(ERROR,"Views on unions not implemented.");
                    $$ = (Node *)n;
                }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                load "filename"
 *
 *****************************************************************************/

LoadStmt:  LOAD file_name
                {
                    LoadStmt *n = makeNode(LoadStmt);
                    n->filename = $2;
                    $$ = (Node *)n;
                }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                createdb dbname
 *
 *****************************************************************************/

CreatedbStmt:  CREATE DATABASE database_name opt_database
                {
                    CreatedbStmt *n = makeNode(CreatedbStmt);
                    n->dbname = $3;
                    n->dbpath = $4;
                    $$ = (Node *)n;
                }
        ;

opt_database:  WITH LOCATION '=' location        { $$ = $4; }
        | /*EMPTY*/                                { $$ = NULL; }
        ;

location:  Sconst                                { $$ = $1; }
        | DEFAULT                                { $$ = NULL; }
        | /*EMPTY*/                                { $$ = NULL; }
        ;

/*****************************************************************************
 *
 *        QUERY:
 *                destroydb dbname
 *
 *****************************************************************************/

DestroydbStmt:    DROP DATABASE database_name
                {
                    DestroydbStmt *n = makeNode(DestroydbStmt);
                    n->dbname = $3;
                    $$ = (Node *)n;
                }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                cluster <index_name> on <relation_name>
 *
 *****************************************************************************/

ClusterStmt:  CLUSTER index_name ON relation_name
                {
                   ClusterStmt *n = makeNode(ClusterStmt);
                   n->relname = $4;
                   n->indexname = $2;
                   $$ = (Node*)n;
                }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                vacuum
 *
 *****************************************************************************/

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 opt_analyze relation_name opt_va_list
                {
                    VacuumStmt *n = makeNode(VacuumStmt);
                    n->verbose = $2;
                    n->analyze = $3;
                    n->vacrel = $4;
                    n->va_spec = $5;
                    if ( $5 != NIL && !$4 )
                        elog(ERROR,"parser: syntax error at or near \"(\"");
                    $$ = (Node *)n;
                }
        ;

opt_verbose:  VERBOSE                            { $$ = TRUE; }
        | /*EMPTY*/                                { $$ = FALSE; }
        ;

opt_analyze:  ANALYZE                            { $$ = TRUE; }
        | /*EMPTY*/                                { $$ = FALSE; }
        ;

opt_va_list:  '(' va_list ')'
                { $$ = $2; }
        | /* EMPTY */
                { $$ = NIL; }
        ;

va_list:  name
                { $$=lcons($1,NIL); }
        | va_list ',' name
                { $$=lappend($1,$3); }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                EXPLAIN query
 *
 *****************************************************************************/

ExplainStmt:  EXPLAIN opt_verbose OptimizableStmt
                {
                    ExplainStmt *n = makeNode(ExplainStmt);
                    n->verbose = $2;
                    n->query = (Query*)$3;
                    $$ = (Node *)n;
                }
        ;


/*****************************************************************************
 *                                                                             *
 *        Optimizable Stmts:                                                     *
 *                                                                             *
 *        one of the five queries processed by the planner                     *
 *                                                                             *
 *        [ultimately] produces query-trees as specified                         *
 *        in the query-spec document in ~postgres/ref                             *
 *                                                                             *
 *****************************************************************************/

OptimizableStmt:  SelectStmt
        | CursorStmt
        | UpdateStmt
        | InsertStmt
        | NotifyStmt
        | DeleteStmt                    /* by default all are $$=$1 */
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                INSERT STATEMENTS
 *
 *****************************************************************************/

InsertStmt:  INSERT INTO relation_name opt_column_list insert_rest
                {
                    $5->relname = $3;
                    $5->cols = $4;
                    $$ = (Node *)$5;
                }
        ;

insert_rest:  VALUES '(' res_target_list2 ')'
                {
                    $$ = makeNode(InsertStmt);
                    $$->targetList = $3;
                    $$->fromClause = NIL;
                    $$->whereClause = NULL;
                }
        | SELECT res_target_list2 from_clause where_clause
                {
                    $$ = makeNode(InsertStmt);
                    $$->targetList = $2;
                    $$->fromClause = $3;
                    $$->whereClause = $4;
                }
        ;

opt_column_list:  '(' columnList ')'            { $$ = $2; }
        | /*EMPTY*/                                { $$ = NIL; }
        ;

columnList:
          columnList ',' columnElem
                { $$ = lappend($1, $3); }
        | columnElem
                { $$ = lcons($1, NIL); }
        ;

columnElem:  ColId opt_indirection
                {
                    Ident *id = makeNode(Ident);
                    id->name = $1;
                    id->indirection = $2;
                    $$ = (Node *)id;
                }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                DELETE STATEMENTS
 *
 *****************************************************************************/

DeleteStmt:  DELETE FROM relation_name
             where_clause
                {
                    DeleteStmt *n = makeNode(DeleteStmt);
                    n->relname = $3;
                    n->whereClause = $4;
                    $$ = (Node *)n;
                }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                UpdateStmt (UPDATE)
 *
 *****************************************************************************/

UpdateStmt:  UPDATE relation_name
              SET res_target_list
              from_clause
              where_clause
                {
                    UpdateStmt *n = makeNode(UpdateStmt);
                    n->relname = $2;
                    n->targetList = $4;
                    n->fromClause = $5;
                    n->whereClause = $6;
                    $$ = (Node *)n;
                }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                CURSOR STATEMENTS
 *
 *****************************************************************************/
CursorStmt:  DECLARE name opt_binary CURSOR FOR
              SELECT opt_unique res_target_list2
             from_clause where_clause
             group_clause having_clause
             union_clause sort_clause
                {
                    SelectStmt *n = makeNode(SelectStmt);

                    /* from PORTAL name */
                    /*
                     *    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.
                     *                            -- mao
                     */
                    if (!IsTransactionBlock())
                        elog(ERROR,"Named portals may only be used in begin/end transaction blocks");

                    n->portalname = $2;
                    n->binary = $3;
                    n->unique = $7;
                    n->targetList = $8;
                    n->fromClause = $9;
                    n->whereClause = $10;
                    n->groupClause = $11;
                    n->havingClause = $12;
                    n->unionClause = $13;
                    n->sortClause = $14;
                    $$ = (Node *)n;
                }
        ;


/*****************************************************************************
 *
 *        QUERY:
 *                SELECT STATEMENTS
 *
 *****************************************************************************/

SelectStmt:  SELECT opt_unique res_target_list2
             result from_clause where_clause
             group_clause having_clause
             union_clause sort_clause
                {
                    SelectStmt *n = makeNode(SelectStmt);
                    n->unique = $2;
                    n->targetList = $3;
                    n->into = $4;
                    n->fromClause = $5;
                    n->whereClause = $6;
                    n->groupClause = $7;
                    n->havingClause = $8;
                    n->unionClause = $9;
                    n->sortClause = $10;
                    $$ = (Node *)n;
                }
        ;

union_clause:  UNION opt_union select_list
                {
                    SelectStmt *n = (SelectStmt *)lfirst($3);
                    n->unionall = $2;
                    $$ = $3;
                }
        | /*EMPTY*/
                { $$ = NIL; }
        ;

select_list:  select_list UNION opt_union SubSelect
                {
                    SelectStmt *n = (SelectStmt *)$4;
                    n->unionall = $3;
                    $$ = lappend($1, $4);
                }
        | SubSelect
                { $$ = lcons($1, NIL); }
        ;

SubSelect:    SELECT opt_unique res_target_list2
             from_clause where_clause
             group_clause having_clause
                {
                    SelectStmt *n = makeNode(SelectStmt);
                    n->unique = $2;
                    n->unionall = FALSE;
                    n->targetList = $3;
                    n->fromClause = $4;
                    n->whereClause = $5;
                    n->groupClause = $6;
                    n->havingClause = $7;
                    $$ = (Node *)n;
                }
        ;

result:  INTO TABLE relation_name
                {    $$= $3; }
        | /*EMPTY*/
                {    $$ = NULL; }
        ;

opt_union:  ALL                                    { $$ = TRUE; }
        | /*EMPTY*/                                { $$ = FALSE; }
        ;

opt_unique:  DISTINCT                            { $$ = "*"; }
        | DISTINCT ON ColId                        { $$ = $3; }
        | ALL                                    { $$ = NULL; }
        | /*EMPTY*/                                { $$ = NULL; }
        ;

sort_clause:  ORDER BY sortby_list                { $$ = $3; }
        | /*EMPTY*/                                { $$ = NIL; }
        ;

sortby_list:  sortby                            { $$ = lcons($1, NIL); }
        | sortby_list ',' sortby                { $$ = lappend($1, $3); }
        ;

sortby:  ColId OptUseOp
                {
                    $$ = makeNode(SortGroupBy);
                    $$->resno = 0;
                    $$->range = NULL;
                    $$->name = $1;
                    $$->useOp = $2;
                }
        | ColId '.' ColId OptUseOp
                {
                    $$ = makeNode(SortGroupBy);
                    $$->resno = 0;
                    $$->range = $1;
                    $$->name = $3;
                    $$->useOp = $4;
                }
        | Iconst OptUseOp
                {
                    $$ = makeNode(SortGroupBy);
                    $$->resno = $1;
                    $$->range = NULL;
                    $$->name = NULL;
                    $$->useOp = $2;
                }
        ;

OptUseOp:  USING Op                                { $$ = $2; }
        | USING '<'                                { $$ = "<"; }
        | USING '>'                                { $$ = ">"; }
        | ASC                                    { $$ = "<"; }
        | DESC                                    { $$ = ">"; }
        | /*EMPTY*/                                { $$ = "<"; /*default*/ }
        ;

/*
 *    jimmy bell-style recursive queries aren't supported in the
 *    current system.
 *
 *    ...however, recursive addattr and rename supported.  make special
 *    cases for these.
 */
opt_inh_star:  '*'                                { $$ = TRUE; }
        | /*EMPTY*/                                { $$ = FALSE; }
        ;

relation_name_list:  name_list;

name_list:  name
                {    $$ = lcons(makeString($1),NIL); }
        | name_list ',' name
                {    $$ = lappend($1,makeString($3)); }
        ;

group_clause:  GROUP BY groupby_list            { $$ = $3; }
        | /*EMPTY*/                                { $$ = NIL; }
        ;

groupby_list:  groupby                            { $$ = lcons($1, NIL); }
        | groupby_list ',' groupby                { $$ = lappend($1, $3); }
        ;

groupby:  ColId
                {
                    $$ = makeNode(SortGroupBy);
                    $$->resno = 0;
                    $$->range = NULL;
                    $$->name = $1;
                    $$->useOp = NULL;
                }
        | ColId '.' ColId
                {
                    $$ = makeNode(SortGroupBy);
                    $$->resno = 0;
                    $$->range = $1;
                    $$->name = $3;
                    $$->useOp = NULL;
                }
        | Iconst
                {
                    $$ = makeNode(SortGroupBy);
                    $$->resno = $1;
                    $$->range = NULL;
                    $$->name = NULL;
                    $$->useOp = NULL;
                }
        ;

having_clause:  HAVING a_expr                    { $$ = $2; }
        | /*EMPTY*/                                { $$ = NULL; }
        ;


/*****************************************************************************
 *
 *    clauses common to all Optimizable Stmts:
 *        from_clause        -
 *        where_clause    -
 *
 *****************************************************************************/

from_clause:  FROM '(' relation_expr join_expr JOIN relation_expr join_spec ')'
                {
                    $$ = NIL;
                    elog(ERROR,"JOIN not yet implemented");
                }
        | FROM from_list                        { $$ = $2; }
        | /*EMPTY*/                                { $$ = NIL; }
        ;

from_list:    from_list ',' from_val
                { $$ = lappend($1, $3); }
        | from_val CROSS JOIN from_val
                { elog(ERROR,"CROSS JOIN not yet implemented"); }
        | from_val
                { $$ = lcons($1, NIL); }
        ;

from_val:  relation_expr AS ColLabel
                {
                    $$ = makeNode(RangeVar);
                    $$->relExpr = $1;
                    $$->name = $3;
                }
        | relation_expr ColId
                {
                    $$ = makeNode(RangeVar);
                    $$->relExpr = $1;
                    $$->name = $2;
                }
        | relation_expr
                {
                    $$ = makeNode(RangeVar);
                    $$->relExpr = $1;
                    $$->name = NULL;
                }
        ;

join_expr:  NATURAL join_expr                    { $$ = NULL; }
        | FULL join_outer
                { elog(ERROR,"FULL OUTER JOIN not yet implemented"); }
        | LEFT join_outer
                { elog(ERROR,"LEFT OUTER JOIN not yet implemented"); }
        | RIGHT join_outer
                { elog(ERROR,"RIGHT OUTER JOIN not yet implemented"); }
        | OUTER_P
                { elog(ERROR,"OUTER JOIN not yet implemented"); }
        | INNER_P
                { elog(ERROR,"INNER JOIN not yet implemented"); }
        | UNION
                { elog(ERROR,"UNION JOIN not yet implemented"); }
        | /*EMPTY*/
                { elog(ERROR,"INNER JOIN not yet implemented"); }
        ;

join_outer:  OUTER_P                            { $$ = 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:  ColId
                {
                    $$ = makeNode(SortGroupBy);
                    $$->resno = 0;
                    $$->range = NULL;
                    $$->name = $1;
                    $$->useOp = NULL;
                }
        | ColId '.' ColId
                {
                    $$ = 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 */ }
        ;

relation_expr:    relation_name
                {
                    /* normal relations */
                    $$ = makeNode(RelExpr);
                    $$->relname = $1;
                    $$->inh = FALSE;
                }
        | relation_name '*'                  %prec '='
                {
                    /* inheritance query */
                    $$ = makeNode(RelExpr);
                    $$->relname = $1;
                    $$->inh = TRUE;
                }

opt_array_bounds:  '[' ']' nest_array_bounds
                {  $$ = lcons(makeInteger(-1), $3); }
        | '[' Iconst ']' nest_array_bounds
                {  $$ = lcons(makeInteger($2), $4); }
        | /* EMPTY */
                {  $$ = NIL; }
        ;

nest_array_bounds:    '[' ']' nest_array_bounds
                {  $$ = lcons(makeInteger(-1), $3); }
        | '[' Iconst ']' nest_array_bounds
                {  $$ = lcons(makeInteger($2), $4); }
        | /*EMPTY*/
                {  $$ = NIL; }
        ;


/*****************************************************************************
 *
 *    Type syntax
 *        SQL92 introduces a large amount of type-specific syntax.
 *        Define individual clauses to handle these cases, and use
 *         the generic case to handle regular type-extensible Postgres syntax.
 *        - thomas 1997-10-10
 *
 *****************************************************************************/

Typename:  Array opt_array_bounds
                {
                    $$ = $1;
                    $$->arrayBounds = $2;

                    /* Is this the name of a complex type? If so, implement
                     * it as a set.
                     */
                    if (!strcmp(saved_relname, $$->name))
                        /* This attr is the same type as the relation
                         * being defined. The classic example: create
                         * emp(name=text,mgr=emp)
                         */
                        $$->setof = TRUE;
                    else if (typeTypeRelid(typenameType($$->name)) != InvalidOid)
                         /* (Eventually add in here that the set can only
                          * contain one element.)
                          */
                        $$->setof = TRUE;
                    else
                        $$->setof = FALSE;
                }
        | Character
        | SETOF Array
                {
                    $$ = $2;
                    $$->setof = TRUE;
                }
        ;

Array:  Generic
        | Datetime
        | Numeric
        ;

Generic:  generic
                {
                    $$ = makeNode(TypeName);
                    $$->name = xlateSqlType($1);
                }
        ;

generic:  Id                                    { $$ = $1; }
        | TYPE_P                                { $$ = xlateSqlType("type"); }
        | DOUBLE PRECISION                        { $$ = xlateSqlType("float8"); }
        ;

/* SQL92 numeric data types
 * Check FLOAT() precision limits assuming IEEE floating types.
 * Provide rudimentary DECIMAL() and NUMERIC() implementations
 *  by checking parameters and making sure they match what is possible with INTEGER.
 * - thomas 1997-09-18
 */
Numeric:  FLOAT opt_float
                {
                    $$ = makeNode(TypeName);
                    $$->name = xlateSqlType($2);
                }
        | DECIMAL opt_decimal
                {
                    $$ = makeNode(TypeName);
                    $$->name = xlateSqlType("integer");
                }
        | NUMERIC opt_numeric
                {
                    $$ = makeNode(TypeName);
                    $$->name = xlateSqlType("integer");
                }
        ;

opt_float:  '(' Iconst ')'
                {
                    if ($2 < 1)
                        elog(ERROR,"precision for FLOAT must be at least 1");
                    else if ($2 < 7)
                        $$ = xlateSqlType("float4");
                    else if ($2 < 16)
                        $$ = xlateSqlType("float8");
                    else
                        elog(ERROR,"precision for FLOAT must be less than 16");
                }
        | /*EMPTY*/
                {
                    $$ = xlateSqlType("float8");
                }
        ;

opt_numeric:  '(' Iconst ',' Iconst ')'
                {
                    if ($2 != 9)
                        elog(ERROR,"NUMERIC precision %d must be 9",$2);
                    if ($4 != 0)
                        elog(ERROR,"NUMERIC scale %d must be zero",$4);
                }
        | '(' Iconst ')'
                {
                    if ($2 != 9)
                        elog(ERROR,"NUMERIC precision %d must be 9",$2);
                }
        | /*EMPTY*/
                {
                    $$ = NULL;
                }
        ;

opt_decimal:  '(' Iconst ',' Iconst ')'
                {
                    if ($2 > 9)
                        elog(ERROR,"DECIMAL precision %d exceeds implementation limit of 9",$2);
                    if ($4 != 0)
                        elog(ERROR,"DECIMAL scale %d must be zero",$4);
                    $$ = NULL;
                }
        | '(' Iconst ')'
                {
                    if ($2 > 9)
                        elog(ERROR,"DECIMAL precision %d exceeds implementation limit of 9",$2);
                    $$ = NULL;
                }
        | /*EMPTY*/
                {
                    $$ = NULL;
                }
        ;

/* SQL92 character data types
 * The following implements CHAR() and VARCHAR().
 * We do it here instead of the 'Generic' 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
 */
Character:  character '(' Iconst ')'
                {
                    $$ = makeNode(TypeName);
                    if (!strcasecmp($1, "char"))
                        $$->name = xlateSqlType("bpchar");
                    else if (!strcasecmp($1, "varchar"))
                        $$->name = xlateSqlType("varchar");
                    else
                        yyerror("parse error");
                    if ($3 < 1)
                        elog(ERROR,"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(ERROR,"length for type '%s' 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 = VARHDRSZ + $3;
                }
        | character
                {
                    $$ = makeNode(TypeName);
                    $$->name = xlateSqlType($1);
                }
        ;

character:  CHARACTER opt_varying opt_charset opt_collate
                {
                    char *type, *c;
                    if (($3 == NULL) || (strcasecmp($3, "sql_text") == 0)) {
                        if ($2) type = xlateSqlType("varchar");
                        else type = xlateSqlType("char");
                    } else {
                        if ($2) {
                            c = palloc(strlen("var") + strlen($3) + 1);
                            strcpy(c, "var");
                            strcat(c, $3);
                            type = xlateSqlType(c);
                        } else {
                            type = xlateSqlType($3);
                        }
                    };
                    if ($4 != NULL)
                    elog(ERROR,"COLLATE %s not yet implemented",$4);
                    $$ = type;
                }
        | CHAR opt_varying                        { $$ = xlateSqlType($2? "varchar": "char"); }
        | VARCHAR                                { $$ = xlateSqlType("varchar"); }
        | NATIONAL CHARACTER opt_varying        { $$ = xlateSqlType($3? "varchar": "char"); }
        | NCHAR opt_varying                        { $$ = xlateSqlType($2? "varchar": "char"); }
        ;

opt_varying:  VARYING                            { $$ = TRUE; }
        | /*EMPTY*/                                { $$ = FALSE; }
        ;

opt_charset:  CHARACTER SET ColId                { $$ = $3; }
        | /*EMPTY*/                                { $$ = NULL; }
        ;

opt_collate:  COLLATE ColId                        { $$ = $2; }
        | /*EMPTY*/                                { $$ = NULL; }
        ;

Datetime:  datetime
                {
                    $$ = makeNode(TypeName);
                    $$->name = xlateSqlType($1);
                }
        | TIMESTAMP opt_timezone
                {
                    $$ = makeNode(TypeName);
                    $$->name = xlateSqlType("timestamp");
                    $$->timezone = $2;
                }
        | TIME
                {
                    $$ = makeNode(TypeName);
                    $$->name = xlateSqlType("time");
                }
        | INTERVAL opt_interval
                {
                    $$ = makeNode(TypeName);
                    $$->name = xlateSqlType("interval");
                }
        ;

datetime:  YEAR_P                                { $$ = "year"; }
        | MONTH_P                                { $$ = "month"; }
        | DAY_P                                    { $$ = "day"; }
        | HOUR_P                                { $$ = "hour"; }
        | MINUTE_P                                { $$ = "minute"; }
        | SECOND_P                                { $$ = "second"; }
        ;

opt_timezone:  WITH TIME ZONE                    { $$ = TRUE; }
        | /*EMPTY*/                                { $$ = FALSE; }
        ;

opt_interval:  datetime                            { $$ = lcons($1, NIL); }
        | YEAR_P TO MONTH_P                        { $$ = NIL; }
        | DAY_P TO HOUR_P                        { $$ = NIL; }
        | DAY_P TO MINUTE_P                        { $$ = NIL; }
        | DAY_P TO SECOND_P                        { $$ = NIL; }
        | HOUR_P TO MINUTE_P                    { $$ = NIL; }
        | HOUR_P TO SECOND_P                    { $$ = NIL; }
        | /*EMPTY*/                                { $$ = NIL; }
        ;


/*****************************************************************************
 *
 *    expression grammar, still needs some cleanup
 *
 *****************************************************************************/

a_expr_or_null:  a_expr
                { $$ = $1; }
        | NULL_P
                {
                    A_Const *n = makeNode(A_Const);
                    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.
 *
 * Note that "SOME" is the same as "ANY" in syntax.
 * - thomas 1998-01-10
 */
row_expr: '(' row_descriptor ')' IN '(' SubSelect ')'
                {
                    $$ = makeA_Expr(OP, "=any", (Node *)$2, (Node *)$6);
                }
        | '(' row_descriptor ')' NOT IN '(' SubSelect ')'
                {
                    $$ = makeA_Expr(OP, "<>any", (Node *)$2, (Node *)$7);
                }
        | '(' row_descriptor ')' RowOp row_opt '(' SubSelect ')'
                {
                    char *opr;
                    opr = palloc(strlen($4)+strlen($5)+1);
                    strcpy(opr, $4);
                    strcat(opr, $5);
                    $$ = makeA_Expr(OP, opr, (Node *)$2, (Node *)$7);
                }
        | '(' 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);
                }
        ;

RowOp:  '='                        { $$ = "="; }
        | '<'                    { $$ = "<"; }
        | '>'                    { $$ = ">"; }
        ;

row_opt:  ALL                    { $$ = "all"; }
        | ANY                    { $$ = "any"; }
        | SOME                    { $$ = "any"; }
        ;

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
                {    $$ = makeA_Expr(OP, "-", NULL, $2); }
        | a_expr '+' a_expr
                {    $$ = makeA_Expr(OP, "+", $1, $3); }
        | a_expr '-' a_expr
                {    $$ = makeA_Expr(OP, "-", $1, $3); }
        | a_expr '/' a_expr
                {    $$ = makeA_Expr(OP, "/", $1, $3); }
        | a_expr '*' a_expr
                {    $$ = makeA_Expr(OP, "*", $1, $3); }
        | a_expr '<' a_expr
                {    $$ = makeA_Expr(OP, "<", $1, $3); }
        | a_expr '>' a_expr
                {    $$ = makeA_Expr(OP, ">", $1, $3); }
        | a_expr '=' a_expr
                {    $$ = makeA_Expr(OP, "=", $1, $3); }
        | ':' a_expr
                {    $$ = makeA_Expr(OP, ":", NULL, $2); }
        | ';' a_expr
                {    $$ = makeA_Expr(OP, ";", NULL, $2); }
        | '|' a_expr
                {    $$ = makeA_Expr(OP, "|", NULL, $2); }
        | a_expr TYPECAST Typename
                {
                    $$ = (Node *)$1;
                    /* AexprConst can be either A_Const or ParamNo */
                    if (nodeTag($1) == T_A_Const) {
                        ((A_Const *)$1)->typename = $3;
                    } else if (nodeTag($1) == T_Param) {
                        ((ParamNo *)$1)->typename = $3;
                    /* otherwise, try to transform to a function call */
                    } else {
                        FuncCall *n = makeNode(FuncCall);
                        n->funcname = $3->name;
                        n->args = lcons($1,NIL);
                        $$ = (Node *)n;
                    }
                }
        | CAST a_expr AS Typename
                {
                    $$ = (Node *)$2;
                    /* AexprConst can be either A_Const or ParamNo */
                    if (nodeTag($2) == T_A_Const) {
                        ((A_Const *)$2)->typename = $4;
                    } else if (nodeTag($2) == T_Param) {
                        ((ParamNo *)$2)->typename = $4;
                    /* otherwise, try to transform to a function call */
                    } else {
                        FuncCall *n = makeNode(FuncCall);
                        n->funcname = $4->name;
                        n->args = lcons($2,NIL);
                        $$ = (Node *)n;
                    }
                }
        | '(' a_expr_or_null ')'
                {    $$ = $2; }
        | a_expr Op a_expr
                {    $$ = makeIndexable($2,$1,$3);    }
        | a_expr LIKE a_expr
                {    $$ = makeIndexable("~~", $1, $3); }
        | a_expr NOT LIKE a_expr
                {    $$ = makeA_Expr(OP, "!~~", $1, $4); }
        | Op a_expr
                {    $$ = makeA_Expr(OP, $1, NULL, $2); }
        | a_expr Op
                {    $$ = makeA_Expr(OP, $2, $1, NULL); }
        | ColId
                {
                    /* could be a column name or a relation_name */
                    Ident *n = makeNode(Ident);
                    n->name = $1;
                    n->indirection = NULL;
                    $$ = (Node *)n;
                }
        | name '(' '*' ')'
                {
                    /* cheap hack for aggregate (eg. count) */
                    FuncCall *n = makeNode(FuncCall);
                    A_Const *star = makeNode(A_Const);

                    star->val.type = T_String;
                    star->val.val.str = "";
                    n->funcname = $1;
                    n->args = lcons(star, NIL);
                    $$ = (Node *)n;
                }
        | name '(' ')'
                {
                    FuncCall *n = makeNode(FuncCall);
                    n->funcname = $1;
                    n->args = NIL;
                    $$ = (Node *)n;
                }
        | name '(' expr_list ')'
                {
                    FuncCall *n = makeNode(FuncCall);
                    n->funcname = $1;
                    n->args = $3;
                    $$ = (Node *)n;
                }
        | CURRENT_DATE
                {
                    A_Const *n = makeNode(A_Const);
                    TypeName *t = makeNode(TypeName);

                    n->val.type = T_String;
                    n->val.val.str = "now";
                    n->typename = t;

                    t->name = xlateSqlType("date");
                    t->setof = FALSE;

                    $$ = (Node *)n;
                }
        | CURRENT_TIME
                {
                    A_Const *n = makeNode(A_Const);
                    TypeName *t = makeNode(TypeName);

                    n->val.type = T_String;
                    n->val.val.str = "now";
                    n->typename = t;

                    t->name = xlateSqlType("time");
                    t->setof = FALSE;

                    $$ = (Node *)n;
                }
        | CURRENT_TIME '(' Iconst ')'
                {
                    FuncCall *n = makeNode(FuncCall);
                    A_Const *s = makeNode(A_Const);
                    TypeName *t = makeNode(TypeName);

                    n->funcname = xlateSqlType("time");
                    n->args = lcons(s, NIL);

                    s->val.type = T_String;
                    s->val.val.str = "now";
                    s->typename = t;

                    t->name = xlateSqlType("time");
                    t->setof = FALSE;

                    if ($3 != 0)
                        elog(NOTICE,"CURRENT_TIME(%d) precision not implemented; zero used instead",$3);

                    $$ = (Node *)n;
                }
        | CURRENT_TIMESTAMP
                {
                    A_Const *n = makeNode(A_Const);
                    TypeName *t = makeNode(TypeName);

                    n->val.type = T_String;
                    n->val.val.str = "now";
                    n->typename = t;

                    t->name = xlateSqlType("timestamp");
                    t->setof = FALSE;

                    $$ = (Node *)n;
                }
        | CURRENT_TIMESTAMP '(' Iconst ')'
                {
                    FuncCall *n = makeNode(FuncCall);
                    A_Const *s = makeNode(A_Const);
                    TypeName *t = makeNode(TypeName);

                    n->funcname = xlateSqlType("timestamp");
                    n->args = lcons(s, NIL);

                    s->val.type = T_String;
                    s->val.val.str = "now";
                    s->typename = t;

                    t->name = xlateSqlType("timestamp");
                    t->setof = FALSE;

                    if ($3 != 0)
                        elog(NOTICE,"CURRENT_TIMESTAMP(%d) precision not implemented; zero used instead",$3);

                    $$ = (Node *)n;
                }
        | CURRENT_USER
                {
                    FuncCall *n = makeNode(FuncCall);
                    n->funcname = "getpgusername";
                    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(ERROR,"EXISTS not yet implemented");
                    $$ = $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;
                }
        | a_expr ISNULL
                {    $$ = makeA_Expr(ISNULL, NULL, $1, NULL); }
        | a_expr IS NULL_P
                {    $$ = makeA_Expr(ISNULL, NULL, $1, NULL); }
        | a_expr NOTNULL
                {    $$ = 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
                {
                    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 FALSE_P
                {
                    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_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 TRUE_P
                {
                    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
                {
                    $$ = makeA_Expr(AND, NULL,
                        makeA_Expr(OP, ">=", $1, $3),
                        makeA_Expr(OP, "<=", $1, $5));
                }
        | a_expr NOT BETWEEN AexprConst AND AexprConst
                {
                    $$ = makeA_Expr(OR, NULL,
                        makeA_Expr(OP, "<", $1, $4),
                        makeA_Expr(OP, ">", $1, $6));
                }
        | a_expr IN { saved_In_Expr = $1; } '(' in_expr ')'
                {    $$ = $5; }
        | a_expr NOT IN { saved_In_Expr = $1; } '(' not_in_expr ')'
                {    $$ = $6; }
        | a_expr AND a_expr
                {    $$ = makeA_Expr(AND, NULL, $1, $3); }
        | a_expr OR a_expr
                {    $$ = makeA_Expr(OR, NULL, $1, $3); }
        | NOT a_expr
                {    $$ = makeA_Expr(NOT, NULL, NULL, $2); }
        ;

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_Indices *ai = makeNode(A_Indices);
                    ai->lidx = $2;
                    ai->uidx = $4;
                    $$ = lcons(ai, $6);
                }
        | /* 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;
                    $$ = lappend(lcons((Node *)n,NIL), $3);
                }
        | /* EMPTY */
                {    $$ = NIL; }
        ;

position_list:  position_expr IN position_expr
                {    $$ = makeList($3, $1, -1); }
        | /* EMPTY */
                {    $$ = NIL; }
        ;

position_expr:  attr opt_indirection
                {
                    $1->indirection = $2;
                    $$ = (Node *)$1;
                }
        | AexprConst
                {    $$ = $1;  }
        | '-' position_expr %prec UMINUS
                {    $$ = makeA_Expr(OP, "-", NULL, $2); }
        | position_expr '+' position_expr
                {    $$ = makeA_Expr(OP, "+", $1, $3); }
        | position_expr '-' position_expr
                {    $$ = makeA_Expr(OP, "-", $1, $3); }
        | position_expr '/' position_expr
                {    $$ = makeA_Expr(OP, "/", $1, $3); }
        | position_expr '*' position_expr
                {    $$ = makeA_Expr(OP, "*", $1, $3); }
        | '|' position_expr
                {    $$ = makeA_Expr(OP, "|", NULL, $2); }
        | position_expr TYPECAST Typename
                {
                    $$ = (Node *)$1;
                    /* AexprConst can be either A_Const or ParamNo */
                    if (nodeTag($1) == T_A_Const) {
                        ((A_Const *)$1)->typename = $3;
                    } else if (nodeTag($1) == T_Param) {
                        ((ParamNo *)$1)->typename = $3;
                    /* otherwise, try to transform to a function call */
                    } else {
                        FuncCall *n = makeNode(FuncCall);
                        n->funcname = $3->name;
                        n->args = lcons($1,NIL);
                        $$ = (Node *)n;
                    }
                }
        | CAST position_expr AS Typename
                {
                    $$ = (Node *)$2;
                    /* AexprConst can be either A_Const or ParamNo */
                    if (nodeTag($2) == T_A_Const) {
                        ((A_Const *)$2)->typename = $4;
                    } else if (nodeTag($2) == T_Param) {
                        ((ParamNo *)$2)->typename = $4;
                    /* otherwise, try to transform to a function call */
                    } else {
                        FuncCall *n = makeNode(FuncCall);
                        n->funcname = $4->name;
                        n->args = lcons($2,NIL);
                        $$ = (Node *)n;
                    }
                }
        | '(' position_expr ')'
                {    $$ = $2; }
        | position_expr Op position_expr
                {    $$ = makeA_Expr(OP, $2, $1, $3); }
        | Op position_expr
                {    $$ = makeA_Expr(OP, $1, NULL, $2); }
        | position_expr Op
                {    $$ = makeA_Expr(OP, $2, $1, NULL); }
        | ColId
                {
                    /* could be a column name or a relation_name */
                    Ident *n = makeNode(Ident);
                    n->name = $1;
                    n->indirection = NULL;
                    $$ = (Node *)n;
                }
        | name '(' ')'
                {
                    FuncCall *n = makeNode(FuncCall);
                    n->funcname = $1;
                    n->args = NIL;
                    $$ = (Node *)n;
                }
        | name '(' expr_list ')'
                {
                    FuncCall *n = makeNode(FuncCall);
                    n->funcname = $1;
                    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;
                }
        ;

substr_list:  expr_list substr_from substr_for
                {
                    $$ = nconc(nconc($1,$2),$3);
                }
        | /* EMPTY */
                {    $$ = NIL; }
        ;

substr_from:  FROM expr_list
                {    $$ = $2; }
        | /* EMPTY */
                {
                    A_Const *n = makeNode(A_Const);
                    n->val.type = T_Integer;
                    n->val.val.ival = 1;
                    $$ = lcons((Node *)n,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:  SubSelect
                {    $$ = makeA_Expr(OP, "=", saved_In_Expr, (Node *)$1); }
        | in_expr_nodes
                {    $$ = $1; }
        ;

in_expr_nodes:  AexprConst
                {    $$ = makeA_Expr(OP, "=", saved_In_Expr, $1); }
        | in_expr_nodes ',' AexprConst
                {    $$ = makeA_Expr(OR, NULL, $1,
                        makeA_Expr(OP, "=", saved_In_Expr, $3));
                }
        ;

not_in_expr:  SubSelect
                {    $$ = makeA_Expr(OP, "<>", saved_In_Expr, (Node *)$1); }
        | not_in_expr_nodes
                {    $$ = $1; }
        ;

not_in_expr_nodes:  AexprConst
                {    $$ = makeA_Expr(OP, "<>", saved_In_Expr, $1); }
        | not_in_expr_nodes ',' AexprConst
                {    $$ = makeA_Expr(AND, NULL, $1,
                        makeA_Expr(OP, "<>", saved_In_Expr, $3));
                }
        ;

attr:  relation_name '.' attrs
                {
                    $$ = makeNode(Attr);
                    $$->relname = $1;
                    $$->paramNo = NULL;
                    $$->attrs = $3;
                    $$->indirection = NULL;
                }
        | ParamNo '.' attrs
                {
                    $$ = makeNode(Attr);
                    $$->relname = NULL;
                    $$->paramNo = $1;
                    $$->attrs = $3;
                    $$->indirection = NULL;
                }
        ;

attrs:      attr_name
                { $$ = lcons(makeString($1), NIL); }
        | attrs '.' attr_name
                { $$ = lappend($1, makeString($3)); }
        | attrs '.' '*'
                { $$ = lappend($1, makeString("*")); }
        ;


/*****************************************************************************
 *
 *    target lists
 *
 *****************************************************************************/

res_target_list:  res_target_list ',' res_target_el
                {    $$ = lappend($1,$3);  }
        | res_target_el
                {    $$ = lcons($1, NIL);  }
        | '*'
                {
                    ResTarget *rt = makeNode(ResTarget);
                    Attr *att = makeNode(Attr);
                    att->relname = "*";
                    att->paramNo = NULL;
                    att->attrs = NULL;
                    att->indirection = NIL;
                    rt->name = NULL;
                    rt->indirection = NULL;
                    rt->val = (Node *)att;
                    $$ = lcons(rt, NIL);
                }
        ;

res_target_el:  ColId opt_indirection '=' a_expr_or_null
                {
                    $$ = makeNode(ResTarget);
                    $$->name = $1;
                    $$->indirection = $2;
                    $$->val = (Node *)$4;
                }
        | attr opt_indirection
                {
                    $$ = makeNode(ResTarget);
                    $$->name = NULL;
                    $$->indirection = $2;
                    $$->val = (Node *)$1;
                }
        | relation_name '.' '*'
                {
                    Attr *att = makeNode(Attr);
                    att->relname = $1;
                    att->paramNo = NULL;
                    att->attrs = lcons(makeString("*"), NIL);
                    att->indirection = NIL;
                    $$ = makeNode(ResTarget);
                    $$->name = 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
                {    $$ = lappend($1, $3);  }
        | res_target_el2
                {    $$ = lcons($1, NIL);  }
        ;

/* AS is not optional because shift/red conflict with unary ops */
res_target_el2:  a_expr_or_null AS ColLabel
                {
                    $$ = makeNode(ResTarget);
                    $$->name = $3;
                    $$->indirection = NULL;
                    $$->val = (Node *)$1;
                }
        | a_expr_or_null
                {
                    $$ = makeNode(ResTarget);
                    $$->name = NULL;
                    $$->indirection = NULL;
                    $$->val = (Node *)$1;
                }
        | relation_name '.' '*'
                {
                    Attr *att = makeNode(Attr);
                    att->relname = $1;
                    att->paramNo = NULL;
                    att->attrs = lcons(makeString("*"), NIL);
                    att->indirection = NIL;
                    $$ = makeNode(ResTarget);
                    $$->name = NULL;
                    $$->indirection = NULL;
                    $$->val = (Node *)att;
                }
        | '*'
                {
                    Attr *att = makeNode(Attr);
                    att->relname = "*";
                    att->paramNo = NULL;
                    att->attrs = NULL;
                    att->indirection = NIL;
                    $$ = makeNode(ResTarget);
                    $$->name = NULL;
                    $$->indirection = NULL;
                    $$->val = (Node *)att;
                }
        ;

opt_id:  ColId                                    { $$ = $1; }
        | /* EMPTY */                            { $$ = NULL; }
        ;

relation_name:    SpecialRuleRelation
                {
                    $$ = $1;
                    StrNCpy(saved_relname, $1, NAMEDATALEN);
                }
        | ColId
                {
                    /* disallow refs to variable system tables */
                    if (strcmp(LogRelationName, $1) == 0
                       || strcmp(VariableRelationName, $1) == 0)
                        elog(ERROR,"%s cannot be accessed by users",$1);
                    else
                        $$ = $1;
                    StrNCpy(saved_relname, $1, NAMEDATALEN);
                }
        ;

database_name:            ColId            { $$ = $1; };
access_method:            Id                { $$ = $1; };
attr_name:                ColId            { $$ = $1; };
class:                    Id                { $$ = $1; };
index_name:                ColId            { $$ = $1; };

/* Functions
 * Include date/time keywords as SQL92 extension.
 * Include TYPE as a SQL92 unreserved keyword. - thomas 1997-10-05
 */
name:                    ColId            { $$ = $1; };

file_name:                Sconst            { $$ = $1; };
recipe_name:            Id                { $$ = $1; };

/* Constants
 * Include TRUE/FALSE for SQL3 support. - thomas 1997-10-24
 */
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;
                    $$ = (Node *)n;
                }
        | Typename Sconst
                {
                    A_Const *n = makeNode(A_Const);
                    n->typename = $1;
                    n->val.type = T_String;
                    n->val.val.str = $2;
                    $$ = (Node *)n;
                }
        | ParamNo
                {    $$ = (Node *)$1;  }
        | TRUE_P
                {
                    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
                {
                    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;
                }
        ;

ParamNo:  PARAM
                {
                    $$ = makeNode(ParamNo);
                    $$->number = $1;
                }
        ;

NumConst:  Iconst                        { $$ = makeInteger($1); }
        | FCONST                        { $$ = makeFloat($1); }
        ;

Iconst:  ICONST                            { $$ = $1; };
Sconst:  SCONST                            { $$ = $1; };

/* Column and type identifier
 * Does not include explicit datetime types
 *  since these must be decoupled in Typename syntax.
 * Use ColId for most identifiers. - thomas 1997-10-21
 */
Id:  IDENT                                { $$ = $1; };

/* Column identifier
 * Include date/time keywords as SQL92 extension.
 * Include TYPE as a SQL92 unreserved keyword. - thomas 1997-10-05
 * Add other keywords. Note that as the syntax expands,
 *  some of these keywords will have to be removed from this
 *  list due to shift/reduce conflicts in yacc. If so, move
 *  down to the ColLabel entity. - thomas 1997-11-06
 */
ColId:  Id                                { $$ = $1; }
        | datetime                        { $$ = $1; }
        | ACTION                        { $$ = "action"; }
        | DATABASE                        { $$ = "database"; }
        | DELIMITERS                    { $$ = "delimiters"; }
        | FUNCTION                        { $$ = "function"; }
        | INDEX                            { $$ = "index"; }
        | KEY                            { $$ = "key"; }
        | LANGUAGE                        { $$ = "language"; }
        | LOCATION                        { $$ = "location"; }
        | MATCH                            { $$ = "match"; }
        | OPERATOR                        { $$ = "operator"; }
        | OPTION                        { $$ = "option"; }
        | PRIVILEGES                    { $$ = "privileges"; }
        | RECIPE                        { $$ = "recipe"; }
        | TIME                            { $$ = "time"; }
        | TRIGGER                        { $$ = "trigger"; }
        | TYPE_P                        { $$ = "type"; }
        | VERSION                        { $$ = "version"; }
        | ZONE                            { $$ = "zone"; }
        ;

/* Column label
 * Allowed labels in "AS" clauses.
 * Include TRUE/FALSE SQL3 reserved words for Postgres backward
 *  compatibility. Cannot allow this for column names since the
 *  syntax would not distinguish between the constant value and
 *  a column name. - thomas 1997-10-24
 * Add other keywords to this list. Note that they appear here
 *  rather than in ColId if there was a shift/reduce conflict
 *  when used as a full identifier. - thomas 1997-11-06
 */
ColLabel:  ColId                        { $$ = $1; }
        | ARCHIVE                        { $$ = "archive"; }
        | CLUSTER                        { $$ = "cluster"; }
        | CONSTRAINT                    { $$ = "constraint"; }
        | CROSS                            { $$ = "cross"; }
        | FOREIGN                        { $$ = "foreign"; }
        | GROUP                            { $$ = "group"; }
        | LOAD                            { $$ = "load"; }
        | ORDER                            { $$ = "order"; }
        | POSITION                        { $$ = "position"; }
        | PRECISION                        { $$ = "precision"; }
        | TABLE                            { $$ = "table"; }
        | TRANSACTION                    { $$ = "transaction"; }
        | TRUE_P                        { $$ = "true"; }
        | FALSE_P                        { $$ = "false"; }
        ;

SpecialRuleRelation:  CURRENT
                {
                    if (QueryIsRule)
                        $$ = "*CURRENT*";
                    else
                        elog(ERROR,"CURRENT used in non-rule query");
                }
        | NEW
                {
                    if (QueryIsRule)
                        $$ = "*NEW*";
                    else
                        elog(ERROR,"NEW used in non-rule query");
                }
        ;

%%

static Node *
makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr)
{
    A_Expr *a = makeNode(A_Expr);
    a->oper = oper;
    a->opname = opname;
    a->lexpr = lexpr;
    a->rexpr = 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(ERROR,"Unequal number of entries in row expression");

    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(ERROR,"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(ERROR,"CREATE TABLE/AS SELECT has mismatched column count");

    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)
{
    Node *result = NULL;

    /* we do this so indexes can be used */
    if (strcmp(opname,"~") == 0 ||
        strcmp(opname,"~*") == 0)
    {
        if (nodeTag(rexpr) == T_A_Const &&
           ((A_Const *)rexpr)->val.type == T_String &&
           ((A_Const *)rexpr)->val.val.str[0] == '^')
        {
            A_Const *n = (A_Const *)rexpr;
            char *match_least = palloc(strlen(n->val.val.str)+2);
            char *match_most = palloc(strlen(n->val.val.str)+2);
            int pos, match_pos=0;

            /* skip leading ^ */
            for (pos = 1; n->val.val.str[pos]; pos++)
            {
                if (n->val.val.str[pos] == '.' ||
                    n->val.val.str[pos] == '?' ||
                    n->val.val.str[pos] == '*' ||
                    n->val.val.str[pos] == '[' ||
                    n->val.val.str[pos] == '$' ||
                    (strcmp(opname,"~*") == 0 && isalpha(n->val.val.str[pos])))
                     break;
                 if (n->val.val.str[pos] == '\\')
                    pos++;
                match_least[match_pos] = n->val.val.str[pos];
                match_most[match_pos++] = n->val.val.str[pos];
            }

            if (match_pos != 0)
            {
                A_Const *least = makeNode(A_Const);
                A_Const *most = makeNode(A_Const);

                /* make strings to be used in index use */
                match_least[match_pos] = '\0';
                match_most[match_pos] = '\377';
                match_most[match_pos+1] = '\0';
                least->val.type = T_String;
                least->val.val.str = match_least;
                most->val.type = T_String;
                most->val.val.str = match_most;
                result = makeA_Expr(AND, NULL,
                        makeA_Expr(OP, "~", lexpr, rexpr),
                        makeA_Expr(AND, NULL,
                            makeA_Expr(OP, ">=", lexpr, (Node *)least),
                            makeA_Expr(OP, "<=", lexpr, (Node *)most)));
            }
        }
    }
    else if (strcmp(opname,"~~") == 0)
    {
        if (nodeTag(rexpr) == T_A_Const &&
           ((A_Const *)rexpr)->val.type == T_String)
        {
            A_Const *n = (A_Const *)rexpr;
            char *match_least = palloc(strlen(n->val.val.str)+2);
            char *match_most = palloc(strlen(n->val.val.str)+2);
            int pos, match_pos=0;

            for (pos = 0; n->val.val.str[pos]; pos++)
            {
                if ((n->val.val.str[pos] == '%' &&
                     n->val.val.str[pos+1] != '%') ||
                    (n->val.val.str[pos] == '_' &&
                      n->val.val.str[pos+1] != '_'))
                     break;
                 if (n->val.val.str[pos] == '%' ||
                    n->val.val.str[pos] == '_' ||
                    n->val.val.str[pos] == '\\')
                    pos++;
                match_least[match_pos] = n->val.val.str[pos];
                match_most[match_pos++] = n->val.val.str[pos];
            }

            if (match_pos != 0)
            {
                A_Const *least = makeNode(A_Const);
                A_Const *most = makeNode(A_Const);

                /* make strings to be used in index use */
                match_least[match_pos] = '\0';
                match_most[match_pos] = '\377';
                match_most[match_pos+1] = '\0';
                least->val.type = T_String;
                least->val.val.str = match_least;
                most->val.type = T_String;
                most->val.val.str = match_most;
                result = makeA_Expr(AND, NULL,
                        makeA_Expr(OP, "~~", lexpr, rexpr),
                        makeA_Expr(AND, NULL,
                            makeA_Expr(OP, ">=", lexpr, (Node *)least),
                            makeA_Expr(OP, "<=", lexpr, (Node *)most)));
            }
        }
    }

    if (result == NULL)
        result = makeA_Expr(OP, opname, lexpr, rexpr);
    return result;
} /* makeIndexable() */


/* xlateSqlType()
 * Convert alternate type names to internal Postgres types.
 * Do not convert "float", since that is handled elsewhere
 *  for FLOAT(p) syntax.
 */
static char *
xlateSqlType(char *name)
{
    if (!strcasecmp(name,"int")
     || !strcasecmp(name,"integer"))
        return "int4";
    else if (!strcasecmp(name, "smallint"))
        return "int2";
    else if (!strcasecmp(name, "real"))
        return "float8";
    else if (!strcasecmp(name, "interval"))
        return "timespan";
    else if (!strcasecmp(name, "boolean"))
        return "bool";
    else
        return name;
} /* xlateSqlName() */


void parser_init(Oid *typev, int nargs)
{
    QueryIsRule = FALSE;
    saved_relname[0]= '\0';
    saved_In_Expr = NULL;

    param_type_init(typev, nargs);
}


/* FlattenStringList()
 * Traverse list of string nodes and convert to a single string.
 * Used for reconstructing string form of complex expressions.
 *
 * Allocate at least one byte for terminator.
 */
static char *
FlattenStringList(List *list)
{
    List *l;
    Value *v;
    char *s;
    char *sp;
    int nlist, len = 0;

    nlist = length(list);
    l = list;
    while(l != NIL) {
        v = (Value *)lfirst(l);
        sp = v->val.str;
        l = lnext(l);
        len += strlen(sp);
    };
    len += nlist;

    s = (char*) palloc(len+1);
    *s = '\0';

    l = list;
    while(l != NIL) {
        v = (Value *)lfirst(l);
        sp = v->val.str;
        l = lnext(l);
        strcat(s,sp);
        if (l != NIL) strcat(s," ");
    };
    *(s+len) = '\0';

#ifdef PARSEDEBUG
printf( "flattened string is \"%s\"\n", s);
#endif

    return(s);
} /* FlattenStringList() */


/* makeConstantList()
 * Convert constant value node into string node.
 */
static List *
makeConstantList( A_Const *n)
{
    char *defval = NULL;
    if (nodeTag(n) != T_A_Const) {
        elog(ERROR,"Cannot handle non-constant parameter");

    } else if (n->val.type == T_Float) {
        defval = (char*) palloc(20+1);
        sprintf( defval, "%g", n->val.val.dval);

    } else if (n->val.type == T_Integer) {
        defval = (char*) palloc(20+1);
        sprintf( defval, "%ld", n->val.val.ival);

    } else if (n->val.type == T_String) {
        defval = (char*) palloc(strlen( ((A_Const *) n)->val.val.str) + 3);
        strcpy( defval, "'");
        strcat( defval, ((A_Const *) n)->val.val.str);
        strcat( defval, "'");

    } else {
        elog(ERROR,"Internal error in makeConstantList(): cannot encode node");
    };

#ifdef PARSEDEBUG
printf( "AexprConst argument is \"%s\"\n", defval);
#endif

    return( lcons( makeString(defval), NIL));
} /* makeConstantList() */


/* fmtId()
 * Check input string for non-lowercase/non-numeric characters.
 * Returns either input string or input surrounded by double quotes.
 */
static char *
fmtId(char *rawid)
{
    static char *cp;

    for (cp = rawid; *cp != '\0'; cp++)
        if (! (islower(*cp) || isdigit(*cp) || (*cp == '_'))) break;

    if (*cp != '\0') {
        cp = palloc(strlen(rawid)+1);
        strcpy(cp,"\"");
        strcat(cp,rawid);
        strcat(cp,"\"");
    } else {
        cp = rawid;
    };

#ifdef PARSEDEBUG
printf("fmtId- %sconvert %s to %s\n", ((cp == rawid)? "do not ": ""), rawid, cp);
#endif

    return(cp);
}

/*
 * param_type_init()
 *
 * keep enough information around fill out the type of param nodes
 * used in postquel functions
 */
static void
param_type_init(Oid *typev, int nargs)
{
    pfunc_num_args = nargs;
    param_type_info = typev;
}

Oid param_type(int t)
{
    if ((t > pfunc_num_args) || (t == 0))
        return InvalidOid;
    return param_type_info[t - 1];
}
/*-------------------------------------------------------------------------
 *
 * keywords.c--
 *      lexical token lookup for reserved words in postgres SQL
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *      $Header: /usr/local/cvsroot/pgsql/src/backend/parser/keywords.c,v 1.29 1998/01/05 03:32:22 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <ctype.h>
#include <string.h>

#include "postgres.h"
#include "nodes/parsenodes.h"
#include "nodes/pg_list.h"
#include "parse.h"
#include "parser/keywords.h"
#include "utils/elog.h"

/*
 * List of (keyword-name, keyword-token-value) pairs.
 *
 * !!WARNING!!: This list must be sorted, because binary
 *         search is used to locate entries.
 */
static ScanKeyword ScanKeywords[] = {
    /* name                    value            */
    {"abort", ABORT_TRANS},
    {"acl", ACL},
    {"action", ACTION},
    {"add", ADD},
    {"after", AFTER},
    {"aggregate", AGGREGATE},
    {"all", ALL},
    {"alter", ALTER},
    {"analyze", ANALYZE},
    {"and", AND},
    {"any", ANY},
    {"append", APPEND},
    {"archive", ARCHIVE},
    {"as", AS},
    {"asc", ASC},
    {"backward", BACKWARD},
    {"before", BEFORE},
    {"begin", BEGIN_TRANS},
    {"between", BETWEEN},
    {"binary", BINARY},
    {"both", BOTH},
    {"by", BY},
    {"cascade", CASCADE},
    {"cast", CAST},
    {"change", CHANGE},
    {"char", CHAR},
    {"character", CHARACTER},
    {"check", CHECK},
    {"close", CLOSE},
    {"cluster", CLUSTER},
    {"collate", COLLATE},
    {"column", COLUMN},
    {"commit", COMMIT},
    {"constraint", CONSTRAINT},
    {"copy", COPY},
    {"create", CREATE},
    {"createdb", CREATEDB},
    {"createuser", CREATEUSER},
    {"cross", CROSS},
    {"current", CURRENT},
    {"current_date", CURRENT_DATE},
    {"current_time", CURRENT_TIME},
    {"current_timestamp", CURRENT_TIMESTAMP},
    {"current_user", CURRENT_USER},
    {"cursor", CURSOR},
    {"database", DATABASE},
    {"day", DAY_P},
    {"decimal", DECIMAL},
    {"declare", DECLARE},
    {"default", DEFAULT},
    {"delete", DELETE},
    {"delimiters", DELIMITERS},
    {"desc", DESC},
    {"distinct", DISTINCT},
    {"do", DO},
    {"double", DOUBLE},
    {"drop", DROP},
    {"end", END_TRANS},
    {"execute", EXECUTE},
    {"exists", EXISTS},
    {"explain", EXPLAIN},
    {"extend", EXTEND},
    {"extract", EXTRACT},
    {"false", FALSE_P},
    {"fetch", FETCH},
    {"float", FLOAT},
    {"for", FOR},
    {"foreign", FOREIGN},
    {"forward", FORWARD},
    {"from", FROM},
    {"full", FULL},
    {"function", FUNCTION},
    {"grant", GRANT},
    {"group", GROUP},
    {"handler", HANDLER},
    {"having", HAVING},
    {"hour", HOUR_P},
    {"in", IN},
    {"index", INDEX},
    {"inherits", INHERITS},
    {"inner", INNER_P},
    {"insert", INSERT},
    {"instead", INSTEAD},
    {"interval", INTERVAL},
    {"into", INTO},
    {"is", IS},
    {"isnull", ISNULL},
    {"join", JOIN},
    {"key", KEY},
    {"lancompiler", LANCOMPILER},
    {"language", LANGUAGE},
    {"leading", LEADING},
    {"left", LEFT},
    {"like", LIKE},
    {"listen", LISTEN},
    {"load", LOAD},
    {"local", LOCAL},
    {"location", LOCATION},
    {"match", MATCH},
    {"merge", MERGE},
    {"minute", MINUTE_P},
    {"month", MONTH_P},
    {"move", MOVE},
    {"national", NATIONAL},
    {"natural", NATURAL},
    {"nchar", NCHAR},
    {"new", NEW},
    {"no", NO},
    {"nocreatedb", NOCREATEDB},
    {"nocreateuser", NOCREATEUSER},
    {"none", NONE},
    {"not", NOT},
    {"nothing", NOTHING},
    {"notify", NOTIFY},
    {"notnull", NOTNULL},
    {"null", NULL_P},
    {"numeric", NUMERIC},
    {"oids", OIDS},
    {"on", ON},
    {"operator", OPERATOR},
    {"option", OPTION},
    {"or", OR},
    {"order", ORDER},
    {"outer", OUTER_P},
    {"partial", PARTIAL},
    {"password", PASSWORD},
    {"position", POSITION},
    {"precision", PRECISION},
    {"primary", PRIMARY},
    {"privileges", PRIVILEGES},
    {"procedural", PROCEDURAL},
    {"procedure", PROCEDURE},
    {"public", PUBLIC},
    {"recipe", RECIPE},
    {"references", REFERENCES},
    {"rename", RENAME},
    {"replace", REPLACE},
    {"reset", RESET},
    {"retrieve", RETRIEVE},
    {"returns", RETURNS},
    {"revoke", REVOKE},
    {"right", RIGHT},
    {"rollback", ROLLBACK},
    {"rule", RULE},
    {"second", SECOND_P},
    {"select", SELECT},
    {"sequence", SEQUENCE},
    {"set", SET},
    {"setof", SETOF},
    {"show", SHOW},
    {"some", SOME},
    {"stdin", STDIN},
    {"stdout", STDOUT},
    {"substring", SUBSTRING},
    {"table", TABLE},
    {"time", TIME},
    {"to", TO},
    {"trailing", TRAILING},
    {"transaction", TRANSACTION},
    {"trigger", TRIGGER},
    {"trim", TRIM},
    {"true", TRUE_P},
    {"trusted", TRUSTED},
    {"type", TYPE_P},
    {"union", UNION},
    {"unique", UNIQUE},
    {"until", UNTIL},
    {"update", UPDATE},
    {"user", USER},
    {"using", USING},
    {"vacuum", VACUUM},
    {"valid", VALID},
    {"values", VALUES},
    {"varchar", VARCHAR},
    {"varying", VARYING},
    {"verbose", VERBOSE},
    {"version", VERSION},
    {"view", VIEW},
    {"where", WHERE},
    {"with", WITH},
    {"work", WORK},
    {"year", YEAR_P},
    {"zone", ZONE},
};

ScanKeyword *
ScanKeywordLookup(char *text)
{
    ScanKeyword *low = &ScanKeywords[0];
    ScanKeyword *high = endof(ScanKeywords) - 1;
    ScanKeyword *middle;
    int            difference;

    while (low <= high)
    {
        middle = low + (high - low) / 2;
        difference = strcmp(middle->name, text);
        if (difference == 0)
            return (middle);
        else if (difference < 0)
            low = middle + 1;
        else
            high = middle - 1;
    }

    return (NULL);
}

#ifdef NOT_USED
char       *
AtomValueGetString(int atomval)
{
    ScanKeyword *low = &ScanKeywords[0];
    ScanKeyword *high = endof(ScanKeywords) - 1;
    int            keyword_list_length = (high - low);
    int            i;

    for (i = 0; i < keyword_list_length; i++)
        if (ScanKeywords[i].value == atomval)
            return (ScanKeywords[i].name);

    elog(ERROR, "AtomGetString called with bogus atom # : %d", atomval);
    return (NULL);
}

#endif

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Edmund Mergl
Дата:
Сообщение: Re: [HACKERS] DefaultHost
Следующее
От: darcy@druid.net (D'Arcy J.M. Cain)
Дата:
Сообщение: Re: [HACKERS] Can't run current PostgreSQL