options {
	STATIC = false;
	ERROR_REPORTING = true;
	JAVA_UNICODE_ESCAPE = true;
	UNICODE_INPUT = false;
	IGNORE_CASE = true;
	DEBUG_PARSER = false;
	DEBUG_LOOKAHEAD = false;
    DEBUG_TOKEN_MANAGER = false;
}

PARSER_BEGIN(SQLParser)

package org.teiid.query.parser;

import java.math.BigInteger;
import java.util.*;
import org.teiid.core.types.*;
import org.teiid.core.util.StringUtil;
import org.teiid.query.QueryPlugin;
import org.teiid.query.sql.LanguageObject;
import org.teiid.query.sql.proc.*;
import org.teiid.query.sql.symbol.*;
import org.teiid.query.metadata.*;
import org.teiid.metadata.*;
import org.teiid.metadata.BaseColumn.NullType;
import org.teiid.language.Like.MatchMode;
import org.teiid.translator.*;
import org.teiid.query.sql.lang.*;

/** 
 * <p>The SQLParser is a JavaCC-generated parser that reads a SQL string and produces a 
 * Query object.  The SQLParser.java file is generated by JavaCC from the SQLParser.jj
 * file.  <B>WARNING:</B> DO NOT MODIFY the SQLParser.java file as it will be 
 * regenerated from the .jj file and your changes will be lost!</p>
 */
@SuppressWarnings({"nls", "unused"})
public class SQLParser extends SQLParserUtil {

  private void jj_add_error_token ( int kind, int pos) {
	    if (pos >= 100) return;
	    if (pos == jj_endpos + 1) {
	      jj_lasttokens[jj_endpos++] = kind;
	    } else if (jj_endpos != 0) {
	      jj_expentry = new int[jj_endpos];
	      for (int i = 0; i < jj_endpos; i++) {
	        jj_expentry[i] = jj_lasttokens[i];
	      }
	      boolean exists = false;
	      for (java.util.Iterator e = jj_expentries.iterator(); e.hasNext();) {
	        int[] oldentry = (int[])(e.next());
	        if (oldentry.length == jj_expentry.length) {
	          exists = true;
	          for (int i = 0; i < jj_expentry.length; i++) {
	            if (oldentry[i] != jj_expentry[i]) {
	              exists = false;
	              break;
	            }
	          }
	          if (exists) break;
	        }
	      }
	      if (!exists) jj_expentries.add(jj_expentry);
	      if (pos != 0) jj_lasttokens[(jj_endpos = pos) - 1] = kind;
	    }
	  }

} // end class

PARSER_END(SQLParser)

TOKEN_MGR_DECLS : 
{
    int commentNestingDepth; 
} 

SKIP :
{
	" " | "\t" | "\n" | "\r"
}

MORE :
{
  "/*" { commentNestingDepth = 1 ; } : IN_MULTI_LINE_COMMENT
}

<IN_MULTI_LINE_COMMENT>
MORE : 
{  
  "/*" { commentNestingDepth += 1 ; } 
} 

<IN_MULTI_LINE_COMMENT>
SPECIAL_TOKEN : 
{  
  "*/" { 
    commentNestingDepth -= 1;
    SwitchTo( commentNestingDepth==0 ? DEFAULT : IN_MULTI_LINE_COMMENT ) ; 
  }
} 

<IN_MULTI_LINE_COMMENT>
MORE :
{
  < ~[] >
}

SPECIAL_TOKEN:
{
   <LINE_COMMENT: "--"(~["\r","\n"])*>
}

TOKEN: /* Data types */
{
	<STRING: "string">
|   <VARBINARY: "varbinary">
|   <VARCHAR: "varchar">
|	<BOOLEAN: "boolean">
|	<BYTE: "byte">
|   <TINYINT: "tinyint">
|	<SHORT: "short">
|   <SMALLINT: "smallint">
|	<CHAR: "char">
|	<INTEGER: "integer">
|	<LONG: "long">
|   <BIGINT: "bigint">
|	<BIGINTEGER: "biginteger">
|	<FLOAT: "float">
|   <REAL: "real"> 
|	<DOUBLE: "double">
|	<BIGDECIMAL: "bigdecimal">
|   <DECIMAL: "decimal">
|	<DATE: "date">
|	<TIME: "time">
|	<TIMESTAMP: "timestamp">
|	<OBJECT: "object">
|	<BLOB: "blob">
|	<CLOB: "clob">
|	<XML: "xml">
}


TOKEN: /* Functions with special syntax */
{
	<CAST: "cast">
|	<CONVERT: "convert">
}

TOKEN : /* Reserved words */
{
    <ADD: "add">
|   <ALL: "all">
|   <ALTER: "alter">
|   <AND: "and">
|   <ANY: "any">
|   <ARRAY: "array">
|   <ARRAY_AGG: "array_agg">
|   <AS: "as">
|   <ASC: "asc">
|   <ATOMIC: "atomic">
|   <AUTORIZATION: "authorization">
|   <BEGIN: "begin">
|   <BETWEEN: "between">
|   <BINARY: "binary">
|   <BOTH: "both">
|   <BREAK: "break">
|   <BY: "by">
|   <CALL: "call">
|   <CALLED: "called">
|   <CASCADED: "cascaded">
|   <CASE: "case">
|   <CHARACTER: "character">
|   <CHECK: "check">
|   <CLOSE: "close">
|   <COLLATE: "collate">
|   <COLUMN: "column">
|   <COMMIT: "commit">
|   <CONNECT: "connect">
|   <CONSTRAINT: "constraint">
|   <CONTINUE: "continue">
|   <CORRESPONDING: "corresponding">
|   <CURRENT_DATE: "current_date">
|   <CURRENT_TIME: "current_time">
|   <CURRENT_TIMESTAMP: "current_timestamp">
|   <CURRENT_USER: "current_user">
|   <CREATE: "create">
|   <CRITERIA: "criteria">
|   <CROSS: "cross">
|   <CURSOR: "cursor">
|   <DAY: "day">
|   <DEALLOCATE: "deallocate">
|   <DECLARE: "declare">
|   <DELETE: "delete">
|   <DESC: "desc">
|   <DESCRIBE: "describe">
|   <DETERMINISTIC: "deterministic">
|   <DISCONNECT: "disconnect">
|   <DISTINCT: "distinct">
|   <DROP: "drop">
|	<EACH: "each">
|   <ELSE: "else">
|   <END: "end">
|   <ERROR: "error">
|   <ESCAPE: "escape">
|   <EXCEPT: "except">
|   <EXEC: "exec">
|   <EXECUTE: "execute">
|   <EXTERNAL: "external">
|   <EXISTS: "exists">
|   <FALSE: "false">
|   <FETCH: "fetch">
|   <FILTER: "filter">
|   <FOR: "for">
|   <FOREIGN: "foreign">
|   <FROM: "from">
|   <FULL: "full">
|   <FUNCTION: "function">
|   <GET: "get">
|   <GLOBAL: "global">
|   <GRANT: "grant">
|   <GROUP: "group">
|   <HAS: "has">
|   <HAVING: "having">
|   <HOLD: "hold">
|   <HOUR: "hour">
|   <IF: "if">
|   <IDENTITY: "identity">
|   <IMMEDIATE: "immediate">
|   <IN: "in">
|   <INDICATOR: "indicator">
|   <INNER: "inner">
|   <INPUT: "input">
|   <INOUT: "inout">
|   <INSENSITIVE: "insensitive">
|   <INSERT: "insert">
|   <INTERSECT: "intersect">
|   <INTERVAL: "interval">
|   <INTO: "into">
|   <IS: "is">
|   <ISOLATION: "isolation">
|   <JOIN: "join">
|   <LEFT: "left">
|   <LANGUAGE: "language">
|   <LARGE: "large">
|   <LEADING: "leading">
|   <LEAVE: "leave">
|   <LIKE: "like">
|   <LIKE_REGEX: "like_regex">
|   <LIMIT: "limit">
|   <LOCAL: "local">
|   <LOOP: "loop">
|   <MAKEDEP: "makedep">
|   <MAKEIND: "makeind">
|   <MAKENOTDEP: "makenotdep">
|   <MATCH: "match">
|   <MERGE: "merge">
|   <METHOD: "method">
|   <MINUTE: "minute">
|   <MODIFIES: "modifies">
|   <MODULE: "module">
|   <MONTH: "month">
|   <NATURAL: "natural">
|   <NEW: "new">
|	<NOCACHE: "nocache">
|   <NO: "no">
|   <NOT: "not">
|   <NULL: "null">
|   <OF: "of">
|   <OFFSET: "offset">
|   <OLD: "old">
|   <ON: "on">
|   <ONLY: "only">
|   <OPEN: "open">
|   <OPTION: "option">
|   <OPTIONS: "options">
|   <OR: "or">
|   <ORDER: "order">
|   <OUT: "out">
|   <OUTER: "outer">
|   <OUTPUT: "output">
|   <OVER: "over">
|   <OVERLAPS: "OVERLAPS">
|   <PARAMETER: "parameter">
|   <PARTITION: "partition">
|   <PRECISION: "precision">
|   <PREPARE: "prepare">
|   <PRIMARY: "primary">
|   <PROCEDURE: "procedure">
|   <RANGE: "range">
|   <READS: "reads">
|   <RECURSIVE: "recursive">
|   <REFERENCES: "REFERENCES">
|   <REFERENCING: "REFERENCING">
|   <RETURN: "return">
|   <RETURNS: "returns">
|   <REVOKE: "REVOKE">
|   <RIGHT: "right">
|   <ROLLBACK: "ROLLBACK">
|   <ROLLUP: "ROLLUP">
|   <ROW: "row">
|   <ROWS: "rows">
|   <SAVEPOINT: "savepoint">
|   <SCROLL: "scroll">
|   <SEARCH: "search">
|   <SECOND: "second">
|   <SELECT: "select">
|   <SENSITIVE: "sensitive">
|   <SESSION_USER: "session_user">
|   <SET: "set">
|   <SIMILAR: "similar">
|   <SPECIFIC: "specific">
|   <SOME: "some">
|   <SQL: "sql">
|   <SQLEXCEPTION: "sqlexception">
|   <SQLSTATE: "sqlstate">
|   <SQLWARNING: "sqlwarning">
|   <START: "start">
|   <STATIC: "static">
|   <SYSTEM: "system">
|   <SYSTEM_USER: "system_user">
|   <TABLE: "table">
|   <TEMPORARY: "temporary">
|   <THEN: "then">
|   <TIMEZONE_HOUR: "timezone_hour">
|   <TIMEZONE_MINUTE: "timezone_minute">
|   <TO: "to">
|   <TRAILING: "trailing">
|   <TRANSLATE: "translate">
|   <TRIGGER: "trigger">
|   <TRUE: "true">
|   <UNION: "union">
|   <UNIQUE: "unique">
|   <UNKNOWN: "unknown">
|   <USER: "user">
|   <UPDATE: "update">
|   <USING: "using">
|   <VALUE: "value">
|   <VALUES: "values">
|   <VIRTUAL: "virtual">
|   <WHEN: "when">
|   <WHENEVER: "whenever">
|   <WHERE: "where">
|   <WITH: "with">
|   <WHILE: "while">
|   <WINDOW: "window">
|   <WITHIN: "within">
|   <WITHOUT: "without">
|   <YEAR: "year">

|   <ALLOCATE: "allocate">
|   <ARE: "are">
|   <ASENSITIVE: "asensitive">
|   <ASYMETRIC: "asymetric">
|   <CYCLE: "cycle">
|   <DEC: "dec">
|   <DEREF: "deref">
|   <DYNAMIC: "dynamic">
|   <ELEMENT: "element">
|   <FREE: "free">
|   <INT: "int">
|   <LATERAL: "lateral">
|   <LOCALTIME: "localtime">
|   <LOCALTIMESTAMP: "localtimestamp">
|   <MEMBER: "member">
|   <MULTISET: "multiset">
|   <NATIONAL: "national">
|   <NCHAR: "nchar">
|   <NCLOB: "nclob">
|   <NUMERIC: "numeric">
|   <RELEASE: "release">
|   <SPECIFICTYPE: "specifictype">
|   <SYMETRIC: "symetric">
|   <SUBMULTILIST: "submultilist">
|   <TRANSLATION: "translation">
|   <TREAT: "treat">
|   <VARYING: "varying">
}

TOKEN : /* SQL/XML Reserved words */
{
    <XMLAGG: "xmlagg">
|   <XMLATTRIBUTES: "xmlattributes">
|   <XMLBINARY: "xmlbinary">
|   <XMLCAST: "xmlcast">
|   <XMLCONCAT: "xmlconcat">
|   <XMLCOMMENT: "xmlcomment">
|   <XMLDOCUMENT: "xmldocument">
|   <XMLELEMENT: "xmlelement">
|   <XMLEXISTS: "xmlexists">
|   <XMLFOREST: "xmlforest">
|   <XMLITERATE: "xmliterate">
|   <XMLNAMESPACES: "xmlnamespaces">
|   <XMLPARSE: "xmlparse">
|   <XMLPI: "xmlpi">
|   <XMLQUERY: "xmlquery">
|   <XMLSERIALIZE: "xmlserialize">
|   <XMLTABLE: "xmltable">
|   <XMLTEXT: "xmltext">
|   <XMLVALIDATE: "xmlvalidate">
}

TOKEN : /* SQL/MED Reserved words */
{
    <DATALINK: "datalink">
|   <DLNEWCOPY: "dlnewcopy">
|   <DLPREVIOUSCOPY: "dlpreviouscopy">
|   <DLURLCOMPLETE: "dlurlcomplete">
|   <DLURLCOMPLETEWRITE: "dlurlcompletewrite">
|   <DLURLCOMPLETEONLY: "dlurlcompleteonly">
|   <DLURLPATH: "dlurlpath">
|   <DLURLPATHWRITE: "dlurlpathwrite">
|   <DLURLPATHONLY: "dlurlpathonly">
|   <DLURLSCHEME: "dlurlscheme">
|   <DLURLSERVER: "dlurlserver">
|   <DLVALUE: "dlvalue">
|   <IMPORT: "import">
}

