%{

#include <NdbApi.hpp>
#include <common/StmtArea.hpp>
#include <common/DataRow.hpp>
#include "CodeGen.hpp"
#include <FlexLexer.h>
#include "SimpleParser.hpp"

/* redefine globals after headers */
#define yyparse		SimpleParser_yyparse
#if YYDEBUG
#define yydebug		SimpleParser_yydebug
#endif

#define YYLEX_PARAM	simpleParserPtr
#define YYPARSE_PARAM	simpleParserPtr
#define simpleParser	(*static_cast<SimpleParser*>(simpleParserPtr))

static int yylex(YYSTYPE* lvalp, void* simpleParserPtr);

#define yyerror(s)	simpleParser.parseError(s)

#if YYDEBUG
// does not work in bison 1.75
#undef stderr
#define stderr		0
#define YYFPRINTF	simpleParser.ctx().print
#endif

// scanner states

#define pushState(sc)	simpleParser.pushState(sc)
#define popState()	simpleParser.popState()

#define StateEval	SimpleParser_stateEval
#define StateType	SimpleParser_stateType
#define StatePhys	SimpleParser_statePhys
extern int SimpleParser_stateEval;
extern int SimpleParser_stateType;
extern int SimpleParser_statePhys;

struct LimitPair { int off; int cnt; };

struct PhysAttr { int storage; int logging; };

%}

%defines
%pure-parser
%verbose

%union {
    Plan_root* m_root;
    Plan_stmt* m_stmt;
    Plan_select* m_select;
    Insert_op m_insert_op;
    Plan_insert* m_insert;
    Plan_update* m_update;
    Plan_delete* m_delete;
    Plan_create_table* m_create_table;
    Plan_create_index* m_create_index;
    NdbDictionary::Object::Type m_index_type;
    Plan_create_row* m_create_row;
    Plan_ddl_row* m_ddl_row;
    Plan_ddl_column* m_ddl_column;
    Plan_ddl_constr* m_ddl_constr;
    Plan_idx_column* m_idx_column;
    Plan_data_type* m_data_type;
    Plan_drop_table* m_drop_table;
    Plan_drop_index* m_drop_index;
    Plan_set_row* m_set_row;
    Plan_expr_row* m_expr_row;
    bool m_asc_desc;
    Plan_pred* m_pred;
    Pred_op::Opcode m_pred_opcode;
    Comp_op::Opcode m_comp_opcode;
    Plan_expr* m_expr;
    Expr_op::Opcode m_expr_opcode;
    Plan_dml_row* m_dml_row;
    Plan_dml_column* m_dml_column;
    Plan_table* m_table;
    Plan_table_list* m_table_list;
    const char* m_string;
    struct LimitPair* m_limit;
    int m_signed_integer;
    bool m_distinct;
    struct PhysAttr* m_phys_attr;
    NdbDictionary::Object::FragmentType m_storage_attr;
    bool m_logging_attr;
    SqlType::Type m_sql_type;
}

/* keywords */
%token
    T_AND
    T_ASC
    T_AUTO_INCREMENT
    T_BIGINT
    T_BINARY
    T_BLOB
    T_BY
    T_CHAR
    T_CLOB
    T_CONSTRAINT
    T_CREATE
    T_DATETIME
    T_DEFAULT
    T_DELETE
    T_DESC
    T_DISTINCT
    T_DOUBLE
    T_DROP
    T_FLOAT
    T_FOREIGN
    T_FROM
    T_GROUP
    T_HASH
    T_HAVING
    T_IN
    T_INDEX
    T_INSERT
    T_INT
    T_INTEGER
    T_INTO
    T_IS
    T_KEY
    T_LARGE
    T_LIKE
    T_LIMIT
    T_LOGGING
    T_LONGBLOB
    T_LONGCLOB
    T_MEDIUM
    T_NOLOGGING
    T_NOT
    T_NULL
    T_OFFSET
    T_ON
    T_OR
    T_ORDER
    T_PRECISION
    T_PRIMARY
    T_REAL
    T_REFERENCES
    T_ROWNUM
    T_SELECT
    T_SET
    T_SINGLE
    T_SMALL
    T_SMALLINT
    T_STORAGE
    T_SYSDATE
    T_TABLE
    T_UNIQUE
    T_UNSIGNED
    T_UPDATE
    T_VALUES
    T_VARBINARY
    T_VARCHAR
    T_WHERE
    T_WRITE

/* identifiers and constants */
%token
    <m_string> T_IDENTIFIER
    <m_string> T_LINTEGER
    <m_string> T_LDECIMAL
    <m_string> T_LREAL
    <m_string> T_STRING

/* expressions and predicates */
%token
    T_PLUS
    T_MINUS
    T_TIMES
    T_DIVIDE
    T_EQ
    T_NOTEQ
    T_LT
    T_LTEQ
    T_GT
    T_GTEQ
    T_QUES

/* common special symbols */
%token
    T_PERIOD
    T_COMMA
    T_PARENLEFT
    T_PARENRIGHT
    T_ASTERISK
    T_ASSIGN