TOKEN : /* NonReserved words */
{
    <INSTEAD: "instead">
|   <ENABLED: "enabled">
|   <DISABLED: "disabled">
|   <VIEW: "view">
|   <KEY: "key">
|   <SERIAL: "serial">
|   <TEXTAGG: "textagg">
|   <COUNT: "count">
|   <ROW_NUMBER: "row_number">
|   <RANK: "rank">
|   <DENSE_RANK: "dense_rank">
|   <SUM: "sum">
|   <AVG: "avg">
|   <MIN: "min">
|   <MAX: "max">
|   <EVERY: "every">
|   <STDDEV_POP: "stddev_pop">
|   <STDDEV_SAMP: "stddev_samp">
|   <VAR_SAMP: "var_samp">
|   <VAR_POP: "var_pop">
|   <DOCUMENT: "document">
|   <CONTENT: "content">
|   <TRIM: "trim">
|   <EMPTY: "empty">
|   <ORDINALITY: "ordinality">
|   <PATH: "path">
|   <FIRST: "first">
|   <LAST: "last">
|   <NEXT: "next">
|   <SUBSTRING: "substring">
|   <EXTRACT: "extract">
|   <TO_CHARS: "to_chars">
|   <TO_BYTES: "to_bytes">
|   <TIMESTAMPADD: "timestampadd">
|   <TIMESTAMPDIFF: "timestampdiff">
|   <QUERYSTRING: "querystring">
|   <NAMESPACE: "namespace">
|   <RESULT: "result">
|   <INDEX: "index">
|   <ACCESSPATTERN: "accesspattern">
|   <AUTO_INCREMENT: "auto_increment">
|   <WELLFORMED: "wellformed">
|   <SQL_TSI_FRAC_SECOND: "sql_tsi_frac_second">
|   <SQL_TSI_SECOND: "sql_tsi_second">
|   <SQL_TSI_MINUTE: "sql_tsi_minute">
|   <SQL_TSI_HOUR: "sql_tsi_hour">
|   <SQL_TSI_DAY: "sql_tsi_day">
|   <SQL_TSI_WEEK: "sql_tsi_week">
|   <SQL_TSI_MONTH: "sql_tsi_month">
|   <SQL_TSI_QUARTER: "sql_tsi_quarter">
|   <SQL_TSI_YEAR: "sql_tsi_year">
|   <TEXTTABLE: "texttable">
|   <ARRAYTABLE: "arraytable">
|   <SELECTOR: "selector">
|   <SKIP_KEYWORD: "skip">
|   <WIDTH: "width">
|   <PASSING: "passing">
|   <NAME: "name">
|   <ENCODING: "encoding">
|   <COLUMNS: "columns">
|   <DELIMITER: "delimiter">
|   <QUOTE: "quote">
|   <HEADER: "header">
|   <NULLS: "nulls">
|   <OBJECTTABLE: "objecttable">
|   <VERSION: "version">
|   <INCLUDING: "including">
|   <EXCLUDING: "excluding">
|   <XMLDECLARATION: "xmldeclaration">
|   <VARIADIC: "variadic">
|   <EXCEPTION: "exception">
|   <RAISE: "raise">
|   <CHAIN: "chain">
|   <JSONARRAY_AGG: "jsonarray_agg">
|   <JSONOBJECT: "jsonobject">
|   <PRESERVE: "preserve">
|   <GEOMETRY: "geometry">
|   <UPSERT: "upsert">
|   <AFTER: "after">
|   <DEFAULT_KEYWORD: "default">
|   <AUTHENTICATED: "authenticated">
|   <TYPE: "type">
|   <TRANSLATOR: "translator">
|   <JAAS: "jaas">
|   <CONDITION: "condition">
|   <MASK: "mask">
|   <ACCESS: "access">
|   <CONTROL: "control">
|   <NONE: "none">
|   <DATA: "data">
|   <DATABASE: "database">
|   <PRIVILEGES: "privileges">
|   <SCHEMA: "schema">
|   <ROLE: "role">
|   <USE: "use">
|   <SERVER: "server">
|   <WRAPPER: "wrapper">
|   <REPOSITORY: "repository">
|   <RENAME: "rename">
|   <DOMAIN: "domain">
|   <USAGE: "usage">
}
/*
name=all in group identifier
*/
TOKEN : { < ALL_IN_GROUP: <ID> <PERIOD> <STAR> > }
/*
name=identifier
*/
TOKEN : { < ID: <QUOTED_ID> (<PERIOD> <QUOTED_ID>)* > }
TOKEN : { < #QUOTED_ID: <ID_PART> | ("\"" (("\"\"") | ~["\""] )+ "\"") >  }     
TOKEN : { < #ID_PART: (("_" | "@" | "#" | <LETTER>) (<LETTER> | "_" | <DIGIT>)*) > }
/*
name=escaped function
*/
TOKEN : { < ESCAPEDFUNCTION: "{" "fn" > }
/*
name=escaped join
*/
TOKEN : { < ESCAPEDJOIN: "{" "oj" > }
/*
name=escaped type
*/
TOKEN : { < ESCAPEDTYPE: "{" ("d" | "t" | "ts" | "b") > }
/*
name=decimal numeric literal
*/
TOKEN : { < DECIMALVAL: (<DIGIT>)* <PERIOD> <UNSIGNEDINTEGER> > }
/*
name=approximate numeric literal
*/
TOKEN : { < FLOATVAL: <DIGIT> <PERIOD> <UNSIGNEDINTEGER> 
				["e", "E"] (<PLUS>|<MINUS>)? <UNSIGNEDINTEGER> > }
/*
name=string literal
*/				
TOKEN : { < STRINGVAL: (("N"|"E")? "'" ( ("''") | ~["'"] )* "'") > }
TOKEN : { < #LETTER: (["a"-"z","A"-"Z"] | ["\u0153"-"\ufffd"]) > }
TOKEN : { < #DIGIT: ["0"-"9"] > }
/*
name=unsigned integer literal
*/
TOKEN : { < UNSIGNEDINTEGER: (<DIGIT>)+ > }
/*
name=binary string literal
*/
TOKEN : { < BINARYSTRINGVAL: ("X"|"x" "'" ( <HEXIT><HEXIT> )+ "'") > }
TOKEN : { < #HEXIT: (["a"-"f","A"-"F"] | <DIGIT> ) > }

TOKEN :
{
	<COMMA: ",">
|	<PERIOD: ".">
|	<LPAREN: "(">
|	<RPAREN: ")">
|   <LBRACE: "{">
|   <RBRACE: "}">
|   <LSBRACE: "[">
|   <RSBRACE: "]">
|	<EQ: "=">
|	<NE: "<>">
|	<NE2: "!=">
|	<LT: "<">
|	<LE: "<=">
|	<GT: ">">
|	<GE: ">=">
|	<STAR: "*">
|	<SLASH: "/">
|	<PLUS: "+">
|	<MINUS: "-">
|   <QMARK: "?">
|   <DOLLAR: "$">
|   <SEMICOLON: ";">
|   <COLON: ":">
|	<CONCAT_OP: "||">
|   <DOUBLE_AMP_OP: "&&">
}

/*
name=string
description=A string literal value.  Use '' to escape ' in the string.  
example=[source,sql]\n----\n'a string'\n----\n\n[source,sql]\n----\n'it''s a string'\n----\n
*/
String stringVal() :
{
	Token t = null;	
}
{
  	t = <STRINGVAL>
  	{
  		return normalizeStringLiteral(t.image);
  	}
}

/*
name=non-reserved identifier
description=Allows non-reserved keywords to be parsed as identifiers  
example=SELECT *COUNT* FROM ...
*/
Token nonReserved() :
{
}
{
    (<INSTEAD>|<VIEW>|<ENABLED>|<DISABLED>|<KEY>|<SERIAL>|<TEXTAGG>|<COUNT>|<ROW_NUMBER>|<RANK>|<DENSE_RANK>|<SUM>|<AVG>|<MIN>|<MAX>|<EVERY>|<STDDEV_POP>
     |<STDDEV_SAMP>|<VAR_SAMP>|<VAR_POP>|<DOCUMENT>|<CONTENT>|<TRIM>|<EMPTY>|<ORDINALITY>|<PATH>|<FIRST>|<LAST>|<NEXT>|<SUBSTRING>|<EXTRACT>|<TO_CHARS>
     |<TO_BYTES>|<TIMESTAMPADD>|<TIMESTAMPDIFF>|<QUERYSTRING>|<NAMESPACE>|<RESULT>|<INDEX>|<ACCESSPATTERN>|<AUTO_INCREMENT>|<WELLFORMED>|<SQL_TSI_FRAC_SECOND>
     |<SQL_TSI_SECOND>|<SQL_TSI_MINUTE>|<SQL_TSI_HOUR>|<SQL_TSI_DAY>|<SQL_TSI_WEEK>|<SQL_TSI_MONTH>|<SQL_TSI_QUARTER>|<SQL_TSI_YEAR>|<TEXTTABLE>|<ARRAYTABLE>
     |<SELECTOR>|<SKIP_KEYWORD>|<WIDTH>|<PASSING>|<NAME>|<ENCODING>|<COLUMNS>|<DELIMITER>|<QUOTE>|<HEADER>|<NULLS>|<OBJECTTABLE>
     |<VERSION>|<INCLUDING>|<EXCLUDING>|<XMLDECLARATION>|<VARIADIC>|<RAISE>|<EXCEPTION>|<CHAIN>|<JSONARRAY_AGG>|<JSONOBJECT>|<PRESERVE>|<UPSERT>|<AFTER>
     |<TYPE>|<TRANSLATOR>|<JAAS>|<CONDITION>|<MASK>|<ACCESS>|<CONTROL>|<NONE>|<DATA>|<DATABASE>|<PRIVILEGES>|<ROLE>|<SCHEMA>|<USE>|<REPOSITORY>|<RENAME>
     |<DOMAIN>|<USAGE>|<GEOMETRY>|<DEFAULT_KEYWORD>)
    {
    	return getToken(0);
    }	
}

/*
name=identifier
description=Partial or full name of a single entity.
example=[source,sql]\n----\ntbl.col\n----\n\n[source,sql]\n----\n"tbl"."col"\n----\n
*/
String id(Boolean nonAlias) :
{
}
{
  	(<ID>|nonReserved())
  	{
  		String id = normalizeId(getToken(0).image);
  		if (nonAlias != null) {
  			return validateName(id, nonAlias);
  		}
  		return id;
  	}
}

/* TODO: createProcedure should not be here, but is due to all of the legacy tests
unused=true
*/
Command command(ParseInfo info) :
{
	Command command = null;
}
{
	 (LOOKAHEAD(2) command = createProcedure(info) |
	  command = userCommand(info) |
	  command = callableStatement(info)
	)
	[<SEMICOLON>]
	<EOF>
	{
		return command;
	}
}

/*
unused=true
*/
Command designerCommand(ParseInfo info) :
{
	Command command = null;
}
{
	(LOOKAHEAD (2) command = createProcedure(info) |
	 command = forEachRowTriggerAction(info) |
	 command = userCommand(info)	  
	)
	[<SEMICOLON>]
	<EOF>
	{
		return command;
	}
}

/*
name=create trigger
description=Creates a trigger action on the given target.
example=[source,sql]\n----\nCREATE TRIGGER ON vw INSTEAD OF INSERT AS FOR EACH ROW BEGIN ATOMIC ... END\n----\n
*/
Command createTrigger(ParseInfo info) :
{
  	String target = null;
  	TriggerAction triggerAction = null;
  	Token event = null;
  	boolean after = false;
  	String name = null;
}
{
	<CREATE> <TRIGGER> [name = id(Boolean.TRUE)] <ON> target = id(null) 
	((<INSTEAD> <OF>)|(<AFTER> {after = true;}))
	(event = <INSERT> | event = <UPDATE> | event = <DELETE>) 
	<AS> triggerAction = forEachRowTriggerAction(info)
	{
		AlterTrigger alterTrigger = new AlterTrigger();
		alterTrigger.setTarget(new GroupSymbol(target));
		alterTrigger.setDefinition(triggerAction);
		alterTrigger.setEvent(Table.TriggerEvent.valueOf(event.image.toUpperCase()));
		alterTrigger.setCreate(true);
		alterTrigger.setAfter(after);
		alterTrigger.setName(name);
		return alterTrigger;				
	}
}

/*
name=alter
description=Alter the given target.
example=[source,sql]\n----\nALTER VIEW vw AS SELECT col FROM tbl\n----\n
*/
Command alter(ParseInfo info) :
{
  	String target = null;
  	QueryCommand command = null;
  	Statement stmt = null;
  	TriggerAction triggerAction = null;
  	Token comment = null;
  	Token event = null;
  	Boolean enabled = null;
  	boolean after = false;
  	String name = null;
}
{
	<ALTER>
	(
	 	(<VIEW> target = id(null) <AS> { comment = getToken(1).specialToken; } command = queryExpression(info)) 
	 	{
	 	    if (comment != null) {
		 	    command.setCacheHint(getQueryCacheOption(comment.image));
	 	    }
	 		AlterView alterView = new AlterView();
	 		alterView.setTarget(new GroupSymbol(target));
	 		alterView.setDefinition(command);
	 		return alterView;	
	 	}
		| (<PROCEDURE> target = id(null) <AS> { comment = getToken(1).specialToken; } stmt = statement(info)) 
		{
			CreateProcedureCommand cup = new CreateProcedureCommand(asBlock(stmt));
			if (comment != null) {
		 	    cup.setCacheHint(getQueryCacheOption(comment.image));
	 	    }
			AlterProcedure alterProc = new AlterProcedure();
			alterProc.setTarget(new GroupSymbol(target));
			alterProc.setDefinition(cup);
			return alterProc;
		}
		| (<TRIGGER> [name = id(Boolean.TRUE)] <ON> target = id(null) 
		  ((<INSTEAD> <OF>)|(<AFTER> {after = true;}))
		  (event = <INSERT> | event = <UPDATE> | event = <DELETE>) 
		  ((<AS> triggerAction = forEachRowTriggerAction(info))
		  |<ENABLED> {enabled=true;}| <DISABLED> {enabled=false;}))
		{
			AlterTrigger alterTrigger = new AlterTrigger();
			alterTrigger.setTarget(new GroupSymbol(target));
			alterTrigger.setDefinition(triggerAction);
			alterTrigger.setEvent(Table.TriggerEvent.valueOf(event.image.toUpperCase()));
			if (enabled != null) {
				alterTrigger.setEnabled(enabled);
			}
			alterTrigger.setName(name);
			alterTrigger.setAfter(after);
			return alterTrigger;				
		}
	)
}

/*
name=for each row trigger action
description=Defines an action to perform on each row.
example=[source,sql]\n----\nFOR EACH ROW BEGIN ATOMIC ... END\n----\n
*/
TriggerAction forEachRowTriggerAction(ParseInfo info) :
{
	Block b = new Block();
	b.setAtomic(true);
	Statement stmt = null;
}
{
	<FOR> <EACH> <ROW>
	(
	 LOOKAHEAD(1) (<BEGIN> [<ATOMIC>]
      (
	    stmt = statement(info)
	    {          
    	    b.addStatement(stmt);            	                	    
	    }
      )*
      <END> 
     )
     |
     stmt = statement(info)
     {
       b = asBlock(stmt);
       b.setAtomic(true);
     }
    ) 
	{
		return new TriggerAction(b);
	}
}

/*
name=directly executable statement
description=A statement that can be executed at runtime.
example=[source,sql]\n----\nSELECT * FROM tbl\n----\n
index=true
*/
Command userCommand(ParseInfo info) :
{
	Command command = null;
	SourceHint sourceHint = null;
	Block block = null;
}
{
	(command = queryExpression(info) |
	command = storedProcedure(info, 1) |
	{ sourceHint = getSourceHint(this); } command = insert(info) |
	{ sourceHint = getSourceHint(this); } command = update(info) |
	{ sourceHint = getSourceHint(this); } command = delete(info) |
	command = dropTable(info) |
	LOOKAHEAD(2) command = createTempTable(info) |
	LOOKAHEAD(2) command = createForeignTempTable(info) |
	command = alter(info) |
	command = createTrigger(info) |
	block = compoundStatement(info) {command = new CreateProcedureCommand(block);}
	)
	{
		setSourceHint(sourceHint, command);
		return command;
	}
}

/*
name=drop table
description=Drop the given table.
example=[source,sql]\n----\nDROP TABLE #temp\n----\n
*/
Command dropTable(ParseInfo info) :
{
	Drop drop = new Drop();
	String table = null;
}
{
	<DROP> <TABLE> 
	table = id(null)
	{
		drop.setTable(new GroupSymbol(table));
		return drop;
	}
}
	
/*
name=create temporary table
description=Creates a temporary table.
example=[source,sql]\n----\nCREATE LOCAL TEMPORARY TABLE tmp (col integer)\n----\n
*/
Command createTempTable(ParseInfo info) :
{
	Create create = new Create();
	String table = null;
	String pkId = null;
	Column col = null;
	List<ElementSymbol> pk = null;
}
{
	<CREATE> [<LOCAL>] <TEMPORARY> <TABLE> 
	table = id(null)
	<LPAREN>
	{
		create.setTable(new GroupSymbol(table));
	}
    col = tableElement(info)
    {
      create.getColumns().add(col);
    }
    (LOOKAHEAD(2) <COMMA>
      col = tableElement(info)
      {
        create.getColumns().add(col);
      }
    )*	
	[<COMMA> <PRIMARY> <KEY> 
	  pk = columnList(true, true)
	  {
	  	create.getPrimaryKey().addAll(pk);
	  }	  
	]
	<RPAREN>
	[<ON> <COMMIT> 
	     <PRESERVE> {create.setCommitAction(Create.CommitAction.PRESERVE_ROWS);} <ROWS>]
	{
		return create;
	}
}

/*
name=temporary table element
description=Defines a temporary table column.
example=[source,sql]\n----\ncol string NOT NULL\n----\n
*/
Column tableElement(ParseInfo info) :
{
	String element = null;
	ParsedDataType type = null;
	boolean autoIncrement = false;
	//TODO: once we support udts, then this will need to inherit the nullability from the type
	boolean notNull = false;
}
{
	 element = id(Boolean.TRUE)
	 (
	  type = parseBasicDataType()
	  |
	  <SERIAL>
	  {
	    type = new ParsedDataType("INTEGER");
	    autoIncrement = true;
	    notNull = true;
	  }
	 )
	 [<NOT> <NULL> { notNull = true; }]
	 {
	    Column c = new Column();
	    c.setName(element);
	    c.setRuntimeType(type.type);
	    c.setAutoIncremented(autoIncrement);
	    c.setNullType(notNull?NullType.No_Nulls:NullType.Nullable);
	    return c;
	 }
}

/*
name=raise error statement
description=Raises an error with the given message.
example=[source,sql]\n----\nERROR 'something went wrong'\n----\n
*/
RaiseStatement errorStatement(ParseInfo info) :
{
    Expression errMsg = null;           
}
{ 
    <ERROR>
    errMsg = expression(info)           
        
    {
        ExceptionExpression ee = new ExceptionExpression();
        ee.setMessage(errMsg);
        return new RaiseStatement(ee);       
    }
}

/*
name=raise statement
description=Raises an error or warning with the given message.
example=[source,sql]\n----\nRAISE SQLEXCEPTION 'something went wrong'\n----\n
*/
RaiseStatement raiseStatement(ParseInfo info) :
{
    Expression err = null;
    boolean warning = false;
}
{ 
    <RAISE>
    [<SQLWARNING> {warning = true;}]
    err = exceptionReference(info)
    {
        return new RaiseStatement(err, warning);       
    }
}

/*
name=exception reference
description=a reference to an exception
example=[source,sql]\n----\nSQLEXCEPTION 'something went wrong' SQLSTATE '00X', 2\n----\n
*/
Expression exceptionReference(ParseInfo info) :
{
	String id = null;
	Expression ex;
}
{
	id = id(null) {return new ElementSymbol(id);}
	|
	ex = exception(info)
	{
		return ex;
	}
}

/*
name=sql exception
description=creates a sql exception or warning with the specified message, state, and code
example=[source,sql]\n----\nSQLEXCEPTION 'something went wrong' SQLSTATE '00X', 2\n----\n
*/
Expression exception(ParseInfo info) :
{
	Expression err = null;
	Expression sqlState = null;
	Expression errCode = null;
	Expression parent = null;
}
{
    <SQLEXCEPTION>
	err = commonValueExpression(info)
	[<SQLSTATE> sqlState = commonValueExpression(info)
     [<COMMA> errCode = commonValueExpression(info)]
    ]
    [<CHAIN> parent = exceptionReference(info)]
	{
		ExceptionExpression ee = new ExceptionExpression();
		ee.setMessage(err);
		ee.setSqlState(sqlState);
		ee.setErrorCode(errCode);
		ee.setParent(parent);
		return ee;
	}	
}

/*
name=statement
description=A procedure statement.
example=[source,sql]\n----\nIF (x = 5) BEGIN ... END\n----\n
*/
Statement statement(ParseInfo info) :
{
    Statement stmt = null;
    String label = null;
}
{
    (
	 LOOKAHEAD(2) ([label = id(null) <COLON>] 
	  (
	   stmt = loopStatement(info) |
	   stmt = whileStatement(info) |
	   stmt = compoundStatement(info)
	  )
	  {
	  	((Statement.Labeled)stmt).setLabel(label);
	  }
	 )
	 |
	 (
      stmt = ifStatement(info) |
      stmt = delimitedStatement(info)
	 )
	)
    
    {
        return stmt;
    }
}

/*
name=delimited statement
description=A procedure statement terminated by ;.
example=[source,sql]\n----\nSELECT * FROM tbl;\n----\n
*/
Statement delimitedStatement(ParseInfo info) :
{
    Statement stmt = null;
}           
{
     (LOOKAHEAD(2) stmt = assignStatement(info) |
	  stmt = sqlStatement(info) |
      stmt = errorStatement(info) |
      stmt = raiseStatement(info) |
	  stmt = declareStatement(info) |
	  stmt = branchingStatement(info) |
	  stmt = returnStatement(info)
	 )
	 <SEMICOLON>
	 {
	     return stmt;
	 }
}

/*
name=compound statement
description=A procedure statement block contained in BEGIN END.
example=[source,sql]\n----\nBEGIN NOT ATOMIC ... END\n----\n
*/
Block compoundStatement(ParseInfo info) :
{
    Statement stmt = null;  
    Block block = new Block();
    Boolean atomic = null;
    String eId = null;
}
{   
    <BEGIN> [[<NOT> {atomic = Boolean.FALSE;}] <ATOMIC> {if (atomic == null) {atomic = Boolean.TRUE;}}]
    (LOOKAHEAD(2)
      stmt = statement(info)
      {          
	    block.addStatement(stmt, false);            	                	    
	  }
 	)*
 	[<EXCEPTION> eId = id(Boolean.FALSE) {block.setExceptionGroup(eId);}
 	 (
       stmt = statement(info)
       {          
	     block.addStatement(stmt, true);            	                	    
	   }
 	 )*
 	]
    <END>
    {
        if (atomic != null) {
        	block.setAtomic(atomic);
        }
        return block;
    }
}        

/*
name=branching statement
description=A procedure branching control statement, which typically specifies a label to return control to.
example=[source,sql]\n----\nBREAK x\n----\n
*/
BranchingStatement branchingStatement(ParseInfo info) :
{
    BranchingStatement breakStmt = new BranchingStatement();
    Token mode = null;
    String label = null;
}
{
 	(
 	 (
 	  (mode = <BREAK> | mode = <CONTINUE>) [label = id(null)] 
 	 )
 	 | 
 	 (mode = <LEAVE> label = id(null))
 	)
 	{
 	    breakStmt.setMode(BranchingStatement.BranchingMode.valueOf(mode.image.toUpperCase()));
 	    breakStmt.setLabel(label);
 		return breakStmt;
 	}
} 	

/*
name=return statement
description=A return statement.
example=[source,sql]\n----\nRETURN 1\n----\n
*/
ReturnStatement returnStatement(ParseInfo info) :
{
    Expression ex = null;
}
{
 	<RETURN> [ex = expression(info)]
 	{
 	    return new ReturnStatement(ex);
 	}
} 	

/*
name=while statement
description=A procedure while statement that executes until its condition is false.
example=[source,sql]\n----\nWHILE (var) BEGIN ... END\n----\n
*/
WhileStatement whileStatement(ParseInfo info) :
{
    WhileStatement whileStmt = null;    
    Criteria criteria = null;
    Statement stmt = null;
}
{
    <WHILE>
    <LPAREN>  
    criteria = criteria(info)
    <RPAREN>
    stmt = statement(info)
    {
        whileStmt = new WhileStatement(criteria, asBlock(stmt));   
        return whileStmt;
    }    
}    

/*
name=loop statement
description=A procedure loop statement that executes over the given cursor.
example=[source,sql]\n----\nLOOP ON (SELECT * FROM tbl) AS x BEGIN ... END\n----\n
*/
LoopStatement loopStatement(ParseInfo info) :
{
    LoopStatement loopStmt = null;    
    String cursor = null;
    QueryCommand query = null;
    Statement stmt = null;
}
{
    <LOOP>
    <ON>
    <LPAREN>  
    query = queryExpression(info)
    <RPAREN>
    <AS>
    cursor = id(null)
    stmt = statement(info)
    {
        loopStmt = new LoopStatement(asBlock(stmt), query, cursor);   
        return loopStmt;
    }    
}   

/*
name=if statement
description=A procedure loop statement that executes over the given cursor.
example=[source,sql]\n----\nIF (boolVal) BEGIN variables.x = 1 END ELSE BEGIN variables.x = 2 END\n----\n
*/
IfStatement ifStatement(ParseInfo info) :
{
    IfStatement ifStmt = null;    
    Criteria criteria = null;
    Statement ifStatement = null;
    Statement elseStatement = null;
    String groupID = null;
    String groupID2 = null;
    boolean not = false;
}
{
    <IF>
    <LPAREN>
    (LOOKAHEAD(4) (groupID = id(null) <IS> [<NOT> {not = true;}] <DISTINCT> <FROM> groupID2 = id(null)
      {
        IsDistinctCriteria idc = new IsDistinctCriteria();
        idc.setLeftRowValue(new GroupSymbol(groupID));
        idc.setRightRowValue(new GroupSymbol(groupID2));
        idc.setNegated(not);
        criteria = idc;
      }
     )
     | criteria = criteria(info)
    )
    <RPAREN>
    ifStatement = statement(info)
    //else blocks will be associated with the closest if block
    [LOOKAHEAD(1)<ELSE> elseStatement = statement(info)]
    {
        ifStmt = new IfStatement(criteria, asBlock(ifStatement));
        ifStmt.setElseBlock(asBlock(elseStatement));
        return ifStmt;
    }    
}    
            
/*
name=declare statement
description=A procedure declaration statement that creates a variable and optionally assigns a value.
example=[source,sql]\n----\nDECLARE STRING x = 'a'\n----\n
*/
DeclareStatement declareStatement(ParseInfo info) :
{
    DeclareStatement declStmt = null;
    String var = null;    
    ParsedDataType type = null;  
    ElementSymbol variableID = null;
    LanguageObject value = null;
}
{
    <DECLARE>
    (type = parseDataType() | <EXCEPTION> {type = new ParsedDataType(getToken(0).image);})
    var = id(null)
    {
        variableID = new ElementSymbol(var);
    }
    [<EQ>
     value = assignStatementOperand(info)
    ]
    
    {
        if (value instanceof Expression) {
    		return new DeclareStatement(variableID, type.type, (Expression)value);
    	}
    	if (value instanceof QueryCommand) {
    		return new DeclareStatement(variableID, type.type, new ScalarSubquery((QueryCommand)value));
    	}
    	return new DeclareStatement(variableID, type.type, (Command)value);
    }           
}

/*
name=assignment statement
description=Assigns a variable a value in a procedure.
example=[source,sql]\n----\nx = 'b'\n----\n
*/
Statement assignStatement(ParseInfo info) :
{
    LanguageObject value = null;
    String var = null;
    ElementSymbol elementID = null;
    boolean returnable=true;
}
{
    var = id(null)
    {
        elementID = new ElementSymbol(var);              
    } 
    <EQ>
    (value = assignStatementOperand(info) |
     (value = storedProcedure(info, 2) [(<WITH>|<WITHOUT> {returnable = false;}) <RETURN>]) {
		StoredProcedure storedProcedure = (StoredProcedure)value;
	    SPParameter parameter = new SPParameter(1, SPParameter.RETURN_VALUE, "return"); //$NON-NLS-1$
	    parameter.setExpression(elementID);
		storedProcedure.setParameter(parameter);  
		storedProcedure.setCalledWithReturn(true);
		CommandStatement cs = new CommandStatement(storedProcedure);
		cs.setReturnable(returnable);
		return cs;      	    
     }
    )
        
    {   
    	if (value instanceof Expression) {
    		return new AssignmentStatement(elementID, (Expression)value);
    	}
    	if (value instanceof QueryCommand) {
    		return new AssignmentStatement(elementID, (QueryCommand)value);
    	}
    	return new AssignmentStatement(elementID, (Command)value);
    }    
}

/*
name=assignment statement operand
description=A value or command that can be used in an assignment.  {note}All assigments except for expression are deprecated.{note}
*/
LanguageObject assignStatementOperand(ParseInfo info) :
{
    LanguageObject value = null;
}
{
    (
	  LOOKAHEAD(<INSERT><INTO>) (value = insert(info)) | //deprecated
      value = update(info) | //deprecated
      value = delete(info) | //deprecated
      LOOKAHEAD(1) value = expression(info) |      
      value = queryExpression(info) | //deprecated should be a scalar subquery
      value = exception(info)
    ) 
    {   
        return value;
    }    
}

/*
name=data statement
description=A procedure statement that executes a SQL statement.  An update statement can have its update count accessed via the ROWCOUNT variable. 
*/
CommandStatement sqlStatement(ParseInfo info) :
{
    Command cmd = null;
    String var = null;
    ElementSymbol elementID = null;
    StoredProcedure storedProcedure = null;
    boolean returnable = true;
}
{
    (LOOKAHEAD(2) cmd = userCommand(info) |
     cmd = dynamicCommand(info)
    ) [(<WITH>|<WITHOUT> {returnable = false;}) <RETURN>]
  
    {
    	CommandStatement cs = new CommandStatement(cmd);
    	cs.setReturnable(returnable);
    	return cs;
    }   
}
 
/*
unused=true
*/
CreateProcedureCommand  createProcedure(ParseInfo info) :
{
    CreateProcedureCommand procCmd = 
        new CreateProcedureCommand();
    Statement stmt = null;  
}
{ 
    <CREATE> [<VIRTUAL>] <PROCEDURE>
    stmt = statement(info)
    {        
        procCmd.setBlock(asBlock(stmt));
        return procCmd;        
    }    
}   

/*
name=procedure body definition
description=Defines a procedure body on a Procedure metadata object.
example=[source,sql]\n----\nBEGIN ... END\n----\n
index=true
*/
CreateProcedureCommand  procedureBodyCommand(ParseInfo info) :
{
    CreateProcedureCommand procCmd = 
        new CreateProcedureCommand();
    Statement stmt = null;  
}
{ 
    [LOOKAHEAD(2) <CREATE> [<VIRTUAL>] <PROCEDURE>]
    stmt = statement(info)
    {        
        procCmd.setBlock(asBlock(stmt));
        return procCmd;        
    }    
} 

/*
name=dynamic data statement
description=A procedure statement that can execute arbitrary sql.
example=[source,sql]\n----\nEXECUTE IMMEDIATE 'SELECT * FROM tbl' AS x STRING INTO #temp\n----\n
*/
DynamicCommand dynamicCommand(ParseInfo info) :
{
    Expression sql = null;           
    String groupID = null;
	GroupSymbol group = null;
	int updateCount = 0;
	List<TableFunctionReference.ProjectedColumn> elements = null;
	SetClauseList using = null;
	DynamicCommand dcStmt = new DynamicCommand();
	SetClauseList setClauseList = null;
}
{ 
    (<EXECUTE>|<EXEC>) [(<STRING>|<IMMEDIATE>)]
    sql = expression(info)
    {
        dcStmt.setSql(sql);
    }
    [<AS> 
	elements = createElementsWithTypes(info)

	 [<INTO>
      groupID = id(null)
      {
         group = new GroupSymbol(groupID);
      }
     ]
     {
         dcStmt.setIntoGroup(group);
         List<ElementSymbol> symbols = new ArrayList(elements.size());
         for (TableFunctionReference.ProjectedColumn col : elements) {
         	symbols.add(col.getSymbol());
         }
         dcStmt.setAsColumns(symbols);
         dcStmt.setAsClauseSet(true);
     }
	]
    [<USING>
     using = setClauseList(true, info)
	 {
	     dcStmt.setUsing(using);
	 }
    ]
    [<UPDATE>
     (updateCount = intVal()
     |
     <STAR> 
     {	
        updateCount = 2;
     })
    ]
    {
        dcStmt.setUpdatingModelCount(updateCount);
        return dcStmt; 
    }
}

/*
name=set clause list
description=A list of value assignments.
example=[source,sql]\n----\ncol1 = 'x', col2 = 'y' ...\n----\n
*/
SetClauseList setClauseList(boolean shortName, ParseInfo info) :
{
	SetClauseList using = new SetClauseList();
	String element = null;
	Expression value = null;
}
{
	element = id(shortName?Boolean.TRUE:null)
    <EQ>
    value = expression(info)
    {
        ElementSymbol symbol = new ElementSymbol(element);
        using.addClause(symbol, value);
    }
	(<COMMA>
	 element = id(null)
     <EQ>
     value = expression(info)
     {
     	  symbol = new ElementSymbol(element);
          using.addClause(symbol, value);
     }
	)*
	{
		return using;
	}
}

/*
name=typed element list
description=A list of typed elements.
example=[source,sql]\n----\ncol1 string, col2 integer ...\n----\n
*/
List<TableFunctionReference.ProjectedColumn> createElementsWithTypes(ParseInfo info) :
{
	String element = null;
	ParsedDataType type = null;
	List<TableFunctionReference.ProjectedColumn> elements = new ArrayList<TableFunctionReference.ProjectedColumn>();
}
{
	 element = id(Boolean.TRUE)
	 type = parseBasicDataType()
	 {
	    TableFunctionReference.ProjectedColumn symbol = new TableFunctionReference.ProjectedColumn(element, type.type);
        elements.add(symbol);
	 }
	 (LOOKAHEAD(2) <COMMA>
		element = id(Boolean.TRUE)
		type = parseBasicDataType()
		{
	      symbol = new TableFunctionReference.ProjectedColumn(element, type.type);
		  elements.add(symbol);
		}
	 )*
	 {
	 	return elements;
	 }
}

/*
name=callable statement
description=A callable statement defined using JDBC escape syntax.
example=[source,sql]\n----\n{? = CALL proc}\n----\n
index=true
*/
StoredProcedure callableStatement(ParseInfo info) :
{
	StoredProcedure storedProcedure = new StoredProcedure();
	storedProcedure.setCallableStatement(true);
	String procName = null;
	Option option = null;
	SPParameter parameter = null;
	int parameterIndex = 1;
	List<Expression> values = null;
}
{
	<LBRACE>
	[<QMARK> <EQ>
	 {
	   parameter = new SPParameter(parameterIndex++, SPParameter.RETURN_VALUE, "return"); //$NON-NLS-1$
	   storedProcedure.setParameter(parameter);
	   storedProcedure.setCalledWithReturn(true);
	 }
	]
	<CALL> procName = id(null)
	{
		storedProcedure.setProcedureName(procName);
	}
		
	//parameters
		
	[<LPAREN>
	  (LOOKAHEAD(2)
        executeNamedParams(info, storedProcedure, parameterIndex)
        |      
        [values = expressionList(info) { convertToParameters(values, storedProcedure, parameterIndex); }]
      )
	 <RPAREN>
	]
	<RBRACE>			
	[option = option(info)
	 {
	   storedProcedure.setOption(option);
	 }
	]

	{
		return storedProcedure;
	}
}

/*
name=call statement
description=Executes the procedure with the given parameters.
example=[source,sql]\n----\nCALL proc('a', 1)\n----\n
*/
StoredProcedure storedProcedure(ParseInfo info, int startIndex) :
{
	String procName = null;
	Option option = null;
	List<Expression> values = null;
	StoredProcedure storedProcedure = new StoredProcedure();
	SourceHint sourceHint = null;
}
{
	{ sourceHint = getSourceHint(this); }
	(
		(<EXEC> | <EXECUTE> | <CALL>)
		procName = id(null)
		{
			storedProcedure.setProcedureName(procName);
		}
		
		//parameters
		<LPAREN>
		
		(
		 LOOKAHEAD(2)
		 executeNamedParams(info, storedProcedure, startIndex)
		 | 		
		 [values = expressionList(info) { convertToParameters(values, storedProcedure, startIndex); }]
		)
		
		<RPAREN>
	)
	[option = option(info)
	 {
	   storedProcedure.setOption(option);
	 }
	]
	{
		storedProcedure.setSourceHint(sourceHint);
		return storedProcedure;
	}
}

/*
name=named parameter list
description=A list of named parameters.
example=[source,sql]\n----\nparam1 => 'x', param2 => 1\n----\n
*/
void executeNamedParams(ParseInfo info, StoredProcedure storedProcedure, int parameterIndex) :
{
	String name = null;
    Expression value = null;
	SPParameter parameter = null;
}
{
	{
		storedProcedure.setDisplayNamedParameters(true);
	}
	(name=id(null)
	 <EQ> [<GT>]
	 value = expression(info)
		{
			parameter = new SPParameter(parameterIndex++, value);
			parameter.setName(name);
			parameter.setParameterType(SPParameter.IN);
			storedProcedure.setParameter(parameter);
			parameter = null;
		}
		(	<COMMA>
		 	name=id(null)
	 		<EQ> [<GT>]
	 		value = expression(info)
			{
				parameter = new SPParameter(parameterIndex++, value);
				parameter.setName(name);
				parameter.setParameterType(SPParameter.IN);
				storedProcedure.setParameter(parameter);
				parameter = null;
			}
		)*
	)
}

/*
name=insert statement
description=Inserts values into the given target.
example=[source,sql]\n----\nINSERT INTO tbl (col1, col2) VALUES ('a', 1)\n----\n
*/
Insert insert(ParseInfo info) :
{
	Insert insert = new Insert();
	boolean upsert = false;
	String group = null;
	List values = null;
	List<ElementSymbol> columns = null;
	Option option = null;
	QueryCommand query = null;
}
{
	(<INSERT>|<MERGE>{upsert=true;}|<UPSERT>{upsert=true;}) <INTO>
	group = id(null)

	[LOOKAHEAD(<LPAREN> id(null)) columns = columnList(false, true) {
	  insert.setVariables(columns);
	}]

    query = queryExpression(info)
	{
	  insert.setQueryExpression(query);
    }
	
	[ option = option(info)
	  {
	    insert.setOption(option);
	  }
	]
	
	{
		// Store group
		insert.setGroup(new GroupSymbol(group) );
		insert.setUpsert(upsert);
		return insert;
	}
}

/*
name=expression list
description=A list of expressions.
example=[source,sql]\n----\ncol1, 'a', ...\n----\n
*/
ArrayList<Expression> expressionList(ParseInfo info) :
{
	ArrayList<Expression> rowVals = new ArrayList<Expression>(4);
	Expression value = null;
}
{
	value = expression(info)
	{
        rowVals.add(value);
	}
	(	<COMMA>
	    value = expression(info)
	    {
            rowVals.add(value);
	    }
	)*
	{
		return rowVals;
	}
}

/*
name=update statement
description=Update values in the given target.
example=[source,sql]\n----\nUPDATE tbl SET (col1 = 'a') WHERE col2 = 1\n----\n
*/
Update update(ParseInfo info) :
{
	Update update = new Update();
	String group = null;
	SetClauseList setClauseList = null;
	Criteria criteria = null;
	Option option = null;
}
{
	<UPDATE>
	group = id(null)
	<SET>
	setClauseList = setClauseList(false, info)
	{
		update.setChangeList(setClauseList);
	}
	[ criteria = where(info) ]
	[option = option(info)
	  {
	   update.setOption(option);
	  }
	]
	{	
		// Store group
		update.setGroup(new GroupSymbol( group) );
		
		// Store optional criteria
		if(criteria != null) {
			update.setCriteria(criteria);
		}
		
		return update;
	}
}

/*
name=delete statement
description=Delete rows from the given target.
example=[source,sql]\n----\nDELETE FROM tbl WHERE col2 = 1\n----\n
*/
Delete delete(ParseInfo info) :
{
	String group = null;
	Criteria criteria = null;
	Delete delete = new Delete();
	Option option = null;
}
{
	<DELETE> <FROM>
	group = id(null)
	[criteria = where(info)]
	[option = option(info)
	  {
	   delete.setOption(option);
	  }
	]
	{
		delete.setGroup(new GroupSymbol(group));
		delete.setCriteria(criteria);
		        
        return delete; 
	}
}

/*
name=query expression
description=A declarative query for data.
example=[source,sql]\n----\nSELECT * FROM tbl WHERE col2 = 1\n----\n
*/
QueryCommand queryExpression(ParseInfo info) :
{
	QueryCommand query = null;
	List<WithQueryCommand> withList = null;
	WithQueryCommand withQueryCommand = null;
	SourceHint sourceHint = null;
}
{
	[<WITH> withQueryCommand = withListElement(info)
	  {
	    withList = new LinkedList<WithQueryCommand>();
	    withList.add(withQueryCommand);
	  }
	  (	<COMMA>
	    withQueryCommand = withListElement(info)
	    {
            withList.add(withQueryCommand);
	    }
	  )*
	]
	{ sourceHint = getSourceHint(this); }
	query = queryExpressionBody(info)
	{
		query.setWith(withList);
		setSourceHint(sourceHint, query);
		return query;
	}
}

/*
name=with list element
description=A query expression for use in the enclosing query. 
example=[source,sql]\n----\nX (Y, Z) AS (SELECT 1, 2)\n----\n
*/
WithQueryCommand withListElement(ParseInfo info) :
{
   String name = null;
   List<ElementSymbol> columns = null;
   QueryCommand queryExpression = null;
   Token lparen = null;
}
{
	name = id(Boolean.FALSE)
	[ columns = columnList(true, true)] 
	<AS> lparen = <LPAREN> queryExpression = queryExpression(info) <RPAREN>
	{
	    WithQueryCommand result = new WithQueryCommand(new GroupSymbol(name), columns, queryExpression);
	    parseWithHints(lparen, result);
	    return result;
	}
}

/*
name=query expression body
description=The body of a query expression, which can optionally be ordered and limited.
example=[source,sql]\n----\nSELECT * FROM tbl ORDER BY col1 LIMIT 1\n----\n
*/
QueryCommand queryExpressionBody(ParseInfo info) :
{
    QueryCommand query = null;
    QueryCommand rightQuery = null;
    boolean all = false;
    Option option = null;
	OrderBy orderby = null;
	Limit limit = null;
	SetQuery.Operation type;
}
{
	query = queryTerm(info) 
	(	(LOOKAHEAD( <UNION>, { getToken(2).kind != JOIN } ) <UNION> { type = SetQuery.Operation.UNION; } | <EXCEPT> { type = SetQuery.Operation.EXCEPT; }) 
 	 	[<ALL> { all = true; } | <DISTINCT>]
 	 	rightQuery = queryTerm(info)
 	   	{
 	   		query = addQueryToSetOperation(query, rightQuery, type, all);
    		all = false;
	   	}
	) *
    [orderby  = orderby(info) {query.setOrderBy( orderby );}]
    [limit    = limit(info) {query.setLimit( limit );}]
    [LOOKAHEAD(<OPTION>) option = option(info) { query.setOption(option);  }	]
    {
		return query;
    }    
}

/*
name=query term
description=Used to establish INTERSECT precedence.
example=[source,sql]\n----\nSELECT * FROM tbl\n----\n\n[source,sql]\n----\nSELECT * FROM tbl1 INTERSECT SELECT * FROM tbl2\n----\n
*/
QueryCommand queryTerm(ParseInfo info) : 
{
    QueryCommand query = null;
    QueryCommand rightQuery = null;
    boolean all = false;
}
{
	query=queryPrimary(info) 
	(	<INTERSECT> 
 		[<ALL> { all = true; } | <DISTINCT>] 
 		rightQuery=queryPrimary(info)
 		{
 	   		query = addQueryToSetOperation(query, rightQuery, SetQuery.Operation.INTERSECT, all);
    		all = false;
 		}
	) *
    {
        return query;
    }
}    

/*
name=query primary
description=A declarative source of rows.
example=[source,sql]\n----\nTABLE tbl\n----\n\n[source,sql]\n----\nSELECT * FROM tbl1\n----\n
*/
QueryCommand queryPrimary(ParseInfo info) : 
{
    QueryCommand query = null;
    String name = null;
    List<Expression> expr = null;
}
{
    (
        query=query(info) |
        (<VALUES> <LPAREN> expr = expressionList(info) 
         {
           query = Query.rowConstructor(expr);
         } 
         <RPAREN> 
          (<COMMA> <LPAREN> expr = expressionList(info) 
           { 
             query = new SetQuery(SetQuery.Operation.UNION, true, query, Query.rowConstructor(expr));
           } 
          <RPAREN>)*) | 
        (<TABLE> name=id(null) {
           Query q = new Query();
           q.setSelect(new Select(Arrays.asList(new MultipleElementSymbol())));
           q.setFrom(new From(Arrays.asList(new UnaryFromClause(new GroupSymbol(name)))));
           query = q;
        }) |
        (<LPAREN> query=queryExpressionBody(info) <RPAREN>)
    )    
    {
        return query;
    }
}    

/*
name=query
description=A SELECT query.
example=[source,sql]\n----\nSELECT col1, max(col2) FROM tbl GROUP BY col1\n----\n
*/
Query query(ParseInfo info) :
{
	Select   select         = null;
	From     from           = null;
	Into 	 into			= null;
	Criteria criteria       = null;
	GroupBy  groupBy        = null;
	Criteria having         = null;	
}
{
    select	  = select(info)
    [into	  = into(info)]
   	[from 	  = from(info)
   		
    [criteria = where(info)]
    [groupBy  = groupBy(info)]
    [having   = having(info)]]

	{
		// Build query from parsed pieces
		Query query = new Query();
		query.setSelect( select );
		query.setFrom( from );
		query.setInto( into );
		query.setCriteria(criteria);
		query.setGroupBy(groupBy);
		query.setHaving(having);

		return query;
  	}
}

/*
name=into clause
description=Used to direct the query into a table.  {note}This is deprecated.  Use INSERT INTO with a query expression instead.{note}
example=[source,sql]\n----\nINTO tbl\n----\n
*/
Into into(ParseInfo info) :
{
	String groupID = null;
	Into into = null;
}
{	
	<INTO>
	(groupID=id(null))
	{
    	into = new Into(new GroupSymbol(groupID));
		return into;
    }	
}

/*
name=select clause
description=The columns returned by a query.  Can optionally be distinct.
example=[source,sql]\n----\nSELECT *\n----\n\n[source,sql]\n----\nSELECT DISTINCT a, b, c\n----\n
 */
Select select(ParseInfo info) :
{
	boolean isDistinct = false;		// unless DISTINCT keyword in SELECT
	Expression symbol = null;
	Select select = new Select();
}
{
	<SELECT>
	[<ALL> | (<DISTINCT> {isDistinct=true;})]
	(<STAR>
		{
			select.addSymbol(new MultipleElementSymbol());
		}
	|
		(	symbol=selectSymbol(info)      { select.addSymbol(symbol); }
		    (	<COMMA>
				symbol=selectSymbol(info)  { select.addSymbol(symbol); }
			)*		
		)
	)		
	{
 		select.setDistinct(isDistinct);
		return select;
	}
}

/*
name=select sublist
description=An element in the select clause
example=[source,sql]\n----\ntbl.*\n----\n\n[source,sql]\n----\ntbl.col AS x\n----\n
*/
Expression selectSymbol(ParseInfo info) :
{
	Expression symbol = null;	
}
{
	(
		symbol = selectExpression(info)
		|
		symbol = allInGroupSymbol()
	)
	{
		return symbol;
	}
}

/*
name=select derived column
description=A select clause item that selects a single column. {note}This is slightly different than a derived column in that the AS keyword is optional.{note}
example=[source,sql]\n----\ntbl.col AS x\n----\n
*/
Expression selectExpression(ParseInfo info) :
{
	Expression expression = null;
    String alias = null;
}
{
	(	
		// Expression
		expression=expression(info)	
		[[<AS>] alias=id(Boolean.FALSE) ]
	)
	{		
		if(alias != null) {
			return new AliasSymbol(alias, expression);
		}
		return expression;
	}
}

/*
name=derived column
description=An optionally named expression.
example=[source,sql]\n----\ntbl.col AS x\n----\n
*/
DerivedColumn derivedColumn(ParseInfo info) :
{
	Expression expression = null;
    String alias = null;
}
{
	(	
		// Expression
		expression=expression(info)	
		[<AS> alias=id(Boolean.FALSE) ]
	)
	{		
		return new DerivedColumn(alias, expression);
	}
}

/*
name=all in group 
description=A select sublist that can select all columns from the given group.
example=[source,sql]\n----\ntbl.*\n----\n
*/
MultipleElementSymbol allInGroupSymbol() :
{
	Token allInGroupToken = null;
	
}
{
   	allInGroupToken = <ALL_IN_GROUP>    
    {
		return new MultipleElementSymbol(normalizeId(allInGroupToken.image.substring(0, allInGroupToken.image.length() - 2)));
	}
}

/*
name=ordered aggregate function
description=An aggregate function that can optionally be ordered.
example=[source,sql]\n----\nXMLAGG(col1) ORDER BY col2\n----\n\n[source,sql]\n----\nARRAY_AGG(col1)\n----\n
*/
AggregateSymbol orderedAgg(ParseInfo info) :
{
    Token t = null;
	Expression expression = null;
	OrderBy orderBy = null;
}
{
	(t=<XMLAGG>|t=<ARRAY_AGG>|t=<JSONARRAY_AGG>)
	<LPAREN>
	expression = expression(info)
	[ orderBy = orderby(info) ]
	<RPAREN>
	{
		String aggName = t.image.toUpperCase();
		AggregateSymbol	agg = new AggregateSymbol(aggName, false, expression);
		agg.setOrderBy(orderBy);
		return agg;
	}
}

/*
name=text aggreate function
description=An aggregate function for creating separated value clobs.
example=[source,sql]\n----\nTEXTAGG (col1 as t1, col2 as t2 DELIMITER ',' HEADER)\n----\n
*/
AggregateSymbol textAgg(ParseInfo info) :
{
	DerivedColumn expression = null;
	Character delimiter = null;
	Character quote = null;
	boolean header = false;	
	List<DerivedColumn> expressions = new ArrayList<DerivedColumn>();
	OrderBy orderBy = null;
	String encoding = null;
}
{
	<TEXTAGG> <LPAREN>
	[<FOR>] expression = derivedColumn(info)
	{
		expressions.add(expression);
	}
	(<COMMA>
		expression = derivedColumn(info)
		{
			expressions.add(expression);
		}
	)* 		
	[
	  <DELIMITER>
	  delimiter = charVal(info, "DELMITER")
	]
	[
	  (<QUOTE>
	  quote = charVal(info, "QUOTE")) |
	  (<NO> <QUOTE> {quote = (char)0;})
	]
	[
	  <HEADER>
	  { header = true; }
	]	
	[
	  <ENCODING>
	  encoding = id(null) 
	]
	[
		orderBy = orderby(info)
	]	
	<RPAREN>
	{
		TextLine tf = new TextLine();
		tf.setDelimiter(delimiter);
		tf.setQuote(quote);
		tf.setIncludeHeader(header);
		tf.setExpressions(expressions);
		tf.setEncoding(encoding);
		AggregateSymbol agg = new AggregateSymbol("TEXTAGG", false, tf);
		agg.setOrderBy(orderBy);
		return agg;		
	}
}

/*
name=standard aggregate function
description=A standard aggregate function.
example=[source,sql]\n----\nCOUNT(*)\n----\n
*/
AggregateSymbol aggregateSymbol(ParseInfo info) :
{
	Token func = null;
	Token starToken = null;
	AggregateSymbol agg = null;
	boolean isDistinct = false;
	Expression expression = null;
}
{
	(
		// COUNT(*)
		LOOKAHEAD(3) (func = <COUNT>
			<LPAREN>
			starToken = <STAR>
			<RPAREN> )	
		| 
		// Remaining aggregates
		(	(<COUNT> | <SUM> | <AVG> | <MIN> | <MAX> | <EVERY> | <STDDEV_POP> | <STDDEV_SAMP> | <VAR_SAMP> | <VAR_POP> | <SOME> | <ANY>) {func = getToken(0);}
			<LPAREN>
			[ <DISTINCT> {isDistinct=true;} | <ALL>]
			expression = expression(info)
			<RPAREN>
		)
	)
	{
		String name = func.image.toUpperCase();
		if(starToken == null) { 
			// Aggregate
			agg = new AggregateSymbol(name, isDistinct, expression);
		} else {
			// COUNT(*)			
			agg = new AggregateSymbol(name, false, null);
		}
		return agg;
	}
}

/*
name=analytic aggregate function
description=An analytic aggregate function.
example=[source,sql]\n----\nROW_NUMBER()\n----\n
*/
AggregateSymbol analyticAggregateSymbol(ParseInfo info) :
{
	Token func = null;
}
{
	(<ROW_NUMBER> | <RANK> | <DENSE_RANK>) {func = getToken(0);}
	<LPAREN> <RPAREN>
	{
		return new AggregateSymbol(func.image.toUpperCase(), false, null);	
	}
}

/*
name=filter clause
description=An aggregate filter clause applied prior to accumulating the value.
example=[source,sql]\n----\nFILTER (WHERE col1='a')\n----\n
*/
Expression filterClause(ParseInfo info) :
{
	Expression condition = null;
}
{
	<FILTER> <LPAREN> <WHERE> condition = booleanPrimary(info) <RPAREN>
	{
		return condition;	
	}
}

/*
name=from clause
description=A query from clause containing a list of table references.
example=[source,sql]\n----\nFROM a, b\n----\n\n[source,sql]\n----\nFROM a right outer join b, c, d join e".</p>\n----\n
*/
From from(ParseInfo info) :
{
	FromClause clause = null;
	List clauseList = new LinkedList();
}
{
	<FROM>
	(	clause = tableReference(info)
		{
			clauseList.add(clause);
			clause = null;
		}
		
		(<COMMA> clause = tableReference(info)
			{
				clauseList.add(clause);
				clause = null;
			}
		)*
	) 

	{
		return new From(clauseList);	
	}
}					

/*
name=table reference
description=An optionally escaped joined table.
example=[source,sql]\n----\na\n----\n\n[source,sql]\n----\na inner join b\n----\n
*/
FromClause tableReference(ParseInfo info) :
{
	FromClause result = null;
}
{	
	(
		(	
			<ESCAPEDJOIN>
			result=joinedTable(info)
			<RBRACE>
		) 
		|
		result=joinedTable(info)
	)
	{
		return result;
	}
}	

/*
name=joined table
description=A table or join.
example=[source,sql]\n----\na\n----\n\n[source,sql]\n----\na inner join b\n----\n
*/
FromClause joinedTable(ParseInfo info) :
{
	FromClause result = null;
	JoinPredicate jp = null;
}
{
	result = tablePrimary(info) /* this is a fix for left recursion in the spec */
	( LOOKAHEAD(2)	
		(	jp = crossJoin(info)
			| jp = qualifiedJoin(info)
		)
		{
			jp.setLeftClause(result);
			result = jp;
		}
	) *
	{
		return result;
	}
}

/*
name=cross join
description=A cross join.
example=[source,sql]\n----\na CROSS JOIN b\n----\n
*/
JoinPredicate crossJoin(ParseInfo info) :
{
	JoinPredicate result = new JoinPredicate();
	FromClause clause = null;
}
{
	(	(<CROSS> {result.setJoinType(JoinType.JOIN_CROSS);}|<UNION> {result.setJoinType(JoinType.JOIN_UNION);})
    	<JOIN> 
		clause = tablePrimary(info) {result.setRightClause(clause);}
	)
	{
		return result;
	}
}

/*
name=qualified table
description=An INNER or OUTER join.
example=[source,sql]\n----\na inner join b\n----\n
*/
JoinPredicate qualifiedJoin(ParseInfo info) :
{
	JoinPredicate result = new JoinPredicate();
	FromClause clause = null;
	Criteria onCriteria = null;
}
{
	(	[	(<RIGHT> {result.setJoinType(JoinType.JOIN_RIGHT_OUTER);} [<OUTER>])
    		| (<LEFT> {result.setJoinType(JoinType.JOIN_LEFT_OUTER);} [<OUTER>])
    		|  (<FULL> {result.setJoinType(JoinType.JOIN_FULL_OUTER);} [<OUTER>])
    		| <INNER>
		]
		<JOIN>
		clause = tableReference(info) {result.setRightClause(clause);}
		<ON>
		onCriteria = criteria(info) {result.setJoinCriteria(Criteria.separateCriteriaByAnd(onCriteria));}
	)
	{
		return result;
	}
}

/*
name=table primary
description=A single source of rows.
example=[source,sql]\n----\na\n----\n
*/
FromClause tablePrimary(ParseInfo info) :
{
	FromClause clause = null;
	Token lparen = null;
	Option.MakeDep makedep = null;
}
{
	(	LOOKAHEAD(2) clause = textTable(info)
	    |
	    LOOKAHEAD(2) clause = arrayTable(info)
	    |
	    clause = xmlTable(info)
	    |
	    LOOKAHEAD(2) clause = objectTable(info)
	    |
	    clause = unaryFromClause(info)
		|
		LOOKAHEAD(subqueryFromClause(info)) clause = subqueryFromClause(info) 
	    |			
		(lparen = <LPAREN> clause = joinedTable(info) 
		 {
			setFromClauseOptions(lparen, clause);
		 }
		 <RPAREN> 
		)
	)
	[	(<MAKEDEP> makedep = makedepOptions() { clause.setMakeDep(makedep); })
	 	| (<MAKENOTDEP> {  clause.setMakeNotDep(true); } )
	]
	[ (<MAKEIND> makedep = makedepOptions() { clause.setMakeInd(makedep); }) ]
	{
		return clause;
	}
}

/*
name=make dep options
description=options for the make dep hint
example=[source,sql]\n----\n(min:10000)\n----\n
*/
Option.MakeDep makedepOptions() :
{
	Integer max = null;
	Boolean join = null;
	boolean no = false;
}
{
 [<LPAREN>
	[<MAX><COLON> max = intVal()]
	[[<NO> {no = true;}] <JOIN> {join = true;}]
  <RPAREN>]
 {
 	Option.MakeDep result = new Option.MakeDep();
 	result.setMax(max);
 	result.setJoin(no?Boolean.FALSE:join);
 	return result;
 }
}

/*
name=xml serialize
description=Serializes an XML value.
example=[source,sql]\n----\nXMLSERIALIZE(col1 AS CLOB)\n----\n
*/
XMLSerialize xmlSerialize(ParseInfo info) :
{
	Expression expr = null;
	Boolean doc = null;
	Token t = null;
	String enc = null;
	Boolean declr = null;
	String ver = null;
}
{
	<XMLSERIALIZE> <LPAREN>
	[LOOKAHEAD(1) (<DOCUMENT> {doc = true;}| <CONTENT> {doc = false;})]
	expr = expression(info)
	[
		<AS> (<STRING> | <VARCHAR> | <CLOB> | <VARBINARY> | <BLOB>) { t = getToken(0);}
	]
	[ <ENCODING> enc = id(null) ]
	[ <VERSION> ver = stringVal() ]
	[ ( <INCLUDING> {declr = true;}| <EXCLUDING> {declr = false;}) <XMLDECLARATION> ]
	<RPAREN>
	{
		XMLSerialize result = new XMLSerialize();
		result.setEncoding(enc);
		result.setVersion(ver);
		result.setDeclaration(declr);
		result.setExpression(expr);
		if (doc != null) {
			result.setDocument(doc);
		}
		if (t != null) {
			result.setTypeString(t.image);
		}
		return result;
	}
}

/*
name=array table
description=The ARRAYTABLE table function creates tabular results from arrays.  It can be used as a nested table reference.
example=[source,sql]\n----\nARRAYTABLE (col1 COLUMNS x STRING) AS y\n----\n
*/
ArrayTable arrayTable(ParseInfo info) :
{
	Expression array = null;
	List<TableFunctionReference.ProjectedColumn> columns;
	String aliasID = null;
}
{
 	<ARRAYTABLE> <LPAREN> array = valueExpressionPrimary(info)
 	<COLUMNS>
 	columns = createElementsWithTypes(info) 
 	<RPAREN>
 	[<AS>] aliasID=id(Boolean.FALSE)
 	{
 		ArrayTable result = new ArrayTable();
 		result.setArrayValue(array);
 		result.setColumns(columns);
 		result.setName(aliasID);
 		return result;
 	}
}

/*
name=text table
description=The TEXTTABLE table function creates tabular results from text.  It can be used as a nested table reference.
example=[source,sql]\n----\nTEXTTABLE (file COLUMNS x STRING) AS y\n----\n
*/
TextTable textTable(ParseInfo info) :
{
	Expression file = null;
	TextTable.TextColumn column = null;
	List<TextTable.TextColumn> columns = new ArrayList<TextTable.TextColumn>();
	boolean useRowDelimiter = true;
	Character delimiter = null;
	Character rowDelimiter = null;
	boolean escape = false;
	Character quote = null;
	Integer header = null;
	Integer skip = null;
	String selector = null;
	String aliasID = null;
	boolean noTrim = false;
}
{
 	<TEXTTABLE> <LPAREN> file = commonValueExpression(info)
 	[
	  <SELECTOR>
	  selector = stringVal()
	]
 	<COLUMNS>
 	column = textColumn(info)
	{
		columns.add(column);
	} 
	(<COMMA>
		column = textColumn(info)
		{
			columns.add(column);
		}
	)* 
	[
	  LOOKAHEAD(2) ((<NO> <ROW> <DELIMITER>
	  {
	    useRowDelimiter = false;
	  })
	  |
	  (<ROW> <DELIMITER>
	    rowDelimiter = charVal(info, " ROW DELMITER")
	  ))
	]
	[
	  <DELIMITER>
	  delimiter = charVal(info, "DELMITER")
	]
	[
	  (<ESCAPE> quote = charVal(info, "ESCAPE") ) { escape = true; }
	  |
	  (<QUOTE> quote = charVal(info, "QUOTE") )
	]
	[
	  <HEADER>
	  [
	  	header = intVal()
	  ]
	  {
	  	if (header == null) {
	  		header = 1;
	  	}
	  }
	]
	[
	  <SKIP_KEYWORD>
	  skip = intVal()
	]
	[ LOOKAHEAD(2)
      <NO> <TRIM>
      {
    	noTrim = true;	  	
      }
  	]
 	<RPAREN>
 	[<AS>] aliasID=id(Boolean.FALSE)
 	{
 		TextTable result = new TextTable();
 		result.setFile(file);
 		result.setColumns(columns);
 		result.setDelimiter(delimiter);
 		result.setEscape(escape);
 		result.setHeader(header);
 		result.setSkip(skip);
 		result.setName(aliasID);
 		result.setQuote(quote);
 		result.setUsingRowDelimiter(useRowDelimiter);
 		result.setSelector(selector);
 		if (noTrim) {
 			result.setNoTrim();
 		}
 		result.setRowDelimiter(rowDelimiter);
 		return result;
 	}
}

/*
name=text table column
description=A text table column.
example=[source,sql]\n----\nx INTEGER WIDTH 6\n----\n
*/
TextTable.TextColumn textColumn(ParseInfo info):
{
	String name = null;
	ParsedDataType datatype = null;
	Integer width = null;
	boolean noTrim = false;
	String selector = null;
	Integer position = null;
	String header = null;
}
{
	name = id(Boolean.TRUE)
	
	( (
      <FOR> <ORDINALITY> 
      {
        return new TextTable.TextColumn(name); 
      } 
    ) | (
        [
            <HEADER>
            header = stringVal()
        ]
    	datatype = parseBasicDataType()
    	[ 
    	  	<WIDTH>
    	  	width = intVal() 
    	  	[ LOOKAHEAD(2)
    		  	<NO> <TRIM>
    		  	{
    				noTrim = true;	  	
    		  	}
    		]
    	]
    	[ 
	  		<SELECTOR>
		    selector = stringVal()
		  	position = intVal() 
		]
		{
			TextTable.TextColumn result = new TextTable.TextColumn(name, datatype.type, width, noTrim);
			result.setSelector(selector);
			result.setPosition(position);
			result.setHeader(header);
			return result;
		}
    ) )
	
}

/*
name=xml query
description=Executes an XQuery to return an XML result.
example=[source,sql]\n----\nXMLQUERY('<a>...</a>' PASSING doc)\n----\n
*/
Expression xmlExists(ParseInfo info) :
{
    String xquery = null;
    XMLNamespaces xmlNamespaces = null;
    DerivedColumn passing = null;
    ArrayList<DerivedColumn> passingValues = new ArrayList<DerivedColumn>();
    Boolean content = null;
    Boolean empty = null;
}
{
    <XMLEXISTS> <LPAREN> 
    [ xmlNamespaces = xmlNamespaces(info) <COMMA> ]
    xquery = stringVal()
    [ 
      <PASSING>
      passing = derivedColumn(info)
      {
        passingValues.add(passing);
        passing.setPropagateName(false);
      }
      (<COMMA>
        passing = derivedColumn(info)
        {
          passingValues.add(passing);
          passing.setPropagateName(false);
        }
      )*
    ]
    <RPAREN>
    {
        XMLQuery result = new XMLQuery();
        result.setXquery(xquery);
        result.setNamespaces(xmlNamespaces);
        result.setPassing(passingValues);
        result.setEmptyOnEmpty(true);
        return new XMLExists(result);
    }
}

/*
name=xml query
description=Executes an XQuery to return an XML result.
example=[source,sql]\n----\nXMLQUERY('<a>...</a>' PASSING doc)\n----\n
*/
XMLQuery xmlQuery(ParseInfo info) :
{
	String xquery = null;
	XMLNamespaces xmlNamespaces = null;
	DerivedColumn passing = null;
	ArrayList<DerivedColumn> passingValues = new ArrayList<DerivedColumn>();
	Boolean content = null;
	Boolean empty = null;
}
{
 	<XMLQUERY> <LPAREN> 
 	[ xmlNamespaces = xmlNamespaces(info) <COMMA> ]
 	xquery = stringVal()
 	[ 
 	  <PASSING>
 	  passing = derivedColumn(info)
 	  {
 	  	passingValues.add(passing);
 	  	passing.setPropagateName(false);
 	  }
 	  (<COMMA>
 	    passing = derivedColumn(info)
 	    {
 	  	  passingValues.add(passing);
 	  	  passing.setPropagateName(false);
 	    }
 	  )*
 	]
/* 	[
 	  LOOKAHEAD(<ID>, { "returning".equalsIgnoreCase(getToken(1).image) }) <ID>
 	  (
 	  	LOOKAHEAD(<ID>, { "content".equalsIgnoreCase(getToken(1).image) }) <ID> { content = true; }
 	  	|
 	  	LOOKAHEAD(<ID>, { "sequence".equalsIgnoreCase(getToken(1).image) }) (<ID> { content = false; }
 	  )
 	]*/
 	[
 	  (<NULL> { empty = false; }
 	   |
 	   <EMPTY> { empty = true; }
 	  )
 	  <ON> <EMPTY>
 	]
 	<RPAREN>
 	{
 		XMLQuery result = new XMLQuery();
 		result.setXquery(xquery);
 		result.setNamespaces(xmlNamespaces);
 		result.setPassing(passingValues);
 		result.setEmptyOnEmpty(empty);
 		return result;
 	}
}

/*
name=object table
description=Returns table results by processing a script.
example=[source,sql]\n----\nOBJECTTABLE('z' PASSING val AS z COLUMNS col OBJECT 'teiid_row') AS X\n----\n
*/
ObjectTable objectTable(ParseInfo info) :
{
	String rowScript = null;
	String lang = null;
	DerivedColumn passing = null;
	ArrayList<DerivedColumn> passingValues = new ArrayList<DerivedColumn>();
	ObjectTable.ObjectColumn column = null;
	ArrayList<ObjectTable.ObjectColumn> columns = new ArrayList<ObjectTable.ObjectColumn>();
	String aliasID = null;
}
{
 	<OBJECTTABLE> <LPAREN> 
 	[<LANGUAGE> lang = stringVal()]
 	rowScript = stringVal()
 	[ 
 	  <PASSING>
 	  passing = derivedColumn(info)
 	  {
 	  	passingValues.add(passing);
 	  	passing.setPropagateName(false);
 	  }
 	  (<COMMA>
 	    passing = derivedColumn(info)
 	    {
 	  	  passingValues.add(passing);
 	  	  passing.setPropagateName(false);
 	    }
 	  )*
 	]
    <COLUMNS>
    column = objectColumn(info)
    {
      columns.add(column);
    }
    (<COMMA>
      column = objectColumn(info)
      {
        columns.add(column);
      }
    )*
 	<RPAREN>
 	[<AS>] aliasID=id(Boolean.FALSE)
 	{
 		ObjectTable result = new ObjectTable();
 		result.setScriptingLanguage(lang);
 		result.setRowScript(rowScript);
 		result.setPassing(passingValues);
 		result.setColumns(columns);
 		result.setName(aliasID);
 		return result;
 	}
}

/*
name=object table column
description=object table column.
example=[source,sql]\n----\ny integer 'teiid_row_number'\n----\n
*/
ObjectTable.ObjectColumn objectColumn(ParseInfo info):
{
	String name = null;
	ParsedDataType datatype = null;
	String path = null;
	Expression defaultExpr = null;
}
{
	name = id(null)
    datatype = parseBasicDataType()
	path = stringVal()    
    [
   	  <DEFAULT_KEYWORD> defaultExpr = expression(info)
	]
	{
	  return new ObjectTable.ObjectColumn(name, datatype.type, path, defaultExpr);
	}
}


/*
name=xml table
description=Returns table results by processing an XQuery.
example=[source,sql]\n----\nXMLTABLE('/a/b' PASSING doc COLUMNS col XML PATH '.') AS X\n----\n
*/
XMLTable xmlTable(ParseInfo info) :
{
	String xquery = null;
	XMLNamespaces xmlNamespaces = null;
	DerivedColumn passing = null;
	ArrayList<DerivedColumn> passingValues = new ArrayList<DerivedColumn>();
	XMLTable.XMLColumn column = null;
	ArrayList<XMLTable.XMLColumn> columns = new ArrayList<XMLTable.XMLColumn>();
	String aliasID = null;
}
{
 	<XMLTABLE> <LPAREN> 
 	[ xmlNamespaces = xmlNamespaces(info) <COMMA> ]
 	xquery = stringVal()
 	[ 
 	  <PASSING>
 	  passing = derivedColumn(info)
 	  {
 	  	passingValues.add(passing);
 	  	passing.setPropagateName(false);
 	  }
 	  (<COMMA>
 	    passing = derivedColumn(info)
 	    {
 	  	  passingValues.add(passing);
 	  	  passing.setPropagateName(false);
 	    }
 	  )*
 	]
 	[
 	  <COLUMNS>
 	  column = xmlColumn(info)
 	  {
 	    columns.add(column);
 	  }
 	  (<COMMA>
 	    column = xmlColumn(info)
 	    {
 	      columns.add(column); 	    
 	    }
 	  )*
 	]
 	<RPAREN>
 	[<AS>] aliasID=id(Boolean.FALSE)
 	{
 		XMLTable result = new XMLTable();
 		result.setXquery(xquery);
 		result.setNamespaces(xmlNamespaces);
 		result.setPassing(passingValues);
 		result.setColumns(columns);
 		result.setName(aliasID);
 		return result;
 	}
}

/*
name=xml table column
description=XML table column.
example=[source,sql]\n----\ny FOR ORDINALITY\n----\n
*/
XMLTable.XMLColumn xmlColumn(ParseInfo info):
{
	String name = null;
	ParsedDataType datatype = null;
	String path = null;
	Expression defaultExpr = null;
}
{
	name = id(Boolean.TRUE)
	
	((
	  <FOR> <ORDINALITY> 
	  {
	    return new XMLTable.XMLColumn(name); 
	  }	
	) | (
      datatype = parseBasicDataType()
      [
	    <DEFAULT_KEYWORD> defaultExpr = expression(info)
	  ]
      [ 
	    <PATH> path = stringVal()
	  ]
	  {
		return new XMLTable.XMLColumn(name, datatype.type, path, defaultExpr);
	  }
	))
}

/*
name=unsigned integer
description=An unsigned interger value.
example=[source,sql]\n----\n12345\n----\n
*/
int intVal() :
{
	Token t;
}
{
	t = <UNSIGNEDINTEGER>
	{
		try { 
			return Integer.valueOf(t.image); 				
		} catch(NumberFormatException e1) { 
			throw new ParseException(QueryPlugin.Util.getString("SQLParser.Integer_parse", t.image)); //$NON-NLS-1$
		}
	}
}

/*
name=table subquery
description=A table defined by a subquery.
example=[source,sql]\n----\n(SELECT * FROM tbl) AS x\n----\n
*/
SubqueryFromClause subqueryFromClause(ParseInfo info) :
{
    String aliasID = null;
	Command command = null;
	Token lparen = null;
	boolean table = false;
}
{	
	[
		(<TABLE>|<LATERAL>) { table = true; }
	]
	lparen = <LPAREN> 
	( command = queryExpression(info) |
      command = storedProcedure(info, 1) )
	<RPAREN>
	[<AS>]
	aliasID = id(Boolean.FALSE)
	
	{
		SubqueryFromClause clause = new SubqueryFromClause(aliasID, command);
        setFromClauseOptions(lparen, clause);
        clause.setLateral(table);
        return clause;
    }	
}	
		
/*
name=table name
description=A table named in the FROM clause.
example=[source,sql]\n----\ntbl AS x\n----\n
*/
UnaryFromClause unaryFromClause(ParseInfo info) :
{
	GroupSymbol group = null;
	String groupID = null;
	Token groupToken = null;
    String aliasID = null;
	UnaryFromClause clause = null;
}
{	
	(groupID=id(null) {groupToken = getToken(0);} [[<AS>] aliasID=id(Boolean.FALSE)])
	{
    	if(aliasID != null) {		
            group = new GroupSymbol(aliasID, groupID);
        } else {
			group = new GroupSymbol(groupID);
        }
        clause = new UnaryFromClause(group);
        setFromClauseOptions(groupToken, clause);
		return clause;
    }	
}		

/*
name=where clause
description=Specifies a search condition
example=[source,sql]\n----\nWHERE x = 'a'\n----\n
*/
Criteria where(ParseInfo info) :
{
    Criteria criteria = null;
}
{
    <WHERE>
	criteria = criteria(info)
	
	{
		return criteria;
	}
}

/*
name=condition
description=A boolean expression.
*/
Criteria criteria(ParseInfo info) :
{
	Criteria criteria = null;
}
{	
	criteria = compoundCritOr(info)

	{
		return criteria;
	}
}

/*
name=boolean value expression
description=An optionally ORed boolean expression.
*/
Criteria compoundCritOr(ParseInfo info) :
{
    ArrayList logicList = null;
    Criteria logicPart = null;
    Criteria logicPart1 = null;
}
{
	logicPart=compoundCritAnd(info) 
	(<OR> logicPart1=compoundCritAnd(info) 			{ if (logicList == null) { logicList = new ArrayList(2); logicList.add(logicPart); } logicList.add(logicPart1); } )*

	{
		if(logicList == null) {
			return logicPart;
		} else {
         	return new CompoundCriteria(CompoundCriteria.OR,logicList);
		}
	}
}

/*
name=boolean term
description=An optional ANDed boolean factor.
*/
Criteria compoundCritAnd(ParseInfo info) :
{
    ArrayList logicList = null;
    Criteria logicPart = null;
    Criteria logicPart1 = null;
}
{
	logicPart=notCrit(info) 		
	(<AND> logicPart1=notCrit(info) 		{ if (logicList == null) { logicList = new ArrayList(2); logicList.add(logicPart); } logicList.add(logicPart1); } )*

	{
   		if(logicList == null) {
			return logicPart;
		} else {	
         	return new CompoundCriteria(CompoundCriteria.AND,logicList);
		}	
	}
}

/*
name=boolean factor
description=A boolean factor.
example=[source,sql]\n----\nNOT x = 'a'\n----\n
*/
Criteria notCrit(ParseInfo info) :
{
    Expression ex = null;
	boolean isNot = false;
}
{
	[<NOT> {isNot=true;}] 
	ex=booleanPrimary(info)

	{
	    Criteria crit = null;
	    if (ex instanceof Criteria) {
	      crit = (Criteria)ex;
	    } else {
	      crit = new ExpressionCriteria(ex);
	    }
  		if(isNot) { 
  			return new NotCriteria(crit);
		} else {
			return crit;
		}
	}
}

/*
name=boolean primary
description=A boolean predicate or simple expression.
example=[source,sql]\n----\ncol LIKE 'a%'\n----\n
*/
Expression booleanPrimary(ParseInfo info) :
{
    Expression ex = null;
}
{
    (
	    (ex = commonValueExpression(info)
	     [(
			LOOKAHEAD(2) ex=betweenCrit(info, ex) |
			LOOKAHEAD(2) ex=matchCrit(info, ex) |
			LOOKAHEAD(2) ex=regexMatchCrit(info, ex) |
			ex=setCrit(info, ex) |
			ex=isNullCrit(info, ex) |
			LOOKAHEAD(operator() (<ANY>|<SOME>|<ALL>)) ex=subqueryCompareCriteria(info, ex) |
			ex=compareCrit(info, ex)
	     )]
	    )
	    | ex=existsCriteria(info)
	    | ex=xmlExists(info)
    )
	{
		return ex;
	}
}

/*
name=comparison operator
description=A comparison operator.
example=[source,sql]\n----\n=\n----\n
*/
Token operator() :
{
	Token operator = null;
}
{
	(operator=<EQ> |
    operator=<NE> |
    operator=<NE2> |
    operator=<LT> |
    operator=<LE> |
    operator=<GT> |
    operator=<GE>
    )
    {
    	return operator;
    }
}

/*
name=comparison predicate
description=A value comparison.
example=[source,sql]\n----\n= 'a'\n----\n
*/
CompareCriteria compareCrit(ParseInfo info, Expression expression) :
{
    Expression value = null;
    Token operator = null;
}
{
	operator=operator()
	value=commonValueExpression(info)

	{
		CompareCriteria compareCriteria = new CompareCriteria();

		// Set left expression
		compareCriteria.setLeftExpression(expression);

		compareCriteria.setOperator(getOperator(operator.image));
		
		// Set value
		compareCriteria.setRightExpression(value);

	    return compareCriteria;
	}
}

/*
name=subquery
description=A subquery.
example=[source,sql]\n----\n(SELECT * FROM tbl)\n----\n
*/
QueryCommand subquery(ParseInfo info) :
{
    QueryCommand subquery = null;
    StoredProcedure proc = null;
}
{
	<LPAREN>
	( subquery = queryExpression(info) | 
	 (
		proc = storedProcedure(info, 1) //deprecated	  
	 )
	 {
	    subquery = new Query(new Select(Arrays.asList(new MultipleElementSymbol())), new From(Arrays.asList(new SubqueryFromClause("x", proc))), null, null, null);
	 }
	)
	<RPAREN>
	{
		return subquery;
	}
}

/*
name=quantified comparison predicate
description=A subquery comparison.
example=[source,sql]\n----\n= ANY (SELECT col FROM tbl)\n----\n
*/
SubqueryCompareCriteria subqueryCompareCriteria(ParseInfo info, Expression expression) :
{
    SubqueryCompareCriteria subqueryCrit = null;
    QueryCommand subquery = null;
    Token operator = null;
    Token quantifier = null;
    ExistsCriteria.SubqueryHint hint = null;
    Expression expr = null;
}
{
	operator=operator()
	( quantifier=<ANY> |
	  quantifier=<SOME> |
	  quantifier=<ALL>
	)
	{ hint = getSubqueryHint(getToken(1)); }
	( LOOKAHEAD(subquery(info)) subquery = subquery(info) | 
      (<LPAREN> expr = expression(info) <RPAREN>)
    )

	{
		subqueryCrit = new SubqueryCompareCriteria();
	    subqueryCrit.setLeftExpression(expression);
	    subqueryCrit.setCommand(subquery);
	    subqueryCrit.setArrayExpression(expr);
    
		// Set operator
		subqueryCrit.setOperator(getOperator(operator.image));

		// Set predicate quantifier
		if(quantifier.image.equalsIgnoreCase("any")) { //$NON-NLS-1$
			subqueryCrit.setPredicateQuantifier(SubqueryCompareCriteria.ANY);
		} else if(quantifier.image.equalsIgnoreCase("some")) { //$NON-NLS-1$
			subqueryCrit.setPredicateQuantifier(SubqueryCompareCriteria.SOME);
		} else if(quantifier.image.equalsIgnoreCase("all")) { //$NON-NLS-1$
			subqueryCrit.setPredicateQuantifier(SubqueryCompareCriteria.ALL);
		} 
        subqueryCrit.setSubqueryHint(hint);
	    return subqueryCrit;
   	}
}

/*
name=match predicate
description=Matches based upon a pattern.
example=[source,sql]\n----\nLIKE 'a_'\n----\n
*/
MatchCriteria matchCrit(ParseInfo info, Expression expression) :
{
    Character esc = null;
	Expression value = null;
	boolean negated = false;
	boolean similar = false;
	Token t = null;
}
{
	[<NOT> {negated = true;}]
    (<LIKE>|(<SIMILAR> <TO> {similar = true;}))
    {t = getToken(1);}
	value=commonValueExpression(info)
    [   <ESCAPE> esc = charVal(info, "LIKE/SIMILAR TO ESCAPE") |
        (<LBRACE> <ESCAPE> esc = charVal(info, "LIKE/SIMILAR TO ESCAPE") <RBRACE>) 
    ]
	{
    	MatchCriteria matchcriteria = new MatchCriteria(expression, value);
		matchcriteria.setNegated(negated);
	    if(esc == null 
			&& ((value instanceof Constant && t.image != null && t.image.startsWith("E'"))
			    || info.isBackslashDefaultMatchEscape())) {
				esc = '\\';
	    }
		if (esc != null) {
		    matchcriteria.setEscapeChar(esc.charValue());
		}
		matchcriteria.setMode(similar?MatchMode.SIMILAR:MatchMode.LIKE);
		return matchcriteria;
   	}
}

/*
name=like regex predicate
description=A regular expression match.
example=[source,sql]\n----\nLIKE_REGEX 'a.*b'\n----\n
*/
MatchCriteria regexMatchCrit(ParseInfo info, Expression expression) :
{
	Expression value = null;
	boolean negated = false;
}
{
	[<NOT> {negated = true;}]
    <LIKE_REGEX>
	value=commonValueExpression(info)
	{
    	MatchCriteria matchcriteria = new MatchCriteria(expression, value);
		matchcriteria.setNegated(negated);
		matchcriteria.setMode(MatchMode.REGEX);
		return matchcriteria;
   	}
}

/*
name=character
description=A single character.
example=[source,sql]\n----\n'a'\n----\n
*/
Character charVal(ParseInfo info, String type) :
{
	String escStr = null;
}
{
	escStr=stringVal()
	{
		if (escStr.length() != 1) {
			throw new ParseException(QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30398, type, escStr)); //$NON-NLS-1$
		}
		return Character.valueOf(escStr.charAt(0));
	}
}

/*
name=between predicate
description=A comparison between two values.
example=[source,sql]\n----\nBETWEEN 1 AND 5\n----\n
*/
BetweenCriteria betweenCrit(ParseInfo info, Expression expression) :
{
	Expression lowerExpression = null, upperExpression = null;
	boolean negated = false;
}
{
	[<NOT> {negated=true;}]
	<BETWEEN>
	lowerExpression = commonValueExpression(info)
	<AND>
	upperExpression = commonValueExpression(info)
	
	{
		BetweenCriteria criteria = new BetweenCriteria(expression, lowerExpression, upperExpression);
		criteria.setNegated(negated);
		return criteria;
	}
}

/*
name=is null predicate
description=A null test.
example=[source,sql]\n----\nIS NOT NULL\n----\n
*/
IsNullCriteria isNullCrit(ParseInfo info, Expression expression) :
{
	boolean negated = false;
	IsNullCriteria criteria = null;
}
{
	<IS>
	[<NOT> {negated = true;}]
	<NULL>

	{
	criteria = new IsNullCriteria(expression);
	criteria.setNegated(negated);
	return criteria;
	}
}

/*
name=in predicate
description=A comparison with multiple values.
example=[source,sql]\n----\nIN (1, 5)\n----\n
*/
AbstractSetCriteria setCrit(ParseInfo info, Expression expression) :
{
	Expression value = null;
	List setList = new ArrayList();
	QueryCommand command = null;
	ExistsCriteria.SubqueryHint hint = null;
	boolean negated = false;
	AbstractSetCriteria criteria = null;
}
{
	[<NOT> {negated = true;}]
	<IN>
	(
		LOOKAHEAD(subquery(info)) { hint = getSubqueryHint(getToken(1)); } (command = subquery(info)) |
		(
			<LPAREN>
			value = commonValueExpression(info)
			{
				setList.add(value);
			}
			( <COMMA>
				value = commonValueExpression(info)
				{
					setList.add(value);
				}
			)*
			<RPAREN>
		)
	)
	{
	    if (command != null) {
			SubquerySetCriteria ssc = new SubquerySetCriteria(expression, command);
			ssc.setSubqueryHint(hint);
			criteria = ssc;
		} else {
		    criteria = new SetCriteria(expression, setList);
		}
	    criteria.setNegated(negated);
	    return criteria;
   	}
}

/*
name=exists predicate
description=A test if rows exist.
example=[source,sql]\n----\nEXISTS (SELECT col FROM tbl)\n----\n
*/
ExistsCriteria existsCriteria(ParseInfo info) :
{
    QueryCommand subquery = null;
    ExistsCriteria.SubqueryHint hint = null;
}
{
	<EXISTS> { hint = getSubqueryHint(getToken(1)); } 
	subquery = subquery(info)

	{
		ExistsCriteria existsCrit = new ExistsCriteria(subquery);
		existsCrit.setSubqueryHint(hint);
	    return existsCrit;
   	}
}

/*
name=group by clause
description=Defines the grouping columns
example=[source,sql]\n----\nGROUP BY col1, col2\n----\n
*/
GroupBy groupBy(ParseInfo info) :
{
	List<Expression> expressions = null;
	boolean rollup = false;
}
{
	<GROUP> <BY>
	(<ROLLUP><LPAREN>
     expressions = expressionList(info)
     <RPAREN>
     {
       rollup = true;
     }
     |
     expressions = expressionList(info)
    )	
	{
		GroupBy result = new GroupBy(expressions);
		result.setRollup(rollup);
		return result;
	}
}

/*
name=having clause
description=Search condition applied after grouping.
example=[source,sql]\n----\nHAVING max(col1) = 5\n----\n
*/
Criteria having(ParseInfo info) :
{
    Criteria criteria = null;
}
{
    <HAVING>
	criteria = criteria(info)
	{
		return criteria;
	}
}

/*
name=order by clause
description=Specifices row ordering.
example=[source,sql]\n----\nORDER BY x, y DESC\n----\n
*/
OrderBy orderby(ParseInfo info) :
{
    OrderBy orderby = new OrderBy();
    OrderByItem item = null;
}
{
	<ORDER> <BY>
	item = sortSpecification(info)
	{
    	orderby.getOrderByItems().add(item);
	}
	(<COMMA>
		item = sortSpecification(info)
		{
    		orderby.getOrderByItems().add(item);
		}
	)*
	{
	    return orderby;
	}
}

/*
name=sort specification
description=Defines how to sort on a particular expression
example=[source,sql]\n----\ncol1 NULLS FIRST\n----\n
*/
OrderByItem sortSpecification(ParseInfo info) :
{
    Expression ex = null;
    boolean ascending = true;
    org.teiid.language.SortSpecification.NullOrdering nullOrdering = null;    
}
{
	ex=sortKey(info) 
	[<ASC> | <DESC> {ascending=false;}]
	[<NULLS> (<FIRST> {nullOrdering=org.teiid.language.SortSpecification.NullOrdering.FIRST;} | <LAST> {nullOrdering=org.teiid.language.SortSpecification.NullOrdering.LAST;})] 
	{
    	OrderByItem item = new OrderByItem(ex, ascending);
    	if (nullOrdering != null) {
    		item.setNullOrdering(nullOrdering);
    	}
    	return item;
	}
}

/*
name=sort key
description=A sort expression.
example=[source,sql]\n----\ncol1\n----\n
*/
Expression sortKey(ParseInfo info) :
{
    Expression ex = null;
}
{
    ex=expression(info)
    {
        //legacy support check for positional constants
        if (ex instanceof Constant) {
        	boolean valid = false;
            Constant c = (Constant)ex;
            if (c.getValue() instanceof Integer) {
                Integer val = (Integer)c.getValue();
                valid = val.intValue() > 0;
            }      
            if (!valid) {
            	throw new ParseException(QueryPlugin.Util.getString("SQLParser.non_position_constant", ex)); //$NON-NLS-1$
            }
        } 
        return ex;
    }
}

/*
name=integer parameter
description=A literal integer or parameter reference to an integer.
example=[source,sql]\n----\n?\n----\n
*/
Expression intParam(ParseInfo info) :
{
   Expression ex = null;
   Integer val = null;
}
{
   val = intVal() 
   {
     return new Constant(val, DataTypeManager.DefaultDataClasses.INTEGER);
   }
   |
   ex = unsignedValueExpressionPrimary(info)
   {
     return ex;
   }
}

/*
name=limit clause
description=Limits and/or offsets the resultant rows.
example=[source,sql]\n----\nLIMIT 2\n----\n
*/
Limit limit(ParseInfo info) :
{
	Expression limit = null;
	Expression offset = null;
	Token t = null;
}
{
    {t = getToken(1);}
    ((<LIMIT> offset = intParam(info)
	[<COMMA> limit = intParam(info)])
	{
	  if (limit == null) {
	    limit = offset;
	    offset = null;
	  }
	}
	|
	(<OFFSET> offset = intParam(info) (<ROW>|<ROWS>) 
    [limit = fetchLimit(info)])
    |
    (limit = fetchLimit(info)))
	{
		Limit result = new Limit(offset, limit);
		result.setStrict(!isNonStrictHint(t));
		return result;
	}
}

/*
name=fetch clause
description=ANSI limit.
example=[source,sql]\n----\nFETCH FIRST 1 ROWS ONLY\n----\n
*/
Expression fetchLimit(ParseInfo info) :
{
    Expression limit = null;
}
{
    <FETCH> (<FIRST> | <NEXT>) [limit = intParam(info)] (<ROW>|<ROWS>) <ONLY>
    {
      if (limit == null) {
        return new Constant(1, DataTypeManager.DefaultDataClasses.INTEGER); 
      }
      return limit;
    }
}

/*
name=option clause
description=Specifies query options.
example=[source,sql]\n----\nOPTION MAKEDEP tbl\n----\n
*/
Option option(ParseInfo info) :
{
	String id = null;
	Token nocache = null;
	Option option = new Option();
	Option.MakeDep makedep = null;
}
{
	<OPTION>
	(
		<MAKEDEP>		
		id=id(null) makedep = makedepOptions()
		{
            option.addDependentGroup(id, makedep);
		}
		(<COMMA>
			id=id(null) makedep = makedepOptions()
			{
    	        option.addDependentGroup(id, makedep);
	        }
		)* |
		<MAKEIND>		
		id=id(null) makedep = makedepOptions()
		{
            option.addIndependentGroup(id, makedep);
		}
		(<COMMA>
			id=id(null) makedep = makedepOptions()
			{
    	        option.addIndependentGroup(id, makedep);
	        }
		)* |
		<MAKENOTDEP>		
		id=id(null)
		{
            option.addNotDependentGroup(id);
		}
		(<COMMA>
			id=id(null)
			{
                option.addNotDependentGroup(id);
	        }
		)* |
		
		nocache	= <NOCACHE>		
		[id=id(null)
		{
            option.addNoCacheGroup(id);
		}
		(<COMMA>
			id=id(null)
			{
                option.addNoCacheGroup(id);
	        }
		)*]
	)*

	{
		option.setNoCache( (nocache != null) );
		return option;
	}
}

/*
name=expression
description=A value.
example=[source,sql]\n----\ncol1\n----\n
*/
Expression expression(ParseInfo info) :
{
	Expression expression = null;
}
{
    expression = criteria(info)
	{
		if (expression instanceof ExpressionCriteria) {
			return ((ExpressionCriteria)expression).getExpression();
		}
		return expression;
	}
}

/*
name=common value expression
description=Establishes the precedence of concat.
example=[source,sql]\n----\n'a' || 'b'\n----\n
*/
Expression commonValueExpression(ParseInfo info) :
{
	Expression leftExpression = null;
	Expression rightExpression = null;
	Token operator = null;
}
{
	(	leftExpression=plusExpression(info)
		(
			(operator=<DOUBLE_AMP_OP>|operator=<CONCAT_OP>)
			rightExpression=plusExpression(info)
			{
				leftExpression = new Function(operator.image, new Expression[] {leftExpression, rightExpression}); //$NON-NLS-1$
				rightExpression = null;				
			}
		)*
	)

	{
		return leftExpression;
	}
}

/*
name=numeric value expression
example=[source,sql]\n----\n1 + 2\n----\n
*/
Expression plusExpression(ParseInfo info) :
{
	Expression leftExpression = null;
	Expression rightExpression = null;
	String operator = null;
}
{
	(	leftExpression=timesExpression(info)
		(
			operator=plusMinus()
			rightExpression=timesExpression(info)
			{
				leftExpression = new Function(operator, new Expression[] { leftExpression, rightExpression });
				rightExpression = null;
				operator = null;
			}
		)*
	)

	{
		return leftExpression;
	}
}

/*
name=plus or minus
description=The + or - operator.
example=[source,sql]\n----\n+\n----\n
*/
String plusMinus() :
{
}
{
	(<PLUS> | <MINUS>)
	{
		return getToken(0).image;
	}
}

/*
name=term
description=A numeric term
example=[source,sql]\n----\n1 * 2\n----\n
*/
Expression timesExpression(ParseInfo info) :
{
	Expression leftExpression = null;
	Expression rightExpression = null;
	String operator = null;
}
{
	(	leftExpression=valueExpressionPrimary(info)
		(
			operator=timesOperator()
			rightExpression=valueExpressionPrimary(info) 
			{
				leftExpression = new Function(operator, new Expression[] {leftExpression, rightExpression});
				rightExpression = null;
				operator = null;
			}
		)*
	)

	{
		return leftExpression;
	}
}

/*
name=star or slash
description=The * or / operator.
example=[source,sql]\n----\n/\n----\n
*/
String timesOperator() :
{
	Token operator = null;
}
{
	(	operator = <STAR> |
		operator = <SLASH>
	)

	{
		return operator.image;
	}
}

/*
name=value expression primary
description=A simple value expression.
example=[source,sql]\n----\n+col1\n----\n
*/
Expression valueExpressionPrimary(ParseInfo info) :
{
	String strVal = null;
	Expression ex = null;
	Expression indexExpr = null;
    List<Expression> indexExprs = null;
}
{
	// Literal
	(ex=nonNumericLiteral()
	| [strVal = plusMinus()] 
	  (ex = unsignedNumericLiteral(strVal)
	   |
	   (ex = unsignedValueExpressionPrimary(info)
	    //array element reference expressions
        (<LSBRACE> indexExpr = plusExpression(info) <RSBRACE> {
           indexExprs = arrayExpressions(indexExprs, indexExpr);
           }
        )*
        {
          if (indexExprs != null) {
            for (Expression expr : indexExprs) {
                ex = new Function("array_get", new Expression[] {ex, expr});
            }
          }
        }
	   ) {
         if (strVal != null && strVal.charAt(0) == '-') {
            ex = new Function("*", new Expression[] { new Constant(-1), ex });
         }
        }
	   )
	 )
	 {
		return ex;	 
	 }  
}

/*
name=parameter reference
description=A parameter reference to be bound later.
example=[source,sql]\n----\n?\n----\n
*/
Reference parameterReference(ParseInfo info) :
{
    Integer index = null;
}
{
    <QMARK> {return new Reference(info.referenceCount++);}
    |
    (<DOLLAR> index = intVal() {return new Reference(index -1); })
}

Expression unescapedFunction(ParseInfo info) :
{
    Expression expression = null;
    Expression condition = null;
    ParsedDataType type = null;
    
}
{
    // Aggregate function
    (LOOKAHEAD(2) ((expression=textAgg(info) | expression=aggregateSymbol(info) | expression=orderedAgg(info)) [condition = filterClause(info)] [expression = windowSpecification(expression, info)])
    |
    LOOKAHEAD(2) (expression=analyticAggregateSymbol(info) [condition = filterClause(info)] expression = windowSpecification(expression, info))
    |
    // Function
    LOOKAHEAD(2) (expression=function(info) [expression = windowSpecification(expression, info)]))
    {
        if (condition != null) {
            if (expression instanceof WindowFunction) {
                ((WindowFunction)expression).getFunction().setCondition(condition);
            } else {
                ((AggregateSymbol)expression).setCondition(condition);
            }
        }
        return expression;
    }
    |
	(			
		<XMLCAST>
		<LPAREN>
		expression = expression(info)
		<AS>
		type = parseDataType()
		<RPAREN>				
    ) { return new XMLCast(expression, type.type); }
}

/*
name=nested expression
description=An expression nested in parens
example=[source,sql]\n----\n(1)\n----\n
*/
Expression nestedExpression(ParseInfo info) :
{
    Expression expression = null;
    Expression arrayExpression = null;
    List<Expression> arrayExpressions = null;
}
{
    ( <LPAREN> 
        [expression = expression(info)
        (LOOKAHEAD(2) <COMMA>arrayExpression = expression(info) {
          if (expression != null) {
             arrayExpressions = arrayExpressions(arrayExpressions, expression);
             expression = null;
          } 
          arrayExpressions = arrayExpressions(arrayExpressions, arrayExpression);})*]
        [<COMMA> {arrayExpressions = arrayExpressions(arrayExpressions, expression);} ]
      <RPAREN>
    )
    {
        if (arrayExpressions != null) {
            expression = new Array(arrayExpressions);
        } else if (expression == null) {
            expression = new Array(new ArrayList<Expression>(0));
        }
        return expression;
    }
}    

/*
name=unsigned value expression primary
description=An unsigned simple value expression.
example=[source,sql]\n----\ncol1\n----\n
*/
Expression unsignedValueExpressionPrimary(ParseInfo info) :
{
	Expression expression = null;
	Token symbol = null;
	QueryCommand subquery = null;
	ExistsCriteria.SubqueryHint hint = null;
}
{
    (
		// Reference
		expression = parameterReference(info)
		|
		// Escaped function
		(	<ESCAPEDFUNCTION>
			expression=function(info)
			<RBRACE>
		)
		|
		LOOKAHEAD(2) expression = unescapedFunction(info)		
		|
		// ElementSymbol
		((symbol=<ID> {
				String symbolImage = symbol.image;
				if(isStringLiteral(symbolImage, info)) {
				    return new Constant(normalizeStringLiteral(symbolImage));
				}  
			} | symbol=nonReserved()) {expression = new ElementSymbol(normalizeId(symbol.image));} 
		)
		|
		LOOKAHEAD(subquery(info)) ({ hint = getSubqueryHint(getToken(1)); } subquery = subquery(info) {
		   ScalarSubquery ss = new ScalarSubquery(subquery);
		   ss.setSubqueryHint(hint);
		   expression = ss;
		})
		|
		expression = nestedExpression(info)
		|
		expression = arrayExpression(info)
		|
		// Searched CASE expressions
		LOOKAHEAD(2) expression = searchedCaseExpression(info)
		|
		// Non-searched CASE expressions
		expression = caseExpression(info)
	)
	{
		return expression;
	}
}

/*
name=ARRAY expression constructor
description=Creates and array of the given expressions.
example=[source,sql]\n----\ARRAY[1,2]\n----\n
*/
Expression arrayExpression(ParseInfo info) :
{
    Expression arrayExpression = null;
    List<Expression> arrayExpressions = new ArrayList<Expression>();
}
{
    ( <ARRAY> <LSBRACE> 
        [arrayExpression = expression(info) {arrayExpressions.add(arrayExpression);}
         (<COMMA> arrayExpression = expression(info) {arrayExpressions.add(arrayExpression);})*
        ]
      <RSBRACE>
    )
    {
        return new Array(arrayExpressions);
    }
}    


/*
name=window specification
description=The window specification for an analytical or windowed aggregate function.
example=[source,sql]\n----\nOVER (PARTION BY col1)\n----\n
*/
Expression windowSpecification(Expression agg, ParseInfo info) :
{
	List<Expression> partitionList = null;
	OrderBy orderBy = null;
}
{
	<OVER> { 
	  if (!(agg instanceof AggregateSymbol)) {
	    if (!(agg instanceof Function)) {
	      throw new ParseException(QueryPlugin.Util.getString("SQLParser.invalid_window", agg)); //$NON-NLS-1$
	    }
	    Function f = (Function)agg;
	    agg = new AggregateSymbol(f.getName(), false, f.getArgs(), null);
	  }
    }
    <LPAREN> 
    [<PARTITION> <BY> partitionList = expressionList(info)]
    [orderBy = orderby(info)]
    <RPAREN>
    {
    	WindowFunction result = new WindowFunction();
    	WindowSpecification ws = new WindowSpecification();
    	result.setFunction((AggregateSymbol)agg);
    	ws.setPartition(partitionList);
    	ws.setOrderBy(orderBy);
    	result.setWindowSpecification(ws);
    	return result;
    }
}

/*
name=case expression
description=If/then/else chain using a common search predicand.
example=[source,sql]\n----\nCASE col1 WHEN 'a' THEN 1 ELSE 2\n----\n
*/
CaseExpression caseExpression(ParseInfo info) :
{
	Expression expression = null, whenExpression = null, thenExpression = null, elseExpression = null;
	ArrayList whenExpressions = new ArrayList(2);
	ArrayList thenExpressions = new ArrayList(2);
}
{
	<CASE>
	expression = expression(info)
	(
		<WHEN>
		whenExpression = expression(info)
		<THEN>
		thenExpression = expression(info)
		{
			whenExpressions.add(whenExpression);
			thenExpressions.add(thenExpression);
		}
	)+
	[<ELSE> elseExpression = expression(info)]
	<END>
	{
		CaseExpression caseExpr = new CaseExpression(expression, whenExpressions, thenExpressions);
		caseExpr.setElseExpression(elseExpression);
		return caseExpr;
	}
}

/*
name=searched case expression
description=If/then/else chain using multiple search conditions.
example=[source,sql]\n----\nCASE WHEN x = 'a' THEN 1 WHEN y = 'b' THEN 2\n----\n
*/
SearchedCaseExpression searchedCaseExpression(ParseInfo info) :
{
	Expression thenExpression = null, elseExpression = null;
	Criteria whenCriteria = null;
	ArrayList whenCriteriaList = new ArrayList(2);
	ArrayList thenExpressions = new ArrayList(2);
}
{
	<CASE>
	(
		<WHEN>
		whenCriteria = criteria(info)
		<THEN>
		thenExpression = expression(info)
		{
			whenCriteriaList.add(whenCriteria);
			thenExpressions.add(thenExpression);
		}
	)+
	
	[<ELSE> elseExpression = expression(info)]
	<END>
	{
		SearchedCaseExpression caseExpr = new SearchedCaseExpression(whenCriteriaList, thenExpressions);
		caseExpr.setElseExpression(elseExpression);
		return caseExpr;
	}
}

/*
name=function
description=Calls a scalar function.
example=[source,sql]\n----\nfunc('1', col1)\n----\n
*/
Expression function(ParseInfo info) :
{
	String funcName = null;
    
	Expression expression = null;
	ArrayList args = new ArrayList(2);
	ArrayList otherArgs = null;
	Token funcToken = null;
	Boolean distinct = null;
	OrderBy orderBy = null;
	Expression condition = null;
	String literal = null;
	ParsedDataType type = null;
}
{
	((	funcToken = <CONVERT>
		<LPAREN>
		expression = expression(info)
		{
			args.add(expression);
		}
		<COMMA>
		type = parseDataType()	
		{
			args.add(new Constant(type.type));
		}
		<RPAREN> 
	)
	|
	(			
		funcToken = <CAST>
		<LPAREN>
		expression = expression(info)
		{
			args.add(expression);
		} 
		<AS>
		type = parseDataType()
		{
			args.add(new Constant(type.type));
		}
		<RPAREN>				
	)
	|
	LOOKAHEAD(2) (
	   funcToken = <SUBSTRING>
	   <LPAREN>
	   expression = expression(info)
	   {
	      args.add(expression);
	   }
	   ((<FROM> expression = expression(info)
	   {
	      args.add(expression);
	   }
  	   [<FOR> expression = expression(info)
	    {
	      args.add(expression);
	    }
	   ])|(<COMMA> otherArgs = expressionList(info)) {args.addAll(otherArgs);})
	   <RPAREN>
	)
	|
	LOOKAHEAD(2) (
	   funcToken = <EXTRACT>
	   <LPAREN>
	   (funcToken = <YEAR> | 
	    funcToken = <MONTH> |
	    funcToken = <DAY> |
	    funcToken = <HOUR> |
	    funcToken = <MINUTE> |
	    funcToken = <SECOND>)
	   <FROM> expression = expression(info)
	   {
	   	  if (funcToken.image.equalsIgnoreCase("DAY")) {
	         funcName = "DAYOFMONTH";
	      }  	      
	      args.add(expression);
	   }
	   <RPAREN>
	)
	|
	LOOKAHEAD(2) (
	   funcToken = <TRIM>
	   <LPAREN>
	   { funcName = funcToken.image; funcToken = null; expression = null;}
	   [
	    LOOKAHEAD((<LEADING>|<TRAILING>|<BOTH>)|(expression(info)<FROM>)) (((funcToken = <LEADING>|
	      funcToken = <TRAILING>|
	      funcToken = <BOTH>)
	    [expression = expression(info)])
	    |
	    expression = expression(info))
		<FROM>
	   ]
  	   {
	      if (funcToken == null) {
	      	  args.add(new Constant("BOTH"));
	      } else {
	          args.add(new Constant(funcToken.image));
	      }
	      if (expression == null) {
	          args.add(new Constant(" "));
	      } else {
	          if (expression instanceof Constant) {
	              Object value = ((Constant)expression).getValue();
	              if (value instanceof String && ((String)value).length() != 1) {
				      throw new ParseException(QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30398, "TRIM CHAR", value)); //$NON-NLS-1$
	              }
	          }
	          args.add(expression);
	      }
	   }
	   expression = expression(info)
	   {
	      args.add(expression);
	   }
	   <RPAREN>
	)
	|
	LOOKAHEAD(2) (	
		(funcToken = <TO_CHARS> | funcToken = <TO_BYTES>)
		<LPAREN>
		expression = expression(info) 
		{		
			args.add(expression);
		}
		<COMMA>
		literal = stringVal()
		{
			args.add(new Constant(literal));
		} 
		[<COMMA> expression = expression(info) { args.add(expression); }]
		<RPAREN>				
	)
	|
	LOOKAHEAD(2) (	
		(funcToken = <TIMESTAMPADD> | funcToken = <TIMESTAMPDIFF>)
		<LPAREN>
		expression = intervalType() 
		{		
			args.add(expression);
		}
		<COMMA>
		expression = expression(info)
		{
			args.add(expression);
		} 
		<COMMA>
		expression = expression(info)
		{
			args.add(expression);
		} 
		<RPAREN>				
	)
	|
	LOOKAHEAD(2) expression = queryString(info)
	  {
		return expression;
	  }		
	|
	(
		(funcToken = <LEFT> | funcToken = <RIGHT> | funcToken = <CHAR> | funcToken = <USER> 
		                    | funcToken = <YEAR> | funcToken = <MONTH> | funcToken = <HOUR>
		                    | funcToken = <MINUTE> | funcToken = <SECOND> | funcToken = <XMLCONCAT>
		                    | funcToken = <XMLCOMMENT> | funcToken = <XMLTEXT>)
        <LPAREN>
		[args = expressionList(info)]
		<RPAREN>
	)		
	|
	LOOKAHEAD(<TRANSLATE>|<INSERT> <LPAREN>) (
		(funcToken = <TRANSLATE> | funcToken = <INSERT>)
		<LPAREN>
		[args = expressionList(info)]
		<RPAREN>
	)  
	| expression = xmlParse(info)
	  {
	  	return expression;
	  }
	| expression = xmlElement(info)
	  {
	  	return expression;
	  }
	| ( funcToken = <XMLPI>
		<LPAREN>
		(
		 [LOOKAHEAD(1) <NAME>] literal = id(null)
		)
		{
			args.add(new Constant(literal));	
		}
		[
			<COMMA> expression = expression(info)
			{
				args.add(expression);	
			}	
		]
		<RPAREN>
	)
	|	expression = xmlForest(info)
		{
			return expression;
		}
	|	LOOKAHEAD(<JSONOBJECT>) expression = jsonObject(info)
		{
			return expression;
		}
	|	expression = xmlSerialize(info)
		{
			return expression;
		}
	|	expression = xmlQuery(info)
		{
			return expression;
		}
	|   
	(	funcName = id(null)
		<LPAREN>
		[<ALL> { distinct = false; } | <DISTINCT> {distinct = true;} ]
		[ args = expressionList(info) ]
		[ orderBy = orderby(info) ]
		<RPAREN>
		[condition = filterClause(info)]
	))		
	{
		if(funcName == null) { 
			funcName = funcToken.image;
		}
		if (distinct != null || orderBy != null || condition != null || "string_agg".equalsIgnoreCase(funcName)) {
			AggregateSymbol as = new AggregateSymbol(funcName, distinct!=null?distinct:false, (Expression[])args.toArray(new Expression[args.size()]), orderBy);
			as.setCondition(condition);
			return as;
		}	
		return new Function(funcName, (Expression[])args.toArray(new Expression[args.size()]));
	}
}

/*
name=xml parse
description=Parses the given value as XML.
example=[source,sql]\n----\nXMLPARSE(DOCUMENT doc WELLFORMED)\n----\n
*/
XMLParse xmlParse(ParseInfo info) :
{
	Expression expr = null;
	boolean doc = false;
	boolean wellformed = false;
}
{
	<XMLPARSE><LPAREN>
	(<DOCUMENT> {doc = true;}| <CONTENT> {doc = false;})
	expr = expression(info)
	[<WELLFORMED>
	 {
	   wellformed = true;
	 }
	]
	<RPAREN>
	{
		XMLParse result = new XMLParse();
		result.setDocument(doc);
		result.setExpression(expr);
		result.setWellFormed(wellformed);
		return result;
	}
}

/*
name=querystring function
description=Produces a URL query string from the given arguments.
example=[source,sql]\n----\nQUERYSTRING('path', col1 AS opt, col2 AS val)\n----\n
*/
QueryString queryString(ParseInfo info) :
{
	Expression path = null;
	DerivedColumn arg = null;
	ArrayList<DerivedColumn> args = new ArrayList<DerivedColumn>(2);
}
{
 	<QUERYSTRING> <LPAREN>
 	path = expression(info)
	(<COMMA> arg=derivedColumn(info)
	 {
		args.add(arg);
		arg = null;
	 }
	)*
	<RPAREN>  		
	{
		return new QueryString(path, args);
	}
}

/*
name=xml element
description=Creates an XML element.
example=[source,sql]\n----\nXMLELEMENT(NAME "root", child)\n----\n
*/
XMLElement xmlElement(ParseInfo info) :
{
	String name = null;
	ArrayList content = new ArrayList(2);
	XMLNamespaces xmlNamespaces = null;
	XMLAttributes xmlAttributes = null;
	Expression expression = null;
}
{
	<XMLELEMENT> <LPAREN>
	(
	 [LOOKAHEAD(1) <NAME>] name = id(null)
	)
	[
		LOOKAHEAD(2) <COMMA> xmlNamespaces = xmlNamespaces(info)
	]
	[
		LOOKAHEAD(2) <COMMA> xmlAttributes = xmlAttributes(info)
	]
	(<COMMA> expression=expression(info)
	{
		content.add(expression);
	}
	)*
	<RPAREN>
	{
		XMLElement result = new XMLElement(name, content);
		result.setNamespaces(xmlNamespaces);
		result.setAttributes(xmlAttributes);
		return result;
	}
}	

/*
name=xml attributes
description=Creates attributes for the containing element.
example=[source,sql]\n----\nXMLATTRIBUTES(col1 AS attr1, col2 AS attr2)\n----\n
*/
XMLAttributes xmlAttributes(ParseInfo info) :
{
	DerivedColumn expression = null;
	ArrayList<DerivedColumn> args = new ArrayList<DerivedColumn>(2);
}
{
 	<XMLATTRIBUTES> <LPAREN>
	expression = derivedColumn(info)
	{
		args.add(expression);
	}
	(<COMMA> expression=derivedColumn(info)
	 {
		args.add(expression);
	 }
	)*
	<RPAREN>  		
	{
		return new XMLAttributes(args);
	}
}

/*
name=json object
description=Produces a JSON object containing name value pairs.
example=[source,sql]\n----\nJSONOBJECT(col1 AS val1, col2 AS val2)\n----\n
*/
JSONObject jsonObject(ParseInfo info) :
{
	ArrayList<DerivedColumn> args = null;
}
{
 	<JSONOBJECT> <LPAREN>
 	args = derivedColumnList(info)
 	<RPAREN>  		
	{
		JSONObject result = new JSONObject(args);
		return result;
	}
}

/*
name=derived column list
description=a list of name value pairs
example=[source,sql]\n----\ncol1 AS val1, col2 AS val2\n----\n
*/
ArrayList<DerivedColumn> derivedColumnList(ParseInfo info) :
{
	DerivedColumn expression = null;
	ArrayList<DerivedColumn> args = new ArrayList<DerivedColumn>(2);
}
{
	expression = derivedColumn(info)
	{
		args.add(expression);
	}
	(<COMMA> expression=derivedColumn(info)
	 {
		args.add(expression);
	 }
	)*
	{ 
		return args;
	}
}

/*
name=xml forest
description=Produces an element for each derived column.
example=[source,sql]\n----\nXMLFOREST(col1 AS ELEM1, col2 AS ELEM2)\n----\n
*/
XMLForest xmlForest(ParseInfo info) :
{
	DerivedColumn expression = null;
	XMLNamespaces xmlNamespaces = null;
	ArrayList<DerivedColumn> args = null;
}
{
 	<XMLFOREST> <LPAREN>
	[
		xmlNamespaces = xmlNamespaces(info) <COMMA>
	]
	args = derivedColumnList(info)
	<RPAREN>  		
	{
		XMLForest result = new XMLForest(args);
		result.setNamespaces(xmlNamespaces);
		return result;
	}
}		
		
/*
name=xml namespaces
description=Defines XML namespace URI/prefix combinations
example=[source,sql]\n----\nXMLNAMESPACES('http://foo' AS foo)\n----\n
*/
XMLNamespaces xmlNamespaces(ParseInfo info) :
{
	ArrayList namespaces = new ArrayList(2);
	XMLNamespaces.NamespaceItem item = null;
}
{
 	<XMLNAMESPACES> <LPAREN>
	item = namespaceItem()
	{
		namespaces.add(item);
	}
	(<COMMA> item = namespaceItem()
	 {
		 namespaces.add(item);
	 }
	)*
	<RPAREN>  		
	{
		return new XMLNamespaces(namespaces);
	}
}

/*
name=xml namespace element
description=An xml namespace
example=[source,sql]\n----\nNO DEFAULT\n----\n
*/
XMLNamespaces.NamespaceItem namespaceItem() :
{
	String uri = null;
	String prefix = null;
}
{
	(
		uri = stringVal() <AS> prefix = id(null)
		{
			return new XMLNamespaces.NamespaceItem(uri, prefix);
		}
	) | (
		<NO> <DEFAULT_KEYWORD>
		{
			return new XMLNamespaces.NamespaceItem();
		}
	) | (
		<DEFAULT_KEYWORD> uri = stringVal()
		{
			return new XMLNamespaces.NamespaceItem(uri);
		}
	)
}

/*
name=simple data type
description=A non-collection data type.
example=[source,sql]\n----\nSTRING\n----\n
*/
ParsedDataType parseDataTypePrimary() :
{
	Token typeToken = null;
	Integer length = null;
	Integer scale = null; 
	boolean precision = true;
}
{
	(
		(typeToken = <STRING> ([<LPAREN>length=intVal()<RPAREN>]) {precision = false;} ) |
		(typeToken = <VARCHAR> ([<LPAREN>length=intVal()<RPAREN>]) {precision = false;} ) | 
		typeToken = <BOOLEAN> | 
		typeToken = <BYTE> | 
		typeToken = <TINYINT> |
		typeToken = <SHORT> | 
		typeToken = <SMALLINT> |
		(typeToken = <CHAR> ([<LPAREN>length=intVal()<RPAREN>]) {precision = false;} ) | 
		typeToken = <INTEGER> | 
		typeToken = <LONG> | 
		typeToken = <BIGINT> |
		(typeToken = <BIGINTEGER> [<LPAREN>length=intVal()<RPAREN>]) | 
		typeToken = <FLOAT> |
		typeToken = <REAL> | 
		typeToken = <DOUBLE> | 
		(typeToken = <BIGDECIMAL> [<LPAREN>length=intVal()[<COMMA>scale=intVal()]<RPAREN>])|
		(typeToken = <DECIMAL> [<LPAREN>length=intVal()[<COMMA>scale=intVal()]<RPAREN>])| 
		typeToken = <DATE> | 
		typeToken = <TIME> |
		typeToken = <TIMESTAMP> |
		(typeToken = <OBJECT> ([<LPAREN>length=intVal()<RPAREN>]) {precision = false;} )|
		(typeToken = <BLOB> ([<LPAREN>length=intVal()<RPAREN>]) {precision = false;} )|
		(typeToken = <CLOB> ([<LPAREN>length=intVal()<RPAREN>]) {precision = false;} )|
		(typeToken = <VARBINARY> ([<LPAREN>length=intVal()<RPAREN>]) {precision = false;} ) |
		typeToken = <GEOMETRY> |
		typeToken = <XML>
	)
	{
	    String typeName = typeToken.image;
		if (scale != null){
			return new ParsedDataType(typeName, length, scale, precision);
		}
		else if (length != null){
			return new ParsedDataType(typeName, length, precision);
		}
		return new ParsedDataType(typeName);
	}
}

/*
name=basic data type
description=A data type.
example=[source,sql]\n----\nSTRING[]\n----\n
*/
ParsedDataType parseBasicDataType() :
{
    int arraySuffixes = 0;
    ParsedDataType type = null;
}
{
    type = parseDataTypePrimary()
    (<LSBRACE><RSBRACE> {arraySuffixes++;})*
    {
        if (arraySuffixes > 0) {
           type.type += StringUtil.join(Collections.nCopies(arraySuffixes, "[]"), "");
        }
        return type;
    }
}

/*
name=data type
description=A data type.
example=[source,sql]\n----\nSTRING[]\n----\n
*/
ParsedDataType parseDataType() :
{
    int arraySuffixes = 0;
    ParsedDataType type = null;
    Token idToken = null;
}
{
    type = parseBasicDataType()
    {
        return type;
    }
    |
    (idToken = <ID> 
    {
        String id = normalizeId(idToken.image);
        id = validateName(id, true);
        type = new ParsedDataType(id);
    }
    (<LSBRACE><RSBRACE> {arraySuffixes++;})*
    {
        if (arraySuffixes > 0) {
           type.type += StringUtil.join(Collections.nCopies(arraySuffixes, "[]"), "");
        }
        return type;
    })
}

/*
name=time interval
description=A time interval keyword.
example=[source,sql]\n----\nSQL_TSI_HOUR\n----\n
*/
Constant intervalType() : 
{
	Token type = null;
}
{
	(
		type = <SQL_TSI_FRAC_SECOND>
	   |type = <SQL_TSI_SECOND>
	   |type = <SQL_TSI_MINUTE>
	   |type = <SQL_TSI_HOUR>
	   |type = <SQL_TSI_DAY>
	   |type = <SQL_TSI_WEEK>
	   |type = <SQL_TSI_MONTH>
	   |type = <SQL_TSI_QUARTER>
	   |type = <SQL_TSI_YEAR>
	)
	{
		return new Constant(type.image);
	}
}

/*
name=non numeric literal
description=An escaped or simple non numeric literal.
example=[source,sql]\n----\n'a'\n----\n
*/
Constant nonNumericLiteral() :
{
    Token t = null;
    String strVal = "";
    Class escapeType = null;
    Constant constant = null;
}
{
	(	
		strVal=stringVal()	{ 
			constant = new Constant(strVal, DataTypeManager.DefaultDataClasses.STRING);
		} |
		t=<BINARYSTRINGVAL> { constant = new Constant(new BinaryType(javax.xml.bind.DatatypeConverter.parseHexBinary(t.image.substring(2, t.image.length() - 1))), DataTypeManager.DefaultDataClasses.VARBINARY); } |
		t=<FALSE>    { constant = new Constant(Boolean.FALSE, DataTypeManager.DefaultDataClasses.BOOLEAN); } |
		t=<TRUE>     { constant = new Constant(Boolean.TRUE, DataTypeManager.DefaultDataClasses.BOOLEAN); }  |
        t=<UNKNOWN>  { constant = new Constant(null, DataTypeManager.DefaultDataClasses.BOOLEAN); }       |
        t=<NULL>     { constant = new Constant(null); }          |
        
        ( <ESCAPEDTYPE> { 
            String image = getToken(0).image; 
        	if (StringUtil.endsWithIgnoreCase(image, "b")) {
        		escapeType=DataTypeManager.DefaultDataClasses.BOOLEAN;
        	} else if (StringUtil.endsWithIgnoreCase(image, "ts")) {
        		escapeType=DataTypeManager.DefaultDataClasses.TIMESTAMP;
        	} else if (StringUtil.endsWithIgnoreCase(image, "d")) {
        		escapeType=DataTypeManager.DefaultDataClasses.DATE;
        	} else {
        		escapeType=DataTypeManager.DefaultDataClasses.TIME;
        	}
		  } strVal=stringVal() { 
	        	try {
	        		constant = new Constant(DataTypeManager.transformValue(strVal, escapeType), escapeType);
	        	} catch (TransformationException e) {
	        		throw new ParseException(e.getMessage()); //$NON-NLS-1$
	        	}
	        } <RBRACE> )
	    | (
	      (<DATE> {escapeType = DataTypeManager.DefaultDataClasses.DATE;}
	       | <TIME> {escapeType = DataTypeManager.DefaultDataClasses.TIME;}
	       | <TIMESTAMP> {escapeType = DataTypeManager.DefaultDataClasses.TIMESTAMP;}
	      )
	      strVal=stringVal() { 
                try {
                    constant = new Constant(DataTypeManager.transformValue(strVal, escapeType), escapeType);
                } catch (TransformationException e) {
                    throw new ParseException(e.getMessage()); //$NON-NLS-1$
                }
            }
	    ) 
	)
	{
		return constant;
	}
}

/*
name=unsigned numeric literal
description=An unsigned numeric literal value.
example=[source,sql]\n----\n1.234\n----\n
*/
Constant unsignedNumericLiteral(String strVal) :
{
    Token t = null;
}
{
		(t=<UNSIGNEDINTEGER> 	{ 
			try { 
			    strVal = prependSign(strVal, t.image);
				// First try as integer - this should nearly always work
				return new Constant(Integer.valueOf(strVal), DataTypeManager.DefaultDataClasses.INTEGER); 				
			} catch(NumberFormatException e1) { 
				try { 
					// Second try as a long
					return new Constant(Long.valueOf(strVal), DataTypeManager.DefaultDataClasses.LONG);
				} catch(NumberFormatException e2) {
					try {
						// Third try as a biginteger
						return new Constant(new BigInteger(strVal), DataTypeManager.DefaultDataClasses.BIG_INTEGER);
					} catch(NumberFormatException e3) {
						Object[] params = new Object[] { strVal };
						throw new ParseException(QueryPlugin.Util.getString("SQLParser.Integer_parse", params)); //$NON-NLS-1$
					}
				}
			}			
		}	| t=<FLOATVAL>	{ 
		    strVal = prependSign(strVal, t.image);
			try { 
				return new Constant(Double.valueOf(strVal), DataTypeManager.DefaultDataClasses.DOUBLE); 
			} catch(NumberFormatException e) {
				Object[] params = new Object[] { strVal };
				throw new ParseException(QueryPlugin.Util.getString("SQLParser.Float_parse", params)); //$NON-NLS-1$
			}
		}  | t=<DECIMALVAL>	{ 
     		strVal = prependSign(strVal, t.image);
			try { 
			    if (DECIMAL_AS_DOUBLE) {
			    	return new Constant(Double.valueOf(strVal), DataTypeManager.DefaultDataClasses.DOUBLE); 
				} else {
			    	return new Constant(new java.math.BigDecimal(strVal), DataTypeManager.DefaultDataClasses.BIG_DECIMAL); 
				}
			} catch(NumberFormatException e) {
				Object[] params = new Object[] { t.image };
				throw new ParseException(QueryPlugin.Util.getString("SQLParser.decimal_parse", params)); //$NON-NLS-1$
			}
		} )
}

/*
unused=true
*/
void parseMetadata(DatabaseStore dbStore) :
{
}
{
    (ddlStmt(dbStore))?
    (<SEMICOLON> [ddlStmt(dbStore)])*
    <EOF>     
}

/*
name=ddl statement
description=A data definition statement.
example=[source,sql]\n----\nCREATE FOREIGN TABLE X (Y STRING)\n----\n
index=true
*/
void ddlStmt(DatabaseStore dbStore) :
{
	Command command = null; 
}
{
	(
	 LOOKAHEAD(3) 	createTable(dbStore) [LOOKAHEAD(3) createTable(dbStore) | createDDLProcedure(dbStore, ParseInfo.DEFAULT_INSTANCE)] | 
	 LOOKAHEAD(2)	createNameSpace(dbStore) |
	 LOOKAHEAD(4)	alterStatement(dbStore) |
	 LOOKAHEAD(4) 	command=createTrigger(ParseInfo.DEFAULT_INSTANCE)
	 {
	 	createDDLTrigger(dbStore, (AlterTrigger)command);
	 } |
	 LOOKAHEAD(2)  createDomain(dbStore) |
	 LOOKAHEAD(3)  createServer(dbStore) |
	 LOOKAHEAD(3)  createRole(dbStore) |
	 LOOKAHEAD(3)  dropRole(dbStore) |
	 LOOKAHEAD(2)  grantOption(dbStore) |
	 LOOKAHEAD(2)  revokeGrantOption(dbStore) | 
	 LOOKAHEAD(3)  dropServer(dbStore) |
	 LOOKAHEAD(3)  dropTableView(dbStore) |
	 LOOKAHEAD(3)  importSchema(dbStore) |
	 LOOKAHEAD(3)  importDatabase(dbStore) |
	 LOOKAHEAD(2)  createDatabase(dbStore) |
	 //LOOKAHEAD(2)  dropDatabase(dbStore) |
	 LOOKAHEAD(2)  useDatabase(dbStore) |
     LOOKAHEAD(2)  dropSchema(dbStore) |
     LOOKAHEAD(2)  useSchema(dbStore) |
     LOOKAHEAD(3)  createSchema(dbStore) |
     LOOKAHEAD(3)  createDDLProcedure(dbStore, ParseInfo.DEFAULT_INSTANCE) [ ddlStmt(dbStore) ] |
     LOOKAHEAD(5)  createDataWrapper(dbStore)|
     LOOKAHEAD(5)  dropDataWrapper(dbStore) |
     LOOKAHEAD(3)  dropProcedure(dbStore)     
	)
}

/*
name=option namespace
description=A namespace used to shorten the full name of an option key.
example=[source,sql]\n----\nSET NAMESPACE 'http://foo' AS foo\n----\n
*/
void createNameSpace(DatabaseStore dbStore) :
{
	String uri = null;
	String prefix = null;
}
{ 
	<SET> <NAMESPACE> uri = stringVal() <AS> prefix = id(Boolean.FALSE)
	{
		dbStore.createNameSpace(prefix, uri);
	}
}

/*
name=create database
description=create a new database
example=[source,sql]\n----\nCREATE DATABASE foo [VERSION 'version'] OPTIONS(...)\n----\n
*/
void createDatabase(DatabaseStore dbStore) :
{
    String name = null;
    Database database = null;
    String version = null;
}
{ 
    <CREATE> <DATABASE> name = id(null) 
    {
        database = new Database(name);
    }
    [<VERSION>
      ( version = stringVal()
        {
            database.setVersion(version);
        }
      )   
    ] 
    [ optionsClause(database, null)
      {
         OptionsUtil.setOptions(database);
      }
    ]
    {
        dbStore.databaseCreated(database);
    }
}

/*
name=use database
description=database into working context
example=[source,sql]\n----\nUSE DATABASE foo [VERSION 'version']\n----\n
*/
void useDatabase(DatabaseStore dbStore) :
{
    String name = null;
    String version = "1";
}
{ 
    <USE> <DATABASE> name = id(null) 
    [<VERSION>(version = stringVal())]
    {
        dbStore.databaseSwitched(name, version);
    }
}

/*
name=drop database
description=drop database 
example=[source,sql]\n----\nDROP DATABASE foo [VERSION 'version']\n----\n
*/
/*void dropDatabase(DatabaseStore dbStore) :
{
    String name = null;
    Database database = null;
    String version = "1";
}
{ 
    <DROP> <DATABASE> name = id(null)
    [<VERSION>(version = stringVal())]     
    {
        dbStore.databaseDropped(name, version);
    }
}*/

/*
name=create schema
description=create a schema in database
example=[source,sql]\n----\nCREATE [VIRTUAL] SCHEMA foo SERVER (s1,s2,s3) OPTIONS(...)\n----\n
*/
void createSchema(DatabaseStore dbStore) :
{
    Schema schema = null;
    String server = null;
    boolean physical = true;
    String name = null;
    ArrayList<String> servers = new ArrayList<String>();
}
{
    <CREATE> [<VIRTUAL> {physical=false;}] <SCHEMA> 
    name = id(null) 
    {
        schema = new Schema();
        schema.setName(name);
        schema.setPhysical(physical);
    }
    [<SERVER> readCSVString(servers)]     
    [ optionsClause(schema, null)
      {
         OptionsUtil.setOptions(schema);
      }
    ]
    /*(
     LOOKAHEAD(2)	createNameSpace(dbStore) |
     LOOKAHEAD(3) 	createTable(dbStore) |
	 LOOKAHEAD(3)   dropTableView(dbStore) |
	 LOOKAHEAD(4)	alterStatement(dbStore) |
	 LOOKAHEAD(4) 	command=createTrigger(ParseInfo.DEFAULT_INSTANCE)
	 {
	 	createDDLTrigger(dbStore, (AlterTrigger)command);
	 } |
	 LOOKAHEAD(2)  grantOption(dbStore) |
	 LOOKAHEAD(2)  revokeGrantOption(dbStore) | 
	 LOOKAHEAD(3)  createDDLProcedure(dbStore, ParseInfo.DEFAULT_INSTANCE) |
	 LOOKAHEAD(3)  importSchema(dbStore)
    )*/
    {        
        dbStore.schemaCreated(schema, servers);
    }
}

/*
name=drop schema
description=drop a schema in database
example=[source,sql]\n----\DROP SCHEMA foo \n----\n
*/
void dropSchema(DatabaseStore dbStore) :
{
    String name = null;
    boolean physical = true;
}
{ 
    <DROP> [<VIRTUAL> {physical=false;}] <SCHEMA> name = id(null) 
    {
        dbStore.schemaDropped(name);
    }
}

/*
name=use schema
description=use schema for following database resources 
example=[source,sql]\n----\nUSE SCHEMA foo \n----\n
*/
void useSchema(DatabaseStore dbStore) :
{
    String name = null;
}
{ 
    <SET> <SCHEMA> (name = id(null) 
    {
        dbStore.schemaSwitched(name);
    })
}

/*
name=create a domain or type alias
description=creates a named type with optional constraints
example=[source,sql]\n----\nCREATE DOMAIN my_type AS INTEGER NOT NULL\n----\n
*/
void createDomain(DatabaseStore dbStore) :
{
    String domainName = null; 
    ParsedDataType type = null;
    boolean notNull = false;
}
{
    <CREATE> <DOMAIN> domainName = id(null) [<AS>] 
    type = parseDataType()
    //-default, and other options
    [<NOT> <NULL> {notNull = true;}]    
    {
        dbStore.createDomain(domainName, type.type, type.length==null?type.precision:type.length, type.scale, notNull);
    }
}

/*
name=create data wrapper
description=Defines a translator; use the options to override the translator properties.
example=[source,sql]\n----\nCREATE FOREIGN (DATA WRAPPER|TRANSLATOR)  wrapper OPTIONS(properties)\n----\n
*/
void createDataWrapper(DatabaseStore dbStore) :
{
    String wrapperName = null; 
    DataWrapper wrapper = null;
    String type = null;
}
{
    <CREATE> <FOREIGN> (<DATA> <WRAPPER>|<TRANSLATOR>) 
    ( wrapperName = id(null)
      {
        wrapper = new DataWrapper(wrapperName);
      } 
    )
    [ <TYPE> (type = id(null) { wrapper.setType(type);}) ]      
    [ optionsClause(wrapper, null)
      {
         OptionsUtil.setOptions(wrapper);
      }
    ]    
    {
        dbStore.dataWrapperCreated(wrapper);
    }
}

/*
name=Drop data wrapper
description=Deletes a translator
example=[source,sql]\n----\nDROP FOREIGN (DATA WRAPPER|TRANSLATOR) wrapper\n----\n
*/
void dropDataWrapper(DatabaseStore dbStore) :
{
    String wrapperName = null;
}
{
    <DROP> <FOREIGN> (<DATA> <WRAPPER>|<TRANSLATOR>)  
    ( wrapperName = id(null)
      {
        dbStore.dataWrapperDropped(wrapperName);
      } 
    )   
}


/*
name=create role 
description=Defines data role for the database
example=[source,sql]\n----\nCREATE DATA ROLE <data-role>  [WITH JAAS ROLE <string>(,<string>)*]\n----\n
*/
void createRole(DatabaseStore dbStore) :
{
    String roleName = null;     
    Role role = null;    
}
{
    <CREATE> <ROLE> 
    ( roleName = id(null)
      {
        role = new Role(roleName);
      }
    )   
    [<WITH> readWith(role) ]
    {
        dbStore.roleCreated(role);
    }
}

/*
name=with role
*/
void readWith(Role role) :
{
    List<String> jaasNames = new ArrayList<String>();
    boolean anyAuthenticated = false;
}
{        
    (<JAAS> <ROLE> readCSVString(jaasNames) | <ANY> <AUTHENTICATED> {anyAuthenticated=true;}) 
    (<WITH>
        (<JAAS> <ROLE> readCSVString(jaasNames) | <ANY> <AUTHENTICATED> {anyAuthenticated=true;})
    )*
    {
        role.setJaasRoles(jaasNames);
        role.setAnyAuthenticated(anyAuthenticated);    
    } 
}

/*
name=drop role 
description=Removes data role for the database
example=[source,sql]\n----\nDROP ROLE <data-role>\n----\n
*/
void dropRole(DatabaseStore dbStore) :
{
    String roleName = null; 
}
{
    <DROP> <ROLE> 
    ( roleName = id(null)
      {
        dbStore.roleDropped(roleName);
      }
    )   
}

/*
name=Create GRANT
description=Defines GRANT for a role
example=[source,sql]\n----\nGRANT SELECT ON TABLE x.y TO role\n----\n
*/
void grantOption(DatabaseStore dbStore) :
{
    Database.ResourceType on= null;
    Grant grant = new Grant();
    String resource = null;
    String mask = null;
    int maskOrder = -1;
    String condition = null;
    boolean isConstraint = false;
    String to = null;
    ArrayList<Grant.Permission.Privilege> privileges = new ArrayList<Grant.Permission.Privilege>();
    Grant.Permission permission = new Grant.Permission();
}
{
    <GRANT> 
    ((
       [readGrantTypes(privileges) (<COMMA> readGrantTypes(privileges))*]
       <ON> ( 
        <TABLE> resource = id(null) {on = Database.ResourceType.TABLE;} 
        	[<CONDITION> [<CONSTRAINT> {isConstraint=true;}] condition = stringVal()] |
        <FUNCTION> resource = id(null) {on = Database.ResourceType.FUNCTION;} |
        <PROCEDURE> resource = id(null) {on = Database.ResourceType.PROCEDURE;} 
        	[<CONDITION> [<CONSTRAINT> {isConstraint=true;}] condition = stringVal()] |
        <SCHEMA> resource = id(null) {on = Database.ResourceType.SCHEMA;} |
        <COLUMN> resource = id(null) {on = Database.ResourceType.COLUMN;} 
            [<MASK> [<ORDER> maskOrder = intVal()] mask = stringVal()] 
       )
      ) 
      |
      (<ALL> <PRIVILEGES> {privileges.add(Grant.Permission.Privilege.ALL_PRIVILEGES);})
      |
      (<TEMPORARY> <TABLE> {privileges.add(Grant.Permission.Privilege.TEMPORARY_TABLE);}) 
      |
      (<USAGE> {privileges.add(Grant.Permission.Privilege.USAGE); on = Database.ResourceType.LANGUAGE;} <ON> <LANGUAGE> resource=id(null))
    )
    <TO>( to = id(null)
        {
            if (privileges.isEmpty() && condition == null && mask == null) {
            	throw new MetadataException(QueryPlugin.Util.getString("SQLParser.empty_grant_revoke"));
            }
            permission.setResourceName(resource);
            permission.setPrivileges(privileges);
            permission.setResourceType(on);            
            if (mask != null) {
                permission.setMask(mask);
                if (maskOrder != -1) {
                    permission.setMaskOrder(maskOrder);
                }
            }
            if(condition != null) {
                permission.setCondition(condition, isConstraint);               
            }
            grant.setRole(to);
            grant.addPermission(permission);
            dbStore.grantCreated(grant);
        } 
    )
}

/*
name=Revoke GRANT
description=Revokes GRANT for a role
example=[source,sql]\n----\nREVOKE SELECT ON TABLE x.y TO role\n----\n
*/
void revokeGrantOption(DatabaseStore dbStore) :
{
    Database.ResourceType on= null;
    Grant grant = new Grant();
    String resource = null;
    String mask = null;
    String condition = null;
    String to = null;
    ArrayList<Grant.Permission.Privilege> privileges = new ArrayList<Grant.Permission.Privilege>();
    Grant.Permission permission = new Grant.Permission();
}
{
    <REVOKE> 
    ((
       [readGrantTypes(privileges) (<COMMA> readGrantTypes(privileges))*]
       <ON> ( 
        <TABLE> resource = id(null) {on = Database.ResourceType.TABLE;} 
        	[<CONDITION> {condition = "";}] |
        <FUNCTION> resource = id(null) {on = Database.ResourceType.FUNCTION;} |
        <PROCEDURE> resource = id(null) {on = Database.ResourceType.PROCEDURE;} 
        	[<CONDITION> {condition = "";} ] |
        <SCHEMA> resource = id(null) {on = Database.ResourceType.SCHEMA;} |
        <COLUMN> resource = id(null) {on = Database.ResourceType.COLUMN;} 
            [<MASK> {mask = "";} ] 
       )
      ) 
      |
      (<ALL> <PRIVILEGES> {privileges.add(Grant.Permission.Privilege.ALL_PRIVILEGES);})
      |
      (<TEMPORARY> <TABLE> {privileges.add(Grant.Permission.Privilege.TEMPORARY_TABLE);}) 
      |
      (<USAGE> {privileges.add(Grant.Permission.Privilege.USAGE); on = Database.ResourceType.LANGUAGE;} <ON> <LANGUAGE> resource=id(null))
    )
    <FROM>( to = id(null)
        {
            if (privileges.isEmpty() && condition == null && mask == null) {
            	throw new MetadataException(QueryPlugin.Util.getString("SQLParser.empty_grant_revoke"));
            }
            permission.setResourceName(resource);
            permission.setRevokePrivileges(privileges);
            permission.setResourceType(on);            
            if (mask != null) {
                permission.setMask(mask);
            }
            if(condition != null) {
                permission.setCondition(condition, null);               
            }
            grant.setRole(to);
            grant.addPermission(permission);
            dbStore.grantRevoked(grant);
        } 
    )
}

/*
name=create server, aka data source
description=Defines connection to foreign source
example=[source,sql]\n----\nCREATE SERVER server_name [ TYPE 'server_type' ] [ VERSION 'server_version' ] FOREIGN (<DATA> <WRAPPER>|<TRANSLATOR>)  fdw_name [ OPTIONS ( option 'value' [, ... ] ) ]\n----\n
*/
void createServer(DatabaseStore dbStore) :
{
    String serverName = null; 
    String type = null;
    String version = null;
    Server server = null;
    String wrapperName = null;
}
{
    <CREATE> <SERVER> 
    ( serverName = id(null)
      {
        server = new Server(serverName);
      }
    )
    [<TYPE>
      ( type = stringVal()
        {
            server.setType(type);
        } 
      )
    ]         
    [<VERSION>
      ( version = stringVal()
        {
            server.setVersion(version);
        }
      )   
    ]    
    <FOREIGN> (<DATA> <WRAPPER>|<TRANSLATOR>) 
    ( wrapperName = id(null)
      {
        server.setDataWrapper(wrapperName);
      } 
    )      
    [ optionsClause(server, null)
      {
         OptionsUtil.setOptions(server);
      }
    ]
    {
        dbStore.serverCreated(server);
    }
}

/*
name=drop server, aka data source
description=Defines dropping connection to foreign source
example=[source,sql]\n----\DROP SERVER server_name\n----\n
*/
void dropServer(DatabaseStore dbStore) :
{
    String serverName = null;
}
{
    <DROP> <SERVER> 
    ( serverName = id(null)
      {
          dbStore.serverDropped(serverName);
      }
    )
}

/*
name=create procedure
description=Defines a procedure or function invocation.
example=[source,sql]\n----\nCREATE FOREIGN PROCEDURE proc (param STRING) RETURNS STRING\n----\n
*/
CreateProcedureCommand createDDLProcedure(DatabaseStore dbStore, ParseInfo info) :
{
	Procedure proc = null;
    CreateProcedureCommand procCmd = new CreateProcedureCommand();
    Statement stmt = null; 	
    ParsedDataType returnDataType = null;
    Token comment = null;    
    Column c = new Column();
    MetadataFactory factory = null;
    String serverName = null;
    FunctionMethod fm = null;
    boolean virtual = true;
    boolean function = false;  
    String procName = null;  
}
{
    <CREATE> [<VIRTUAL> | <FOREIGN> {virtual = false;}](<PROCEDURE> | <FUNCTION> {function = true;})
    procName = id(null)
	//the below is optional because to allow the designer based metadata
	(   {
		    factory = DatabaseStore.createMF(dbStore);
			proc = factory.addProcedure(procName);
		}
	
		<LPAREN>
		[procedureParameter(factory, proc)
		(<COMMA> procedureParameter(factory, proc))*]	
		<RPAREN>
		
		[<RETURNS> [optionsClause(c, factory)] (([<TABLE>] <LPAREN>
	        procedureRsColumn(factory, proc)
	        (<COMMA> procedureRsColumn(factory, proc))*
	    <RPAREN> )
	     |
	     returnDataType = parseDataType())
	    ]
	    [ optionsClause(proc, factory)
		   {
		      OptionsUtil.setOptions(proc);
		   }
		]
		[<AS> { comment = getToken(1).specialToken; } 
		stmt = statement(info)])
		{
			proc.setVirtual(virtual);
			proc.setFunction(function);
			if (stmt != null){
			    if (!virtual) {
			         throw new MetadataException(QueryPlugin.Util.getString("SQLParser.function_def", procName));
			    }
				proc.setQueryPlan((comment != null?comment.image+" ":"") + stmt.toString());
			}
			
	        if (returnDataType != null){
				BaseColumn result = addProcColumn(factory, proc, "return", returnDataType, false);
				result.setProperties(c.getProperties());
	        } else if (proc.getResultSet() != null) {
	            proc.getResultSet().setProperties(c.getProperties());
	        }
	        if (function && proc.getQueryPlan() == null) {
	        	fm = replaceProcedureWithFunction(factory, proc);
	        	if (fm != null) {
	        	  dbStore.functionCreated(fm);
	        	}
	        } else {
	            dbStore.procedureCreated(proc);
	        }
	        return procCmd; 		
		}
}

/*
name=drop procedure
description=Drops a table or view.
example=[source,sql]\n----\nDROP [FOREIGN (TABLE|VIEW) table-name\n----\n
*/
void dropProcedure(DatabaseStore dbStore) :
{
	String procedureName = null;
	boolean virtual = false;
	boolean function = false;
}
{
	<DROP> [<VIRTUAL> | <FOREIGN> {virtual = false;}](<PROCEDURE> | <FUNCTION> {function = true;})
	procedureName = id(null)
	{
	   	if (function){
	   		dbStore.functionDropped(procedureName);
	   	} else {
	   		dbStore.procedureDropped(procedureName);
	    }
	}
}
/*
name=procedure parameter
description=A procedure or function parameter
example=[source,sql]\n----\nOUT x INTEGER\n----\n
*/
void procedureParameter(MetadataFactory factory, Procedure proc) :
{
	String name = null;
	ParsedDataType type = null;
	ProcedureParameter.Type ppType = ProcedureParameter.Type.In;
	ProcedureParameter param = null;
	Expression defaultValue = null;
	boolean notNull = false;
	boolean vararg = false;
}
{
	[LOOKAHEAD(1) ((<IN> {ppType = ProcedureParameter.Type.In ;})
	 |(<OUT> {ppType = ProcedureParameter.Type.Out;})
	 |(<INOUT> {ppType = ProcedureParameter.Type.InOut;})
	 |(<VARIADIC> {ppType = ProcedureParameter.Type.In ; vararg = true;}))]
	name = id(Boolean.TRUE)
	type = parseDataType()
	[<NOT> <NULL> {notNull = true;}]
	[<RESULT>
	  {
	    if (ppType != ProcedureParameter.Type.Out) {
	    	throw new ParseException(QueryPlugin.Util.getString("SQLParser.param_out", proc.getName(), name));
	    }
	    ppType = ProcedureParameter.Type.ReturnValue;
	  }
	]
	{
		param = factory.addProcedureParameter(name, type.type, ppType, proc);
		setTypeInfo(type, param);
		if (notNull) {
			param.setNullType(Column.NullType.No_Nulls);
		}
		if (vararg) {
			param.setVarArg(vararg);
		}			
	}
	[<DEFAULT_KEYWORD> defaultValue = expression(ParseInfo.DEFAULT_INSTANCE) {setDefault(param, defaultValue);}]
	[optionsClause(param, factory)
	   {
	      OptionsUtil.setOptions(param);
	   }
	]	
}

/*
name=procedure result column
description=A procedure result column.
example=[source,sql]\n----\nx INTEGER\n----\n
*/
void procedureRsColumn(MetadataFactory factory, Procedure proc) :
{
	String name = null;
	ParsedDataType type = null;
	boolean notNull = false;
	BaseColumn column = null; 
}
{
      name = id(Boolean.TRUE)
	  type = parseDataType()
	  {
	  	column = addProcColumn(factory, proc, name, type, true);
	  }
	  [<NOT> <NULL> {column.setNullType(Column.NullType.No_Nulls);}]	  
	  [optionsClause(column, factory)
	   {
	      OptionsUtil.setOptions(column);
	   }
	  ]
}

/*
name=create table
description=Defines a table or view.
example=[source,sql]\n----\nCREATE VIEW vw AS SELECT 1\n----\n
*/
void createTable(DatabaseStore dbStore) :
{
	boolean view = false;
	boolean globalTemp = false;
	String tableName = null;
	Table table = null;
	Token comment = null;
	Command query = null;
	MetadataFactory factory = null;
}
{
	<CREATE> (
		(<FOREIGN> <TABLE>) | 
		([<VIRTUAL>] <VIEW> {view = true;}) |
		(<GLOBAL> <TEMPORARY> <TABLE> {globalTemp = true; view = true;})
	) 
	tableName = id(null)
	{	    
	    factory = DatabaseStore.createMF(dbStore);
	    table = factory.addTable(tableName);
	    table.setVirtual(view);
	    if (globalTemp) {
	    	table.setTableType(Table.Type.TemporaryTable);
	    }
	}
	(createTableBody(table, factory) | [optionsClause(table, factory)
	   {
	      OptionsUtil.setOptions(table);
	   }
	])
	[<AS> { comment = getToken(1).specialToken; } query = queryExpression(ParseInfo.DEFAULT_INSTANCE)
	  {
		   if (!view || globalTemp) {
     		   throw new MetadataException(QueryPlugin.Util.getString("SQLParser.view_def", table.getName()));
		   }
		   table.setSelectTransformation((comment != null?comment.image+" ":"") + query.toString());	
	  }
	]
	{
	   dbStore.tableCreated(table);
	}
}

/*
name=drop table
description=Drops a table or view.
example=[source,sql]\n----\nDROP (FOREIGN TABLE | [VIRTUAL] VIEW) table-name\n----\n
*/
void dropTableView(DatabaseStore dbStore) :
{
	boolean view = false;
	boolean globalTemp = false;
	String tableName = null;
}
{
	<DROP> (
		(<FOREIGN> <TABLE>) | 
		([<VIRTUAL>] <VIEW> {view = true;}) |
		(<GLOBAL> <TEMPORARY> <TABLE> {globalTemp = true; view = true;})
	) 
	tableName = id(null)
	{
	   dbStore.tableDropped(tableName);
	}
}

/*
name=create foreign temp table
description=Defines a foreign temp table
example=[source,sql]\n----\nCREATE FOREIGN TEMPORARY TABLE t (x string) ON z\n----\n
*/
Create createForeignTempTable(ParseInfo info) :
{
	Table table = null;
	String tableName = null;
	String model = null;
	MetadataFactory factory = getTempMetadataFactory();
}
{
	<CREATE> [<LOCAL>] <FOREIGN> <TEMPORARY> <TABLE>
	tableName = id(null)
	{
		table = new Table();
		//need to set a dummy/stable uuid
		table.setUUID("tid:0");
		table.setTableType(Table.Type.TemporaryTable);
		table.setName(tableName);
	} 
	createTableBody(table, factory)
	<ON> model = id(null)
	{
		Create create = new Create();
		create.setTableMetadata(table);
		create.setOn(model);
		return create;
	}
}

/*
name=create table body
description=Defines a table.
example=[source,sql]\n----\n(x string) OPTIONS (CARDINALITY 100)\n----\n
*/
void createTableBody(Table table, MetadataFactory factory) :
{
	String name = null;
	AbstractMetadataRecord constraint = null;
	Server server = null;
	String serverName = null;
}
{
	<LPAREN>
    createColumn(factory, table)
    (LOOKAHEAD(3) <COMMA>
      createColumn(factory, table)
    )*	
    (<COMMA>{name=null;} [<CONSTRAINT> name = id(null)] (constraint = primaryKey(factory, table, name) | constraint = constraint(factory, table, name, ParseInfo.DEFAULT_INSTANCE) | constraint = foreignKey(factory, table, name))
       [optionsClause(constraint, factory)
	   {
	      OptionsUtil.setCommonProperties(constraint, constraint.getProperties());
	   }
	]
    )*
    <RPAREN>
    [optionsClause(table, factory)
	   {
	      OptionsUtil.setOptions(table);
	   }
	]
	{
		if (table.getColumns() != null && !table.supportsUpdate()) {
			for (Column c : table.getColumns()) {
				c.setUpdatable(false);
			}
		}
	}
}

/*
name=foreign key
description=Defines the foreign key referential constraint.
example=[source,sql]\n----\nFOREIGN KEY (a, b) REFERENCES tbl (x, y)\n----\n
*/
AbstractMetadataRecord foreignKey(MetadataFactory factory, Table table, String name) :
{
	List<String> columnNames = null;
	String viewName = null;
	Table reference = null;
	KeyRecord pk = null;
	Column column = null;
	List<String> pkColumnNames = null;
	KeyRecord key = null;
}
{
	<FOREIGN> <KEY>
	columnNames = columnList(true, false)
    <REFERENCES>
    viewName = id(null)
    [pkColumnNames = columnList(true, false)]
    {
	    return factory.addForiegnKey(name != null?name:("FK"+table.getForeignKeys().size()), columnNames, pkColumnNames, viewName, table);   
    }
}

/*
name=primary key
description=Defines the primary key.
example=[source,sql]\n----\nPRIMARY KEY (a, b)\n----\n
*/
AbstractMetadataRecord primaryKey(MetadataFactory factory, Table table, String name) :
{
	List<String> columnNames = null;
	Column column = null;
	KeyRecord key = null;
}
{
	<PRIMARY> <KEY>
	columnNames = columnList(true, false)
    {
	    if (table.getPrimaryKey() != null){
	    	throw new MetadataException(QueryPlugin.Util.getString("SQLParser.pk_exists", table.getName()));
	 	}
 		return factory.addPrimaryKey(name!=null?name:"PK", columnNames, table);
    }
}

/*
name=other constraints
description=Defines ACCESSPATTERN and UNIQUE constraints and INDEXes. 
example=[source,sql]\n----\nUNIQUE (a)\n----\n
*/
AbstractMetadataRecord constraint(MetadataFactory factory, Table table, String name, ParseInfo info) :
{
	KeyRecord keyrecord = null;
	Column column = null;
	List<String> columnNames = null;
	Token type = null;
	KeyRecord key = null;
	List<Expression> expressions = null;
}
{
	(( type = <UNIQUE> | type = <ACCESSPATTERN>)
	columnNames = columnList(true, false)	    
	{
		if (type.image.equalsIgnoreCase("UNIQUE")) { 
    		return factory.addIndex(name != null?name:("UNIQUE"+table.getUniqueKeys().size()), false, columnNames, table);
    	} else if (type.image.equalsIgnoreCase("ACCESSPATTERN")) { 
    		return factory.addAccessPattern(name != null?name:("AP"+table.getAccessPatterns().size()), columnNames, table);
    	}
    }) | (
    type = <INDEX>
    <LPAREN>
 	expressions = expressionList(info)
 	<RPAREN>
 	 {
 		return addFBI(factory, expressions, table, name);
 	 }
 	)
}

/*
name=column list
description=A list of column names.
example=[source,sql]\n----\n(a, b)\n----\n
*/
List columnList(boolean validate, boolean asElementSymbols) :
{
	ArrayList list = new ArrayList();
	String id = null;
}
{
	<LPAREN>
	  id = id(validate?Boolean.TRUE:null)
	  {
		if (asElementSymbols) {
			list.add(new ElementSymbol(id));
		} else {
	  		list.add(id);
	  	}
	  }
	  (<COMMA> id = id(validate?Boolean.TRUE:null)
	   {
		if (asElementSymbols) {
			list.add(new ElementSymbol(id));
		} else {
	  		list.add(id);
	  	}
	   }
	  )*
	 <RPAREN> 
	 {
	 	return list;
	 }
}

/*
name=table element
description=Defines a table column.
example=[source,sql]\n----\nx INTEGER NOT NULL\n----\n
*/
void createColumn(MetadataFactory factory, Table table) :
{
	String element = null;
	ParsedDataType type = null;
	boolean autoIncrement = false;
	boolean notNull = false;
	Expression defaultValue = null;
	Column column = null;
	List<String> columnName = new ArrayList<String>();
	boolean index = false;
	boolean unique = false;
	boolean pk = false;
	Token word = null;
}
{
	 element = id(Boolean.TRUE)
	 (
      <SERIAL>
      {
        type = new ParsedDataType("INTEGER");
        autoIncrement = true;
        notNull = true;
      }
      |
      (
	    type = parseDataType()
	    [<NOT> <NULL> { notNull = true; }]
        [<AUTO_INCREMENT> {  autoIncrement = true; }]
	  )
	 )
	 {
       column = factory.addColumn(element, type.type, table);
       column.setUpdatable(true);
       setTypeInfo(type, column);                
       columnName.add(element);
     }
	 ((<PRIMARY> <KEY> { pk = true; }) | ([<UNIQUE> { unique = true;  }] [<INDEX> { index = true; }]))
	 [<DEFAULT_KEYWORD> defaultValue = expression(ParseInfo.DEFAULT_INSTANCE) {setDefault(column, defaultValue);}]
	 [optionsClause(column, factory)
	   {
	      OptionsUtil.setOptions(column);
	   }
	 ]
	 {
	    if (index){
	 		factory.addIndex("INDEX"+table.getIndexes().size(), true, columnName, table);
	 	} else if (unique){
	 		factory.addIndex("UNIQUE"+table.getIndexes().size(), false, columnName, table);
	 	} else if (pk) {
	    	if (table.getPrimaryKey() != null) {
	    		throw new MetadataException(QueryPlugin.Util.getString("SQLParser.pk_exists", table.getName()));
	    	}
	 		factory.addPrimaryKey("PK", columnName, table);
	 	}
	 	if (notNull) {
	       column.setNullType(Column.NullType.No_Nulls);
	    }
	    column.setAutoIncremented(autoIncrement);
	 }
}

/*
name=options clause
description=A list of statement options.
example=[source,sql]\n----\nOPTIONS ('x' 'y', 'a' 'b')\n----\n
*/
void optionsClause(AbstractMetadataRecord record, MetadataFactory factory) :
{
}
{
  <OPTIONS> <LPAREN>
  optionPair(record, factory) (<COMMA> optionPair(record, factory))*	   
  <RPAREN> 
}

/*
name=option pair
description=An option key/value pair.
example=[source,sql]\n----\n'key' 'value'\n----\n
*/
void optionPair(AbstractMetadataRecord record, MetadataFactory factory) :
{
	Constant value = null;
	String key = null;
	Token t = null;
	String strVal = null;
}
{
	 key = id(null)
	 (value = nonNumericLiteral()
	 | [strVal = plusMinus()] value = unsignedNumericLiteral(strVal))
	 { 
	 	if (factory != null){
            key = MetadataFactory.resolvePropertyKey(factory, key);
	 	}
	 	String val = null;
	 	if (!value.isNull()) {
	 		val = value.getValue().toString();
	 	}
	 	record.setProperty(key, val); 
	 } 
}

/*
name=alter option pair
description=Alter An option key/value pair.
example=[source,sql]\n----\n'key' 'value'\n----\n
*/
void alterOptionPair(String objectName, Database.ResourceType type, DatabaseStore dbStore) :
{
    Constant value = null;
    String key = null;
    Token t = null;
    String strVal = null;
}
{
     key = id(null)
     (value = nonNumericLiteral()
     | [strVal = plusMinus()] value = unsignedNumericLiteral(strVal))
     { 
        String val = null;
        if (!value.isNull()) {
            val = value.getValue().toString();
        }
        dbStore.addOrSetOption(objectName, type, key, val, true);
     } 
}


void alterStatement(DatabaseStore dbStore) :
{
    boolean virtual = false;
}
{
    <ALTER> [<VIRTUAL> {virtual = true;} | <FOREIGN>]
    (
        LOOKAHEAD(3)
        alterTable(dbStore, virtual, ParseInfo.DEFAULT_INSTANCE) |
        alterProcedure(dbStore, virtual, ParseInfo.DEFAULT_INSTANCE) |
        alterTrigger(dbStore, ParseInfo.DEFAULT_INSTANCE) |
        alterServer(dbStore) |
        alterTranslator(dbStore) |
        alterDatabase(dbStore)
    )
}

/*
name=ALTER TABLE 
description=alters options of database
example=[source,sql]\n----\nALTER TABLE foo (ADD|DROP|ALTER) COLUMN <name> <type> OPTIONS ( (ADD|SET|DROP) x y)\n----\n
*/
void alterTable(DatabaseStore dbStore, boolean virtual, ParseInfo info) :
{
    String objectName = null;
    QueryCommand command = null;
    Token comment = null;
    Table table = null;
}
{
    ((<VIEW> objectName = id(null)
        {
      		table = (Table)dbStore.getSchemaRecord(objectName, Database.ResourceType.TABLE);
        }
	    <AS> { comment = getToken(1).specialToken; } command = queryExpression(info)
	    {
	        if (comment != null) {
	            command.setCacheHint(getQueryCacheOption(comment.image));
	        }
	        dbStore.setViewDefinition(table.getName(), command.toString(), true);        
	    }
	    [LOOKAHEAD(2) (alterOptionsList(objectName, Database.ResourceType.TABLE, dbStore) |
	    	alterColumn(objectName, Database.ResourceType.TABLE, dbStore))
	    ]
	 ) | (
	 	<TABLE> objectName = id(null)
	 	{
	 		if (virtual) {
	            throw new org.teiid.metadata.MetadataException(QueryPlugin.Event.TEIID31238,
	                    QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31238));                                    	 		
	 		}
	 	}
	    [LOOKAHEAD(2) (alterOptionsList(objectName, Database.ResourceType.TABLE, dbStore) | 
	    	alterAddColumn(objectName, Database.ResourceType.TABLE, dbStore) |
	    	alterDropColumn(objectName, Database.ResourceType.TABLE, dbStore) |
	    	alterColumn(objectName, Database.ResourceType.TABLE, dbStore) |
	    	alterRenameColumn(objectName, Database.ResourceType.TABLE, dbStore))
	    ]	 	
	 ))    
}

/*
name=ADD column
description=alters table and adds a column
example=[source,sql]\n----\nADD COLUMN bar type OPTIONS (ADD updatable true)\n----\n
*/
void alterAddColumn(String objectName, Database.ResourceType type, DatabaseStore dbStore) :
{
	Database.ResourceType childType = Database.ResourceType.COLUMN;
	MetadataFactory factory = null;
	Table table = null;
}
{ 
	<ADD> <COLUMN>
	{
		factory = DatabaseStore.createMF(dbStore);
		table = (Table)dbStore.getSchemaRecord(objectName, type); 
	}
	createColumn(factory, table)
	{
		dbStore.tableModified(table);
	}
}

/*
name=DROP column
description=alters table and adds a column
example=[source,sql]\n----\DROP COLUMN bar\n----\n
*/
void alterDropColumn(String objectName, Database.ResourceType type, DatabaseStore dbStore) :
{
	MetadataFactory factory = null;
	Table table = null;
	String childName = null;
	Column column = null;
}
{ 
	<DROP> <COLUMN>
	childName = id(null)
	{
		factory = DatabaseStore.createMF(dbStore);
		table = (Table)dbStore.getSchemaRecord(objectName, type);
		column = table.getColumnByName(childName);
		if (column == null) {
			throw new org.teiid.metadata.MetadataException(QueryPlugin.Event.TEIID31223,
            	QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31223, childName));		
		}
		table.removeColumn(column);
		dbStore.tableModified(table); 
	}
}

/*
name=alter column options
description=alters a set of column options
example=[source,sql]\n----\nALTER COLUMN bar OPTIONS (ADD updatable true)\n----\n
*/
void alterColumn(String objectName, Database.ResourceType type, DatabaseStore dbStore) :
{
	String childName = null;
	Database.ResourceType childType = Database.ResourceType.COLUMN;
    ParsedDataType datatype = null;
    boolean autoIncrement = false;
    boolean notNull = false;
    BaseColumn column = null;
    Table table = null;
    Procedure proc = null;
    MetadataFactory factory = null;
}
{ 
	<ALTER> (<COLUMN>|<PARAMETER>{childType = Database.ResourceType.PARAMETER;})
	childName = id(null)
	(<TYPE>
     (
      <SERIAL>
      {
        datatype = new ParsedDataType("INTEGER");
        autoIncrement = true;
        notNull = true;
      }
      |
      (
        datatype = parseDataType()
        [<NOT> <NULL> { notNull = true; }]
        [<AUTO_INCREMENT> {  autoIncrement = true; }]
      )
     )
     {
        factory = DatabaseStore.createMF(dbStore);
        if (childType == Database.ResourceType.COLUMN){
            table = (Table)dbStore.getSchemaRecord(objectName, type); 
            column = table.getColumnByName(childName);
            if (column == null){
                throw new ParseException(QueryPlugin.Util.getString("SQLParser.no_table_column_found", childName, table.getName())); //$NON-NLS-1$
            }
            ((Column)column).setAutoIncremented(autoIncrement);
        } else {
            proc = (Procedure)dbStore.getSchemaRecord(objectName, type);
            column = proc.getParameterByName(childName);
            if (column == null){
                throw new ParseException(QueryPlugin.Util.getString("SQLParser.no_proc_column_found", childName, proc.getName())); //$NON-NLS-1$
            }            
        }
        factory.setDataType(datatype.type, column, factory.getDataTypes(), notNull);
        setTypeInfo(datatype, column);
        if (notNull) {
           column.setNullType(Column.NullType.No_Nulls);
        }
        if (childType == Database.ResourceType.COLUMN) {
            ((Column)column).setAutoIncremented(autoIncrement);
        }        
     }	
	|
	alterChildOptionsList(objectName, type, childName, childType, dbStore))
}

/*
name=rename column options
description=renames either a table column or procedure's parameter name
example=[source,sql]\n----\nRENAME COLUMN bar TO foo\n----\n
*/
void alterRenameColumn(String objectName, Database.ResourceType type, DatabaseStore dbStore) :
{
    String oldName = null;
    String newName = null;
    Database.ResourceType childType = Database.ResourceType.COLUMN;
    Table table = null;
    BaseColumn column = null;
    Procedure proc = null;
    MetadataFactory factory = null;
}
{ 
    <RENAME> (<COLUMN>|<PARAMETER>{childType = Database.ResourceType.PARAMETER;})
    oldName = id(null)
    <TO>
    newName = id(null)
    {
        factory = DatabaseStore.createMF(dbStore);
        if (childType == Database.ResourceType.COLUMN){
            table = (Table)dbStore.getSchemaRecord(objectName, type); 
            column = table.getColumnByName(oldName);
            if (column == null){
                throw new ParseException(QueryPlugin.Util.getString("SQLParser.no_table_column_found", oldName, table.getName())); //$NON-NLS-1$
            }
            factory.renameColumn(oldName, newName, table);
        } else {
            proc = (Procedure)dbStore.getSchemaRecord(objectName, type);
            column = proc.getParameterByName(oldName);
            if (column == null){
                throw new ParseException(QueryPlugin.Util.getString("SQLParser.no_proc_column_found", oldName, proc.getName())); //$NON-NLS-1$
            }
            column.setName(newName);            
        } 
    }
}

/*
name=ALTER PROCEDURE 
description=alters options of database
example=[source,sql]\n----\nALTER PROCEDURE foo [AS <stmt>] OPTIONS (ADD x y)\n----\n
*/
void alterProcedure(DatabaseStore dbStore, boolean virtual, ParseInfo info) :
{
    String objectName = null;
    Statement stmt = null;
    Token comment = null;
    Procedure procedure = null;
}
{
    <PROCEDURE> objectName = id(null)
    {
    	procedure = (Procedure)dbStore.getSchemaRecord(objectName, Database.ResourceType.PROCEDURE);
    }    
    [
        <AS> { comment = getToken(1).specialToken; } stmt = statement(info)
        {
            CreateProcedureCommand cup = new CreateProcedureCommand(asBlock(stmt));
            if (comment != null) {
                cup.setCacheHint(getQueryCacheOption(comment.image));
            }
            dbStore.setProcedureDefinition(procedure.getName(), cup.toString(), true);
        }
    ]     
    [LOOKAHEAD(2) (alterOptionsList(objectName, Database.ResourceType.PROCEDURE, dbStore) |   
    	alterColumn(objectName, Database.ResourceType.PROCEDURE, dbStore)   |
    	alterRenameColumn(objectName, Database.ResourceType.PROCEDURE, dbStore))]     
}

/*
name=ALTER TRIGGER 
description=alters options of table triggers
example=[source,sql]\n----\nALTER TRIGGER ON <id> INSTEAD OF (INSERT|UPDATE|DELETE) AS [ENABLED|DISABLED]\n----\n
*/
void alterTrigger(DatabaseStore dbStore, ParseInfo info) :
{
    String objectName = null;
    Boolean enabled = null;
    Token event = null;
    TriggerAction triggerAction = null;
}
{
    <TRIGGER> <ON> objectName = id(null) <INSTEAD> <OF> (event = <INSERT> | event = <UPDATE> | event = <DELETE>) 
    (<AS> triggerAction = forEachRowTriggerAction(info) | <ENABLED> {enabled=true;} | <DISABLED> {enabled=false;})
    {
        if (enabled != null){
            dbStore.enableTableTriggerPlan(objectName, Table.TriggerEvent.valueOf(event.image.toUpperCase()), enabled, true);
        } else {
            dbStore.setTableTriggerPlan(null, objectName, Table.TriggerEvent.valueOf(event.image.toUpperCase()), triggerAction.toString(), false);
        }                
    }
}

/*
name=ALTER SERVER 
description=alters options of database
example=[source,sql]\n----\nALTER SERVER foo OPTIONS (ADD x y)\n----\n
*/
void alterServer(DatabaseStore dbStore) :
{
    String objectName = null;
}
{
    <SERVER> objectName = id(null)
    (alterOptionsList(objectName, Database.ResourceType.SERVER, dbStore))    
}

/*
name=ALTER DATA WRAPPER 
description=alters options of data wrapper
example=[source,sql]\n----\nALTER [DATA WRAPPER|TRANSLATOR] foo OPTIONS (ADD x y)\n----\n
*/
void alterTranslator(DatabaseStore dbStore) :
{
    String objectName = null;
}
{
    (<DATA> <WRAPPER>|<TRANSLATOR>) objectName = id(null)
    (alterOptionsList(objectName, Database.ResourceType.DATAWRAPPER, dbStore))    
}

/*
name=ALTER DATABASE 
description=alters options of database
example=[source,sql]\n----\nALTER DATABASE foo OPTIONS (ADD x y)\n----\n
*/
void alterDatabase(DatabaseStore dbStore) :
{
    String objectName = null;
}
{
    <DATABASE> objectName = id(null)
    (alterOptionsList(objectName, Database.ResourceType.DATABASE, dbStore))    
}

/*
name=alter options list
description=a list of alterations to options
example=[source,sql]\n----\nOPTIONS (ADD updatable true)\n----\n
*/
void alterOptionsList(String objectName, Database.ResourceType type, DatabaseStore dbStore) :
{

}
{
  <OPTIONS> <LPAREN>
  (addSetOption(objectName, type, dbStore) |
   dropOption(objectName, type, dbStore))
  (<COMMA> (addSetOption(objectName, type, dbStore) |
   dropOption(objectName, type, dbStore)))*
  <RPAREN>
}

/*
name=drop option
description=drop option
example=[source,sql]\n----\nDROP updatable\n----\n
*/
void dropOption(String objectName, Database.ResourceType type, DatabaseStore dbStore) :
{
	String key = null;
}
{
   <DROP> key = id(null) 
   {
 		dbStore.removeOption(objectName, type, key);
   }
}

/*
name=add set option
description=add or set an option pair
example=[source,sql]\n----\nADD updatable true\n----\n
*/
void addSetOption(String objectName, Database.ResourceType type, DatabaseStore dbStore) :
{
}
{
   (<ADD>|<SET>) alterOptionPair(objectName, type, dbStore) 
}

/*
name=alter child options list
description=a list of alterations to options
example=[source,sql]\n----\nOPTIONS (ADD updatable true)\n----\n
*/
void alterChildOptionsList(String objectName, Database.ResourceType type, String childName, Database.ResourceType childType, DatabaseStore dbStore) :
{

}
{
  <OPTIONS> <LPAREN>
  (addSetChildOption(objectName, type, childName, childType, dbStore) |
   dropChildOption(objectName, type, childName, childType, dbStore))
  (<COMMA> (addSetChildOption(objectName, type, childName, childType, dbStore) |
   dropChildOption(objectName, type, childName, childType, dbStore)))*
  <RPAREN>
}

/*
name=drop option
description=drop option
example=[source,sql]\n----\nDROP updatable\n----\n
*/
void dropChildOption(String objectName, Database.ResourceType type, String childName, Database.ResourceType childType, DatabaseStore dbStore) :
{
    String key = null;
}
{
   <DROP> key = id(null) 
   {
        dbStore.removeOption(objectName, type, childName, childType, key);
   }
}

/*
name=add set child option
description=add or set an option pair
example=[source,sql]\n----\nADD updatable true\n----\n
*/
void addSetChildOption(String objectName, Database.ResourceType type, String childName, Database.ResourceType childType, DatabaseStore dbStore) :
{
}
{
   (<ADD>|<SET>) alterChildOptionPair(objectName, type, childName, childType, dbStore) 
}

/*
name=alter child option pair
description=Alter An option key/value pair.
example=[source,sql]\n----\n'key' 'value'\n----\n
*/
void alterChildOptionPair(String objectName, Database.ResourceType type, String childName, Database.ResourceType childType, DatabaseStore dbStore) :
{
    Constant value = null;
    String key = null;
    Token t = null;
    String strVal = null;
}
{
     key = id(null)
     (value = nonNumericLiteral()
     | [strVal = plusMinus()] value = unsignedNumericLiteral(strVal))
     { 
        String val = null;
        if (!value.isNull()) {
            val = value.getValue().toString();
        }
        dbStore.addOrSetOption(objectName, type, childName, childType, key, val, true);
     } 
}

/*
name=Import foreign schema
description=imports schema metadata from server
example=[source,sql]\n----\nIMPORT FOREIGN SCHEMA foo [LIMIT TO (x,y,z)|EXCEPT (x,y,z)] FROM SERVER bar\n----\n
*/
void importSchema(DatabaseStore dbStore) :
{
    String foreignSchemaName = null;
    String serverType = null;
    String schemaName = null;
    String serverName = null;
    String importIntoSchemaName = null;
    ArrayList<String> includeTables = new ArrayList<String>();
    ArrayList<String> excludeTables = new ArrayList<String>();
    Server server = new Server("__temp__"); 
}
{ 
    <IMPORT> <FOREIGN> <SCHEMA> 
    foreignSchemaName = id(null)
    //[(<LIMIT> <TO> <LPAREN> readCSVString(includeTables) <RPAREN>) | (<EXCEPT> <LPAREN> readCSVString(excludeTables) <RPAREN>)]
    <FROM> (<SERVER>{serverType = "native";}|<REPOSITORY>) serverName = id(null)    
    <INTO> schemaName = id(null)
    [optionsClause(server, null)
	   {
	      OptionsUtil.setOptions(server);
	   }
	]
    {
        if (serverType == null){
            serverType = serverName;
            serverName = null;
        }
        dbStore.importSchema(schemaName, serverType, serverName, foreignSchemaName, includeTables, excludeTables, server.getProperties());
    }	
}

/*
name=Import another Database
description=imports another database into current database
example=[source,sql]\n----\nIMPORT DATABASE <id> VERSION <string-val> [WITH ACCESS CONTROL]\n----\n
*/
void importDatabase(DatabaseStore dbStore) :
{
    String dbName = null;
    String version = null;
    boolean importAccessControls = false;
}
{ 
    <IMPORT> <DATABASE> 
    dbName = id(null)
    <VERSION>
    version = stringVal()
    [<WITH> <ACCESS> <CONTROL> {importAccessControls=true;}]    
    {
        dbStore.importDatabase(dbName, version, importAccessControls);
    }
}

/*
name=identifier list
*/
void readCSVString(List<String> strings) :
{
    String name = null;
}
{        
    name = id(null)
    {
        strings.add(name);
    } 
    (<COMMA>
        name = id(null)
        {
            strings.add(name);
        }
    )* 
}

/*
name=grant type
*/
void readGrantTypes(ArrayList<Grant.Permission.Privilege> privileges) :
{
}
{        
    <SELECT> {privileges.add(Grant.Permission.Privilege.SELECT);} |
    <INSERT> {privileges.add(Grant.Permission.Privilege.INSERT);} |
    <UPDATE> {privileges.add(Grant.Permission.Privilege.UPDATE);} |
    <DELETE> {privileges.add(Grant.Permission.Privilege.DELETE);} |
    <EXECUTE> {privileges.add(Grant.Permission.Privilege.EXECUTE);} |
    <ALTER> {privileges.add(Grant.Permission.Privilege.ALTER);} |
    <DROP> {privileges.add(Grant.Permission.Privilege.DROP);}
}