%type <m_root>			root
%type <m_stmt>			stmt
%type <m_select>		stmt_select
%type <m_insert>		stmt_insert
%type <m_insert_op>		insert_op
%type <m_update>		stmt_update
%type <m_delete>		stmt_delete
%type <m_create_table>		create_table
%type <m_create_index>		create_index
%type <m_index_type>		index_type
%type <m_create_row>		create_row
%type <m_ddl_column>		create_column
%type <m_ddl_constr>		create_constr
%type <m_idx_column>		idx_column
%type <m_data_type>		data_type
%type <m_ddl_row>		ddl_row
%type <m_ddl_column>		ddl_column
%type <m_drop_table>		drop_table
%type <m_drop_index>		drop_index
%type <m_asc_desc>		asc_desc
%type <m_set_row>		set_row
%type <m_expr_row>		expr_row
%type <m_expr_row>		sort_row
%type <m_pred>			where_clause
%type <m_expr_row>		order_clause
%type <m_pred>			pred
%type <m_pred>			pred1
%type <m_pred_opcode>		pred1_op
%type <m_pred>			pred2
%type <m_pred_opcode>		pred2_op
%type <m_pred>			pred3
%type <m_pred_opcode>		pred3_op
%type <m_pred>			pred4
%type <m_comp_opcode>		comp_op
%type <m_expr>			expr
%type <m_expr>			expr1
%type <m_expr_opcode>		expr1_op
%type <m_expr>			expr2
%type <m_expr_opcode>		expr2_op
%type <m_expr>			expr3
%type <m_expr_opcode>		expr3_op
%type <m_expr>			expr4
%type <m_expr>			expr_column
%type <m_dml_row>		dml_row
%type <m_dml_column>		dml_column
%type <m_expr_row>		value_row
%type <m_expr>			value_expr
%type <m_table>			table
%type <m_table_list>		table_list
%type <m_string>		dot_identifier
%type <m_limit>			limit_clause
%type <m_signed_integer>	signed_integer
%type <m_distinct>		distinct_clause
%type <m_expr_row>		group_clause
%type <m_pred>			having_clause
%type <m_phys_attr>		phys_attr
%type <m_phys_attr>		phys_attr2
%type <m_storage_attr>		storage_attr
%type <m_logging_attr>		logging_attr
%type <m_sql_type>		blob_type

%%

root:
    stmt
    {
	Plan_root* root = simpleParser.root();
	root->setStmt($1);
    }
    ;
stmt:
    stmt_select
    {
	$$ = $1;
    }
    |
    stmt_insert
    {
	$$ = $1;
    }
    |
    stmt_update
    {
	$$ = $1;
    }
    |
    stmt_delete
    {
	$$ = $1;
    }
    |
    create_table
    {
	$$ = $1;
    }
    |
    create_index
    {
	$$ = $1;
    }
    |
    drop_table
    {
	$$ = $1;
    }
    |
    drop_index
    {
	$$ = $1;
    }
    ;
stmt_select:
    T_SELECT distinct_clause expr_row T_FROM table_list where_clause group_clause having_clause order_clause limit_clause
    {
	Plan_root* root = simpleParser.root();
	Plan_select* stmt = new Plan_select(root);
	root->saveNode(stmt);
	stmt->setDistinct($2);
	stmt->setRow($3);
	stmt->setList($5);
	if ($6 != 0)
	    stmt->setPred($6);
	if ($7 != 0)
	    stmt->setGroup($7);
	if ($8 != 0)
	    stmt->setHaving($8);
	if ($9 != 0)
	    stmt->setSort($9);
	if ($10 != 0) {
	    stmt->setLimit($10->off, $10->cnt);
	    delete $10;
	}
	$$ = stmt;
    }
    ;
stmt_insert:
    insert_op T_INTO table T_VALUES T_PARENLEFT value_row T_PARENRIGHT
    {
	Plan_root* root = simpleParser.root();
	Plan_insert* stmt = new Plan_insert(root, $1);
	root->saveNode(stmt);
	stmt->setTable($3);
	stmt->setExprRow($6);
	$$ = stmt;
    }
    |
    insert_op T_INTO table T_PARENLEFT dml_row T_PARENRIGHT T_VALUES T_PARENLEFT value_row T_PARENRIGHT
    {
	Plan_root* root = simpleParser.root();
	Plan_insert* stmt = new Plan_insert(root, $1);
	root->saveNode(stmt);
	stmt->setTable($3);
	stmt->setDmlRow($5);
	stmt->setExprRow($9);
	$$ = stmt;
    }
    |
    insert_op T_INTO table stmt_select
    {
	Plan_root* root = simpleParser.root();
	Plan_insert* stmt = new Plan_insert(root, $1);
	root->saveNode(stmt);
	stmt->setTable($3);
	stmt->setSelect($4);
	$$ = stmt;
    }
    |
    insert_op T_INTO table T_PARENLEFT dml_row T_PARENRIGHT stmt_select
    {
	Plan_root* root = simpleParser.root();
	Plan_insert* stmt = new Plan_insert(root, $1);
	root->saveNode(stmt);
	stmt->setTable($3);
	stmt->setDmlRow($5);
	stmt->setSelect($7);
	$$ = stmt;
    }
    |
    insert_op T_INTO table T_SET set_row
    {
	Plan_root* root = simpleParser.root();
	Plan_insert* stmt = new Plan_insert(root, $1);
	root->saveNode(stmt);
	stmt->setTable($3);
	stmt->setMysqlRow($5);
	$$ = stmt;
    }
    ;
insert_op:
    T_INSERT
    {
	$$ = Insert_op_insert;
    }
    |
    T_WRITE
    {
	$$ = Insert_op_write;
    }
    ;
stmt_update:
    T_UPDATE table T_SET set_row where_clause
    {
	Plan_root* root = simpleParser.root();
	Plan_update* stmt = new Plan_update(root);
	root->saveNode(stmt);
	stmt->setTable($2);
	stmt->setRow($4);
	if ($5 != 0)
	    stmt->setPred($5);
	$$ = stmt;
    }
    ;
stmt_delete:
    T_DELETE T_FROM table where_clause
    {
	Plan_root* root = simpleParser.root();
	Plan_delete* stmt = new Plan_delete(root);
	root->saveNode(stmt);
	stmt->setTable($3);
	if ($4 != 0)
	    stmt->setPred($4);
	$$ = stmt;
    }
    ;
create_table:
    T_CREATE T_TABLE dot_identifier T_PARENLEFT create_row T_PARENRIGHT phys_attr
    {
	Plan_root* root = simpleParser.root();
	Plan_create_table* stmt = new Plan_create_table(root, $3);
	root->saveNode(stmt);
	delete[] $3;
	stmt->setCreateRow($5);
	if ($7->storage != -1)
	    stmt->setFragmentType((NdbDictionary::Object::FragmentType)$7->storage);
	if ($7->logging != -1)
	    stmt->setLogging($7->logging);
	delete $7;
	$$ = stmt;
    }
    ;
create_row:
    create_column
    {
	Plan_root* root = simpleParser.root();
	Plan_create_row* createRow = new Plan_create_row(root);
	root->saveNode(createRow);
	createRow->addColumn($1);
	$$ = createRow;
    }
    |
    create_constr
    {
	Plan_root* root = simpleParser.root();
	Plan_create_row* createRow = new Plan_create_row(root);
	root->saveNode(createRow);
	createRow->addConstr($1);
	$$ = createRow;
    }
    |
    create_row T_COMMA create_column
    {
	Plan_create_row* createRow = $1;
	createRow->addColumn($3);
	$$ = createRow;
    }
    |
    create_row T_COMMA create_constr
    {
	Plan_create_row* createRow = $1;
	createRow->addConstr($3);
	$$ = createRow;
    }
    |
    create_row T_COMMA create_ignore
    {
	$$ = $1;
    }
    ;
create_column:
    T_IDENTIFIER { pushState(StateType); } data_type { popState(); }
    {
	Plan_root* root = simpleParser.root();
	Plan_ddl_column* ddlColumn = new Plan_ddl_column(root, $1);
	root->saveNode(ddlColumn);
	delete[] $1;
	ddlColumn->setType($3);
	simpleParser.curr(ddlColumn);
    }
    create_column_rest
    {
	$$ = simpleParser.curr((Plan_ddl_column*)0);
    }
    ;
data_type:
    T_CHAR T_PARENLEFT T_LINTEGER T_PARENRIGHT dummy_binary
    {
	Plan_root* root = simpleParser.root();
	SqlType sqlType(simpleParser.ctx(), SqlType::Char, atoi($3), true);
	delete[] $3;
	if (! simpleParser.ctx().ok()) {
	    YYABORT;
	}
	Plan_data_type* dataType = new Plan_data_type(root, sqlType);
	root->saveNode(dataType);
	$$ = dataType;
    }
    |
    T_BINARY T_PARENLEFT T_LINTEGER T_PARENRIGHT
    {
	Plan_root* root = simpleParser.root();
	SqlType sqlType(simpleParser.ctx(), SqlType::Binary, atoi($3), true);
	delete[] $3;
	if (! simpleParser.ctx().ok()) {
	    YYABORT;
	}
	Plan_data_type* dataType = new Plan_data_type(root, sqlType);
	root->saveNode(dataType);
	$$ = dataType;
    }
    |
    T_VARCHAR T_PARENLEFT T_LINTEGER T_PARENRIGHT dummy_binary
    {
	Plan_root* root = simpleParser.root();
	SqlType sqlType(simpleParser.ctx(), SqlType::Varchar, atoi($3), true);
	delete[] $3;
	if (! simpleParser.ctx().ok()) {
	    YYABORT;
	}
	Plan_data_type* dataType = new Plan_data_type(root, sqlType);
	root->saveNode(dataType);
	$$ = dataType;
    }
    |
    T_VARBINARY T_PARENLEFT T_LINTEGER T_PARENRIGHT
    {
	Plan_root* root = simpleParser.root();
	SqlType sqlType(simpleParser.ctx(), SqlType::Varbinary, atoi($3), true);
	delete[] $3;
	if (! simpleParser.ctx().ok()) {
	    YYABORT;
	}
	Plan_data_type* dataType = new Plan_data_type(root, sqlType);
	root->saveNode(dataType);
	$$ = dataType;
    }
    |
    T_SMALLINT
    {
	Plan_root* root = simpleParser.root();
	SqlType sqlType(SqlType::Smallint, true);
	Plan_data_type* dataType = new Plan_data_type(root, sqlType);
	root->saveNode(dataType);
	$$ = dataType;
    }
    |
    T_INTEGER
    {
	Plan_root* root = simpleParser.root();
	SqlType sqlType(SqlType::Integer, true);
	Plan_data_type* dataType = new Plan_data_type(root, sqlType);
	root->saveNode(dataType);
	$$ = dataType;
    }
    |
    T_INT
    {
	Plan_root* root = simpleParser.root();
	SqlType sqlType(SqlType::Integer, true);
	Plan_data_type* dataType = new Plan_data_type(root, sqlType);
	root->saveNode(dataType);
	$$ = dataType;
    }
    |
    T_BIGINT
    {
	Plan_root* root = simpleParser.root();
	SqlType sqlType(SqlType::Bigint, true);
	Plan_data_type* dataType = new Plan_data_type(root, sqlType);
	root->saveNode(dataType);
	$$ = dataType;
    }
    |
    T_REAL
    {
	Plan_root* root = simpleParser.root();
	SqlType sqlType(SqlType::Real, true);
	Plan_data_type* dataType = new Plan_data_type(root, sqlType);
	root->saveNode(dataType);
	$$ = dataType;
    }
    |
    T_FLOAT
    {
	Plan_root* root = simpleParser.root();
	SqlType sqlType(SqlType::Double, true);
	Plan_data_type* dataType = new Plan_data_type(root, sqlType);
	root->saveNode(dataType);
	$$ = dataType;
    }
    |
    T_DOUBLE T_PRECISION
    {
	Plan_root* root = simpleParser.root();
	SqlType sqlType(SqlType::Double, true);
	Plan_data_type* dataType = new Plan_data_type(root, sqlType);
	root->saveNode(dataType);
	$$ = dataType;
    }
    |
    T_DATETIME
    {
	Plan_root* root = simpleParser.root();
	SqlType sqlType(SqlType::Datetime, true);
	Plan_data_type* dataType = new Plan_data_type(root, sqlType);
	root->saveNode(dataType);
	$$ = dataType;
    }
    |
    blob_type
    {
	Plan_root* root = simpleParser.root();
	SqlType sqlType($1, true);
	Plan_data_type* dataType = new Plan_data_type(root, sqlType);
	root->saveNode(dataType);
	$$ = dataType;
    }
    ;
dummy_binary:
    /* empty */
    |
    T_BINARY
    ;
blob_type:
    T_BLOB
    {
	$$ = SqlType::Blob;
    }
    |
    T_LONGBLOB
    {
	$$ = SqlType::Blob;
    }
    |
    T_CLOB
    {
	$$ = SqlType::Clob;
    }
    |
    T_LONGCLOB
    {
	$$ = SqlType::Clob;
    }
    ;
create_column_rest:
    /* empty */
    |
    data_constr_list
    ;
data_constr_list:
    data_constr
    |
    data_constr_list data_constr
    ;
data_constr:
    T_NULL
    |
    T_NOT T_NULL
    {
	Plan_ddl_column* ddlColumn = simpleParser.curr((Plan_ddl_column*)0);
	ddlColumn->setNotNull();
    }
    |
    T_UNSIGNED
    {
	Plan_ddl_column* ddlColumn = simpleParser.curr((Plan_ddl_column*)0);
	ddlColumn->setUnSigned();
    }
    |
    T_PRIMARY T_KEY
    {
	Plan_ddl_column* ddlColumn = simpleParser.curr((Plan_ddl_column*)0);
	ddlColumn->setPrimaryKey();
    }
    |
    T_AUTO_INCREMENT
    {
	Plan_ddl_column* ddlColumn = simpleParser.curr((Plan_ddl_column*)0);
	ddlColumn->setAutoIncrement();
    }
    |
    T_DEFAULT expr
    {
	Plan_ddl_column* ddlColumn = simpleParser.curr((Plan_ddl_column*)0);
	ddlColumn->setDefaultValue($2);
    }
    ;
create_constr:
    T_PRIMARY T_KEY T_PARENLEFT ddl_row T_PARENRIGHT
    {
	Plan_root* root = simpleParser.root();
	Plan_ddl_constr* ddlConstr = new Plan_ddl_constr(root);
	root->saveNode(ddlConstr);
	ddlConstr->setRow($4);
	$$ = ddlConstr;
    }
    |
    T_CONSTRAINT dot_identifier T_PRIMARY T_KEY T_PARENLEFT ddl_row T_PARENRIGHT
    {
	Plan_root* root = simpleParser.root();
	Plan_ddl_constr* ddlConstr = new Plan_ddl_constr(root);
	root->saveNode(ddlConstr);
	ddlConstr->setRow($6);
	$$ = ddlConstr;
    }
    ;
create_ignore:
    T_INDEX dot_identifier T_PARENLEFT ddl_row T_PARENRIGHT
    |
    T_FOREIGN T_KEY T_PARENLEFT ddl_row T_PARENRIGHT T_REFERENCES dot_identifier T_PARENLEFT ddl_row T_PARENRIGHT
    ;
ddl_row:
    ddl_column
    {
	Plan_root* root = simpleParser.root();
	Plan_ddl_row* ddlRow = new Plan_ddl_row(root);
	root->saveNode(ddlRow);
	ddlRow->addColumn($1);
	$$ = ddlRow;
    }
    |
    ddl_row T_COMMA ddl_column
    {
	Plan_ddl_row* ddlRow = $1;
	ddlRow->addColumn($3);
	$$ = ddlRow;
    }
    ;
ddl_column:
    T_IDENTIFIER
    {
	Plan_root* root = simpleParser.root();
	Plan_ddl_column* column = new Plan_ddl_column(root, $1);
	root->saveNode(column);
	delete[] $1;
	$$ = column;
    }
    ;
create_index:
    T_CREATE index_type T_INDEX dot_identifier T_ON table
    {
	Plan_root* root = simpleParser.root();
	Plan_create_index* stmt = new Plan_create_index(root, $4);
	root->saveNode(stmt);
	delete[] $4;
	stmt->setType($2);
	stmt->setTable($6);
	simpleParser.curr(stmt);
    }
    T_PARENLEFT idx_row T_PARENRIGHT phys_attr
    {
	$$ = simpleParser.curr((Plan_create_index*)0);
	if ($11->storage != -1)
	    $$->setFragmentType((NdbDictionary::Object::FragmentType)$11->storage);
	if ($11->logging != -1)
	    $$->setLogging($11->logging);
	delete $11;
    }
    ;
index_type:
    T_HASH
    {
	$$ = NdbDictionary::Object::HashIndex;
    }
    |
    T_UNIQUE T_HASH
    {
	$$ = NdbDictionary::Object::UniqueHashIndex;
    }
    |
    /* empty */
    {
	$$ = NdbDictionary::Object::OrderedIndex;
    }
    |
    T_UNIQUE
    {
	$$ = NdbDictionary::Object::UniqueOrderedIndex;
    }
    ;
idx_row:
    idx_column
    {
    }
    |
    idx_row T_COMMA idx_column
    {
    }
    ;
idx_column:
    T_IDENTIFIER
    {
	Plan_root* root = simpleParser.root();
	Plan_idx_column* column = new Plan_idx_column(root, $1);
	root->saveNode(column);
	delete[] $1;
	Plan_create_index* stmt = simpleParser.curr((Plan_create_index*)0);
	stmt->addColumn(column);
    }
    ;
phys_attr:
    { pushState(StatePhys); } phys_attr2 { popState(); }
    {
	$$ = $2;
    }
    ;
phys_attr2:
    /* empty */
    {
	$$ = new PhysAttr();
	$$->storage = $$->logging = -1;
    }
    |
    phys_attr2 storage_attr
    {
	if ($1->storage != -1 && $1->storage != $2) {
	    simpleParser.ctx().pushStatus(Sqlstate::_42000, Error::Gen, "conflicting STORAGE clauses");
	    YYABORT;
	}
	$$->storage = $2;
    }
    |
    phys_attr2 logging_attr
    {
	if ($1->logging != -1 && $1->logging != $2) {
	    simpleParser.ctx().pushStatus(Sqlstate::_42000, Error::Gen, "conflicting LOGGING clauses");
	    YYABORT;
	}
	$$->logging = $2;
    }
    ;
logging_attr:
    T_LOGGING
    {
	$$ = true;
    }
    |
    T_NOLOGGING
    {
	$$ = false;
    }
    ;
storage_attr:
    T_STORAGE T_PARENLEFT T_SINGLE T_PARENRIGHT
    {
	$$ = NdbDictionary::Object::FragSingle;
    }
    |
    T_STORAGE T_PARENLEFT T_SMALL T_PARENRIGHT
    {
	$$ = NdbDictionary::Object::FragAllSmall;
    }
    |
    T_STORAGE T_PARENLEFT T_MEDIUM T_PARENRIGHT
    {
	$$ = NdbDictionary::Object::FragAllMedium;
    }
    |
    T_STORAGE T_PARENLEFT T_LARGE T_PARENRIGHT
    {
	$$ = NdbDictionary::Object::FragAllLarge;
    }
    ;
drop_table:
    T_DROP T_TABLE dot_identifier
    {
	Plan_root* root = simpleParser.root();
	Plan_drop_table* stmt = new Plan_drop_table(root, $3);
	root->saveNode(stmt);
	delete[] $3;
	$$ = stmt;
    }
    ;
drop_index:
    T_DROP T_INDEX dot_identifier
    {
	Plan_root* root = simpleParser.root();
	Plan_drop_index* stmt = new Plan_drop_index(root, $3);
	root->saveNode(stmt);
	delete[] $3;
	$$ = stmt;
    }
    |
    T_DROP T_INDEX dot_identifier T_ON dot_identifier
    {
	Plan_root* root = simpleParser.root();
	Plan_drop_index* stmt = new Plan_drop_index(root, $3, $5);
	root->saveNode(stmt);
	delete[] $3;
	delete[] $5;
	$$ = stmt;
    }
    ;
distinct_clause:
    /* empty */
    {
	$$ = false;
    }
    |
    T_DISTINCT
    {
	$$ = true;
    }
    ;
where_clause:
    /* empty */
    {
	$$ = 0;
    }
    |
    T_WHERE pred
    {
	$$ = $2;
    }
    ;
group_clause:
    /* empty */
    {
	$$ = 0;
    }
    |
    T_GROUP T_BY value_row
    {
	$$ = $3;
    }
    ;
having_clause:
    /* empty */
    {
	$$ = 0;
    }
    |
    T_HAVING pred
    {
	$$ = $2;
    }
    ;
order_clause:
    /* empty */
    {
	$$ = 0;
    }
    |
    T_ORDER T_BY sort_row
    {
	$$ = $3;
    }
    ;
limit_clause:
    /* empty */
    {
	$$ = 0;
    }
    |
    T_LIMIT signed_integer
    {
	LimitPair* p = new LimitPair;
	p->off = 0;
	p->cnt = $2;
	$$ = p;
    }
    |
    T_LIMIT signed_integer T_COMMA signed_integer
    {
	LimitPair* p = new LimitPair;
	p->off = $2,
	p->cnt = $4;
	$$ = p;
    }
    |
    T_LIMIT signed_integer T_OFFSET signed_integer
    {
	LimitPair* p = new LimitPair;
	p->off = $4;
	p->cnt = $2;
	$$ = p;
    }
    ;
signed_integer:
    T_LINTEGER
    {
	$$ = atoi($1);
	delete[] $1;
    }
    |
    T_MINUS T_LINTEGER
    {
	$$ = (-1) * atoi($2);
	delete[] $2;
    }
    ;
set_row:
    dml_column T_ASSIGN expr
    {
	Plan_root* root = simpleParser.root();
	Plan_set_row* row = new Plan_set_row(root);
	root->saveNode(row);
	row->addColumn($1);
	row->addExpr($3);
	$$ = row;
    }
    |
    set_row T_COMMA dml_column T_ASSIGN expr
    {
	Plan_set_row* row = $1;
	row->addColumn($3);
	row->addExpr($5);
	$$ = row;
    }
    ;
dml_row:
    dml_column
    {
	Plan_root* root = simpleParser.root();
	Plan_dml_row* row = new Plan_dml_row(root);
	root->saveNode(row);
	row->addColumn($1);
	$$ = row;
    }
    |
    dml_row T_COMMA dml_column
    {
	Plan_dml_row* row = $1;
	row->addColumn($3);
	$$ = row;
    }
    ;
dml_column:
    T_IDENTIFIER
    {
	Plan_root* root = simpleParser.root();
	Plan_dml_column* column = new Plan_dml_column(root, $1);
	root->saveNode(column);
	delete[] $1;
	$$ = column;
    }
    ;
value_row:
    value_expr
    {
	Plan_root* root = simpleParser.root();
	Plan_expr_row* row = new Plan_expr_row(root);
	root->saveNode(row);
	row->addExpr($1);
	$$ = row;
    }
    |
    value_row T_COMMA value_expr
    {
	Plan_expr_row* row = $1;
	row->addExpr($3);
	$$ = row;
    }
    ;
value_expr:
    expr
    {
	$$ = $1;
    }
    ;
sort_row:
    expr asc_desc
    {
	Plan_root* root = simpleParser.root();
	Plan_expr_row* row = new Plan_expr_row(root);
	root->saveNode(row);
	row->addExpr($1, $2);
	$$ = row;
    }
    |
    sort_row T_COMMA expr asc_desc
    {
	Plan_expr_row* row = $1;
	row->addExpr($3, $4);
	$$ = row;
    }
    ;
asc_desc:
    /* empty */
    {
	$$ = true;
    }
    |
    T_ASC
    {
	$$ = true;
    }
    |
    T_DESC
    {
	$$ = false;
    }
    ;
expr_row:
    T_ASTERISK
    {
	Plan_root* root = simpleParser.root();
	Plan_expr_row* row = new Plan_expr_row(root);
	root->saveNode(row);
	row->setAsterisk();
	$$ = row;
    }
    |
    T_TIMES	/* XXX fix scanner state */
    {
	Plan_root* root = simpleParser.root();
	Plan_expr_row* row = new Plan_expr_row(root);
	root->saveNode(row);
	row->setAsterisk();
	$$ = row;
    }
    |
    expr
    {
	Plan_root* root = simpleParser.root();
	Plan_expr_row* row = new Plan_expr_row(root);
	root->saveNode(row);
	row->addExpr($1);
	$$ = row;
    }
    |
    expr T_IDENTIFIER
    {
	Plan_root* root = simpleParser.root();
	Plan_expr_row* row = new Plan_expr_row(root);
	root->saveNode(row);
	row->addExpr($1, BaseString($2));
	$$ = row;
    }
    |
    expr_row T_COMMA expr
    {
	Plan_expr_row* row = $1;
	row->addExpr($3);
	$$ = row;
    }
    |
    expr_row T_COMMA expr T_IDENTIFIER
    {
	Plan_expr_row* row = $1;
	row->addExpr($3, BaseString($4));
	$$ = row;
    }
    ;
pred:
    { pushState(StateEval); } pred1 { popState(); }
    {
	$$ = $2;
    }
    ;
pred1:
    pred2
    {
	$$ = $1;
    }
    |
    pred1 pred1_op pred2
    {
	Plan_root* root = simpleParser.root();
	Pred_op op($2);
	Plan_pred_op* pred = new Plan_pred_op(root, op);
	root->saveNode(pred);
	pred->setPred(1, $1);
	pred->setPred(2, $3);
	$$ = pred;
    }
    ;
pred1_op:
    T_OR
    {
	$$ = Pred_op::Or;
    }
    ;
pred2:
    pred3
    {
	$$ = $1;
    }
    |
    pred2 pred2_op pred3
    {
	Plan_root* root = simpleParser.root();
	Pred_op op($2);
	Plan_pred_op* pred = new Plan_pred_op(root, op);
	root->saveNode(pred);
	pred->setPred(1, $1);
	pred->setPred(2, $3);
	$$ = pred;
    }
    ;
pred2_op:
    T_AND
    {
	$$ = Pred_op::And;
    }
    ;
pred3:
    pred4
    {
	$$ = $1;
    }
    |
    pred3_op pred3
    {
	Plan_root* root = simpleParser.root();
	Pred_op op($1);
	Plan_pred_op* pred = new Plan_pred_op(root, op);
	root->saveNode(pred);
	pred->setPred(1, $2);
	$$ = pred;
    }
    ;
pred3_op:
    T_NOT
    {
	$$ = Pred_op::Not;
    }
    ;
pred4:
    T_PARENLEFT pred1 T_PARENRIGHT
    {
	$$ = $2;
    }
    |
    expr1 comp_op expr1
    {
	Plan_root* root = simpleParser.root();
	Comp_op op($2);
	Plan_comp_op* comp = new Plan_comp_op(root, op);
	root->saveNode(comp);
	comp->setExpr(1, $1);
	comp->setExpr(2, $3);
	$$ = comp;
    }
    |
    expr1 T_IS T_NULL
    {
	Plan_root* root = simpleParser.root();
	Comp_op op(Comp_op::Isnull);
	Plan_comp_op* comp = new Plan_comp_op(root, op);
	root->saveNode(comp);
	comp->setExpr(1, $1);
	$$ = comp;
    }
    |
    expr1 T_IS T_NOT T_NULL
    {
	Plan_root* root = simpleParser.root();
	Comp_op op(Comp_op::Isnotnull);
	Plan_comp_op* comp = new Plan_comp_op(root, op);
	root->saveNode(comp);
	comp->setExpr(1, $1);
	$$ = comp;
    }
    |
    expr1 T_IN T_PARENLEFT value_row T_PARENRIGHT
    {
	Plan_root* root = simpleParser.root();
	Plan_pred* predOut = 0;		// hack directly into Or of Eq
	Plan_expr* exprLeft = $1;
	Plan_expr_row* row = $4;
	for (unsigned i = row->getSize(); i >= 1; i--) {
	    Plan_expr* exprRight = row->getExpr(i);
	    Plan_comp_op* comp = new Plan_comp_op(root, Comp_op::Eq);
	    root->saveNode(comp);
	    comp->setExpr(1, exprLeft);
	    comp->setExpr(2, exprRight);
	    if (predOut == 0) {
		predOut = comp;
	    } else {
		Plan_pred_op* pred = new Plan_pred_op(root, Pred_op::Or);
		root->saveNode(pred);
		pred->setPred(1, predOut);
		pred->setPred(2, comp);
		predOut = pred;
	    }
	}
	$$ = predOut;
    }
    |
    expr1 T_NOT T_IN T_PARENLEFT value_row T_PARENRIGHT
    {
	Plan_root* root = simpleParser.root();
	Plan_pred* predOut = 0;		// hack directly into And of Noteq
	Plan_expr* exprLeft = $1;
	Plan_expr_row* row = $5;
	for (unsigned i = row->getSize(); i >= 1; i--) {
	    Plan_expr* exprRight = row->getExpr(i);
	    Plan_comp_op* comp = new Plan_comp_op(root, Comp_op::Noteq);
	    root->saveNode(comp);
	    comp->setExpr(1, exprLeft);
	    comp->setExpr(2, exprRight);
	    if (predOut == 0) {
		predOut = comp;
	    } else {
		Plan_pred_op* pred = new Plan_pred_op(root, Pred_op::And);
		root->saveNode(pred);
		pred->setPred(1, predOut);
		pred->setPred(2, comp);
		predOut = pred;
	    }
	}
	$$ = predOut;
    }
    ;
comp_op:
    T_EQ
    {
	$$ = Comp_op::Eq;
    }
    |
    T_NOTEQ
    {
	$$ = Comp_op::Noteq;
    }
    |
    T_LT
    {
	$$ = Comp_op::Lt;
    }
    |
    T_LTEQ
    {
	$$ = Comp_op::Lteq;
    }
    |
    T_GT
    {
	$$ = Comp_op::Gt;
    }
    |
    T_GTEQ
    {
	$$ = Comp_op::Gteq;
    }
    |
    T_LIKE
    {
	$$ = Comp_op::Like;
    }
    |
    T_NOT T_LIKE
    {
	$$ = Comp_op::Notlike;
    }
    ;
expr:
    { pushState(StateEval); } expr1 { popState(); }
    {
	$$ = $2;
    }
    ;
expr1:
    expr2
    {
	$$ = $1;
    }
    |
    expr1 expr1_op expr2
    {
	Plan_root* root = simpleParser.root();
	Expr_op op($2);
	Plan_expr_op* expr = new Plan_expr_op(root, op);
	root->saveNode(expr);
	expr->setExpr(1, $1);
	expr->setExpr(2, $3);
	$$ = expr;
    }
    ;
expr1_op:
    T_PLUS
    {
	$$ = Expr_op::Add;
    }
    |
    T_MINUS
    {
	$$ = Expr_op::Subtract;
    }
    ;
expr2:
    expr3
    {
	$$ = $1;
    }
    |
    expr2 expr2_op expr3
    {
	Plan_root* root = simpleParser.root();
	Expr_op op($2);
	Plan_expr_op* expr = new Plan_expr_op(root, op);
	root->saveNode(expr);
	expr->setExpr(1, $1);
	expr->setExpr(2, $3);
	$$ = expr;
    }
    ;
expr2_op:
    T_TIMES
    {
	$$ = Expr_op::Multiply;
    }
    |
    T_DIVIDE
    {
	$$ = Expr_op::Divide;
    }
    ;
expr3:
    expr4
    {
	$$ = $1;
    }
    |
    expr3_op expr3
    {
	Plan_root* root = simpleParser.root();
	Expr_op op($1);
	Plan_expr_op* expr = new Plan_expr_op(root, op);
	root->saveNode(expr);
	expr->setExpr(1, $2);
	$$ = expr;
    }
    ;
expr3_op:
    T_PLUS
    {
	$$ = Expr_op::Plus;
    }
    |
    T_MINUS
    {
	$$ = Expr_op::Minus;
    }
    ;
expr4:
    T_PARENLEFT expr1 T_PARENRIGHT
    {
	$$ = $2;
    }
    |
    T_IDENTIFIER T_PARENLEFT expr_row T_PARENRIGHT
    {
	Plan_root* root = simpleParser.root();
	const Expr_func& spec = Expr_func::find($1);
	if (spec.m_name == 0) {
	    simpleParser.ctx().pushStatus(Sqlstate::_42000, Error::Gen, "unknown function %s", $1);
	    delete[] $1;
	    YYABORT;
	}
	Plan_expr_func* func = new Plan_expr_func(root, spec);
	root->saveNode(func);
	delete[] $1;
	func->setArgs($3);
	$$ = func;
    }
    |
    T_ROWNUM
    {
	Plan_root* root = simpleParser.root();
	const Expr_func& spec = Expr_func::find("ROWNUM");
	ctx_assert(spec.m_name != 0);
	Plan_expr_func* func = new Plan_expr_func(root, spec);
	root->saveNode(func);
	func->setArgs(0);
	$$ = func;
    }
    |
    T_SYSDATE
    {
	Plan_root* root = simpleParser.root();
	const Expr_func& spec = Expr_func::find("SYSDATE");
	ctx_assert(spec.m_name != 0);
	Plan_expr_func* func = new Plan_expr_func(root, spec);
	root->saveNode(func);
	func->setArgs(0);
	$$ = func;
    }
    |
    expr_column
    {
	$$ = $1;
    }
    |
    T_STRING
    {
	Plan_root* root = simpleParser.root();
	LexType lexType(LexType::Char);
	Plan_expr_const* expr = new Plan_expr_const(root, lexType, $1);
	root->saveNode(expr);
	delete[] $1;
	$$ = expr;
    }
    |
    T_LINTEGER
    {
	Plan_root* root = simpleParser.root();
	LexType lexType(LexType::Integer);
	Plan_expr_const* expr = new Plan_expr_const(root, lexType, $1);
	root->saveNode(expr);
	delete[] $1;
	$$ = expr;
    }
    |
    T_LDECIMAL
    {
	Plan_root* root = simpleParser.root();
	LexType lexType(LexType::Float);
	Plan_expr_const* expr = new Plan_expr_const(root, lexType, $1);
	root->saveNode(expr);
	delete[] $1;
	$$ = expr;
    }
    |
    T_LREAL
    {
	Plan_root* root = simpleParser.root();
	LexType lexType(LexType::Float);
	Plan_expr_const* expr = new Plan_expr_const(root, lexType, $1);
	root->saveNode(expr);
	delete[] $1;
	$$ = expr;
    }
    |
    T_NULL
    {
	Plan_root* root = simpleParser.root();
	LexType lexType(LexType::Null);
	Plan_expr_const* expr = new Plan_expr_const(root, lexType, "");
	root->saveNode(expr);
	$$ = expr;
    }
    |
    T_QUES
    {
	Plan_root* root = simpleParser.root();
	unsigned paramNumber = simpleParser.paramNumber();
	ctx_assert(paramNumber != 0);
	Plan_expr_param* expr = new Plan_expr_param(root, paramNumber);
	root->saveNode(expr);
	$$ = expr;
    }
    ;
expr_column:
    T_IDENTIFIER
    {
	Plan_root* root = simpleParser.root();
	Plan_expr_column* column = new Plan_expr_column(root, $1);
	root->saveNode(column);
	delete[] $1;
	$$ = column;
    }
    |
    T_IDENTIFIER T_PERIOD T_IDENTIFIER
    {
	Plan_root* root = simpleParser.root();
	Plan_expr_column* column = new Plan_expr_column(root, $3);
	root->saveNode(column);
	delete[] $3;
	column->setCname($1);
	$$ = column;
    }
    |
    T_IDENTIFIER T_PERIOD T_IDENTIFIER T_PERIOD T_IDENTIFIER
    {
	Plan_root* root = simpleParser.root();
	BaseString str;
	str.append($1);
	str.append(".");
	str.append($3);
	delete[] $1;
	delete[] $3;
	Plan_expr_column* column = new Plan_expr_column(root, $5);
	root->saveNode(column);
	delete[] $5;
	column->setCname(str);
	$$ = column;
    }
    ;
table_list:
    table
    {
	Plan_root* root = simpleParser.root();
	Plan_table_list* tableList = new Plan_table_list(root);
	root->saveNode(tableList);
	tableList->addTable($1);
	$$ = tableList;
    }
    |
    table_list T_COMMA table
    {
	Plan_table_list* tableList = $1;
	tableList->addTable($3);
	$$ = tableList;
    }
    ;
table:
    dot_identifier
    {
	Plan_root* root = simpleParser.root();
	Plan_table* table = new Plan_table(root, $1);
	root->saveNode(table);
	delete[] $1;
	$$ = table;
    }
    |
    dot_identifier T_IDENTIFIER
    {
	Plan_root* root = simpleParser.root();
	Plan_table* table = new Plan_table(root, $1);
	root->saveNode(table);
	delete[] $1;
	table->setCname($2);
	delete[] $2;
	$$ = table;
    }
    ;
dot_identifier:
    T_IDENTIFIER
    {
	$$ = $1;
    }
    |
    T_IDENTIFIER T_PERIOD T_IDENTIFIER
    {
	char* s = new char[strlen($1) + 1 + strlen($3) + 1];
	strcpy(s, $1);
	strcat(s, ".");
	strcat(s, $3);
	delete[] $1;
	delete[] $3;
	$$ = s;
    }
    ;

%%

static int
yylex(YYSTYPE* lvalp, void* simpleParserPtr)
{
    int ret = simpleParser.yylex();
    *lvalp = simpleParser.yylval();
    return ret;
}

/* vim: set filetype=yacc: */