Merge branch 'cassandra-3.0' into cassandra-3.X
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/206a7bba Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/206a7bba Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/206a7bba Branch: refs/heads/trunk Commit: 206a7bbac70d8ad5f27eff7b22ccfc8ab703a31b Parents: 771a1a2 153583b Author: Carl Yeksigian <c...@apache.org> Authored: Fri Oct 14 10:28:58 2016 -0400 Committer: Carl Yeksigian <c...@apache.org> Committed: Fri Oct 14 10:28:58 2016 -0400 ---------------------------------------------------------------------- CHANGES.txt | 1 + src/antlr/Lexer.g | 2 + src/antlr/Parser.g | 2 +- .../apache/cassandra/cql3/ColumnIdentifier.java | 2 +- .../apache/cassandra/cql3/ReservedKeywords.java | 118 +++++++++++++++++++ .../org/apache/cassandra/cql3/ViewTest.java | 20 ++++ .../db/ColumnFamilyStoreCQLHelperTest.java | 4 +- 7 files changed, 145 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/206a7bba/CHANGES.txt ---------------------------------------------------------------------- diff --cc CHANGES.txt index bc5dcd1,e82eedd..0045ba5 --- a/CHANGES.txt +++ b/CHANGES.txt @@@ -1,85 -1,5 +1,86 @@@ -3.0.10 +3.10 + * cqlsh fails to format collections when using aliases (CASSANDRA-11534) + * Check for hash conflicts in prepared statements (CASSANDRA-12733) + * Exit query parsing upon first error (CASSANDRA-12598) + * Fix cassandra-stress to use single seed in UUID generation (CASSANDRA-12729) + * CQLSSTableWriter does not allow Update statement (CASSANDRA-12450) + * Config class uses boxed types but DD exposes primitive types (CASSANDRA-12199) + * Add pre- and post-shutdown hooks to Storage Service (CASSANDRA-12461) + * Add hint delivery metrics (CASSANDRA-12693) + * Remove IndexInfo cache from FileIndexInfoRetriever (CASSANDRA-12731) + * ColumnIndex does not reuse buffer (CASSANDRA-12502) + * cdc column addition still breaks schema migration tasks (CASSANDRA-12697) + * Upgrade metrics-reporter dependencies (CASSANDRA-12089) + * Tune compaction thread count via nodetool (CASSANDRA-12248) + * Add +=/-= shortcut syntax for update queries (CASSANDRA-12232) + * Include repair session IDs in repair start message (CASSANDRA-12532) + * Add a blocking task to Index, run before joining the ring (CASSANDRA-12039) + * Fix NPE when using CQLSSTableWriter (CASSANDRA-12667) + * Support optional backpressure strategies at the coordinator (CASSANDRA-9318) + * Make randompartitioner work with new vnode allocation (CASSANDRA-12647) + * Fix cassandra-stress graphing (CASSANDRA-12237) + * Allow filtering on partition key columns for queries without secondary indexes (CASSANDRA-11031) + * Fix Cassandra Stress reporting thread model and precision (CASSANDRA-12585) + * Add JMH benchmarks.jar (CASSANDRA-12586) + * Add row offset support to SASI (CASSANDRA-11990) + * Cleanup uses of AlterTableStatementColumn (CASSANDRA-12567) + * Add keep-alive to streaming (CASSANDRA-11841) + * Tracing payload is passed through newSession(..) (CASSANDRA-11706) + * avoid deleting non existing sstable files and improve related log messages (CASSANDRA-12261) + * json/yaml output format for nodetool compactionhistory (CASSANDRA-12486) + * Retry all internode messages once after a connection is + closed and reopened (CASSANDRA-12192) + * Add support to rebuild from targeted replica (CASSANDRA-9875) + * Add sequence distribution type to cassandra stress (CASSANDRA-12490) + * "SELECT * FROM foo LIMIT ;" does not error out (CASSANDRA-12154) + * Define executeLocally() at the ReadQuery Level (CASSANDRA-12474) + * Extend read/write failure messages with a map of replica addresses + to error codes in the v5 native protocol (CASSANDRA-12311) + * Fix rebuild of SASI indexes with existing index files (CASSANDRA-12374) + * Let DatabaseDescriptor not implicitly startup services (CASSANDRA-9054, 12550) + * Fix clustering indexes in presence of static columns in SASI (CASSANDRA-12378) + * Fix queries on columns with reversed type on SASI indexes (CASSANDRA-12223) + * Added slow query log (CASSANDRA-12403) + * Count full coordinated request against timeout (CASSANDRA-12256) + * Allow TTL with null value on insert and update (CASSANDRA-12216) + * Make decommission operation resumable (CASSANDRA-12008) + * Add support to one-way targeted repair (CASSANDRA-9876) + * Remove clientutil jar (CASSANDRA-11635) + * Fix compaction throughput throttle (CASSANDRA-12366, CASSANDRA-12717) + * Delay releasing Memtable memory on flush until PostFlush has finished running (CASSANDRA-12358) + * Cassandra stress should dump all setting on startup (CASSANDRA-11914) + * Make it possible to compact a given token range (CASSANDRA-10643) + * Allow updating DynamicEndpointSnitch properties via JMX (CASSANDRA-12179) + * Collect metrics on queries by consistency level (CASSANDRA-7384) + * Add support for GROUP BY to SELECT statement (CASSANDRA-10707) + * Deprecate memtable_cleanup_threshold and update default for memtable_flush_writers (CASSANDRA-12228) + * Upgrade to OHC 0.4.4 (CASSANDRA-12133) + * Add version command to cassandra-stress (CASSANDRA-12258) + * Create compaction-stress tool (CASSANDRA-11844) + * Garbage-collecting compaction operation and schema option (CASSANDRA-7019) + * Add beta protocol flag for v5 native protocol (CASSANDRA-12142) + * Support filtering on non-PRIMARY KEY columns in the CREATE + MATERIALIZED VIEW statement's WHERE clause (CASSANDRA-10368) + * Unify STDOUT and SYSTEMLOG logback format (CASSANDRA-12004) + * COPY FROM should raise error for non-existing input files (CASSANDRA-12174) + * Faster write path (CASSANDRA-12269) + * Option to leave omitted columns in INSERT JSON unset (CASSANDRA-11424) + * Support json/yaml output in nodetool tpstats (CASSANDRA-12035) + * Expose metrics for successful/failed authentication attempts (CASSANDRA-10635) + * Prepend snapshot name with "truncated" or "dropped" when a snapshot + is taken before truncating or dropping a table (CASSANDRA-12178) + * Optimize RestrictionSet (CASSANDRA-12153) + * cqlsh does not automatically downgrade CQL version (CASSANDRA-12150) + * Omit (de)serialization of state variable in UDAs (CASSANDRA-9613) + * Create a system table to expose prepared statements (CASSANDRA-8831) + * Reuse DataOutputBuffer from ColumnIndex (CASSANDRA-11970) + * Remove DatabaseDescriptor dependency from SegmentedFile (CASSANDRA-11580) + * Add supplied username to authentication error messages (CASSANDRA-12076) + * Remove pre-startup check for open JMX port (CASSANDRA-12074) + * Remove compaction Severity from DynamicEndpointSnitch (CASSANDRA-11738) + * Restore resumable hints delivery (CASSANDRA-11960) +Merged from 3.0: + * Preserve quoted reserved keyword column names in MV creation (CASSANDRA-11803) * nodetool stopdaemon errors out (CASSANDRA-12646) * Split materialized view mutations on build to prevent OOM (CASSANDRA-12268) * mx4j does not work in 3.0.8 (CASSANDRA-12274) http://git-wip-us.apache.org/repos/asf/cassandra/blob/206a7bba/src/antlr/Lexer.g ---------------------------------------------------------------------- diff --cc src/antlr/Lexer.g index 0d79520,0000000..cbbfd6d mode 100644,000000..100644 --- a/src/antlr/Lexer.g +++ b/src/antlr/Lexer.g @@@ -1,326 -1,0 +1,328 @@@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +lexer grammar Lexer; + +@lexer::members { + List<Token> tokens = new ArrayList<Token>(); + + public void emit(Token token) + { + state.token = token; + tokens.add(token); + } + + public Token nextToken() + { + super.nextToken(); + if (tokens.size() == 0) + return new CommonToken(Token.EOF); + return tokens.remove(0); + } + + private final List<ErrorListener> listeners = new ArrayList<ErrorListener>(); + + public void addErrorListener(ErrorListener listener) + { + this.listeners.add(listener); + } + + public void removeErrorListener(ErrorListener listener) + { + this.listeners.remove(listener); + } + + public void displayRecognitionError(String[] tokenNames, RecognitionException e) + { + for (int i = 0, m = listeners.size(); i < m; i++) + listeners.get(i).syntaxError(this, tokenNames, e); + } +} + +// Case-insensitive keywords ++// When adding a new reserved keyword, add entry to o.a.c.cql3.ReservedKeywords as well ++// When adding a new unreserved keyword, add entry to unreserved keywords in Parser.g +K_SELECT: S E L E C T; +K_FROM: F R O M; +K_AS: A S; +K_WHERE: W H E R E; +K_AND: A N D; +K_KEY: K E Y; +K_KEYS: K E Y S; +K_ENTRIES: E N T R I E S; +K_FULL: F U L L; +K_INSERT: I N S E R T; +K_UPDATE: U P D A T E; +K_WITH: W I T H; +K_LIMIT: L I M I T; +K_PER: P E R; +K_PARTITION: P A R T I T I O N; +K_USING: U S I N G; +K_USE: U S E; +K_DISTINCT: D I S T I N C T; +K_COUNT: C O U N T; +K_SET: S E T; +K_BEGIN: B E G I N; +K_UNLOGGED: U N L O G G E D; +K_BATCH: B A T C H; +K_APPLY: A P P L Y; +K_TRUNCATE: T R U N C A T E; +K_DELETE: D E L E T E; +K_IN: I N; +K_CREATE: C R E A T E; +K_KEYSPACE: ( K E Y S P A C E + | S C H E M A ); +K_KEYSPACES: K E Y S P A C E S; +K_COLUMNFAMILY:( C O L U M N F A M I L Y + | T A B L E ); +K_MATERIALIZED:M A T E R I A L I Z E D; +K_VIEW: V I E W; +K_INDEX: I N D E X; +K_CUSTOM: C U S T O M; +K_ON: O N; +K_TO: T O; +K_DROP: D R O P; +K_PRIMARY: P R I M A R Y; +K_INTO: I N T O; +K_VALUES: V A L U E S; +K_TIMESTAMP: T I M E S T A M P; +K_TTL: T T L; +K_CAST: C A S T; +K_ALTER: A L T E R; +K_RENAME: R E N A M E; +K_ADD: A D D; +K_TYPE: T Y P E; +K_COMPACT: C O M P A C T; +K_STORAGE: S T O R A G E; +K_ORDER: O R D E R; +K_BY: B Y; +K_ASC: A S C; +K_DESC: D E S C; +K_ALLOW: A L L O W; +K_FILTERING: F I L T E R I N G; +K_IF: I F; +K_IS: I S; +K_CONTAINS: C O N T A I N S; +K_GROUP: G R O U P; + +K_GRANT: G R A N T; +K_ALL: A L L; +K_PERMISSION: P E R M I S S I O N; +K_PERMISSIONS: P E R M I S S I O N S; +K_OF: O F; +K_REVOKE: R E V O K E; +K_MODIFY: M O D I F Y; +K_AUTHORIZE: A U T H O R I Z E; +K_DESCRIBE: D E S C R I B E; +K_EXECUTE: E X E C U T E; +K_NORECURSIVE: N O R E C U R S I V E; +K_MBEAN: M B E A N; +K_MBEANS: M B E A N S; + +K_USER: U S E R; +K_USERS: U S E R S; +K_ROLE: R O L E; +K_ROLES: R O L E S; +K_SUPERUSER: S U P E R U S E R; +K_NOSUPERUSER: N O S U P E R U S E R; +K_PASSWORD: P A S S W O R D; +K_LOGIN: L O G I N; +K_NOLOGIN: N O L O G I N; +K_OPTIONS: O P T I O N S; + +K_CLUSTERING: C L U S T E R I N G; +K_ASCII: A S C I I; +K_BIGINT: B I G I N T; +K_BLOB: B L O B; +K_BOOLEAN: B O O L E A N; +K_COUNTER: C O U N T E R; +K_DECIMAL: D E C I M A L; +K_DOUBLE: D O U B L E; +K_FLOAT: F L O A T; +K_INET: I N E T; +K_INT: I N T; +K_SMALLINT: S M A L L I N T; +K_TINYINT: T I N Y I N T; +K_TEXT: T E X T; +K_UUID: U U I D; +K_VARCHAR: V A R C H A R; +K_VARINT: V A R I N T; +K_TIMEUUID: T I M E U U I D; +K_TOKEN: T O K E N; +K_WRITETIME: W R I T E T I M E; +K_DATE: D A T E; +K_TIME: T I M E; + +K_NULL: N U L L; +K_NOT: N O T; +K_EXISTS: E X I S T S; + +K_MAP: M A P; +K_LIST: L I S T; +K_NAN: N A N; +K_INFINITY: I N F I N I T Y; +K_TUPLE: T U P L E; + +K_TRIGGER: T R I G G E R; +K_STATIC: S T A T I C; +K_FROZEN: F R O Z E N; + +K_FUNCTION: F U N C T I O N; +K_FUNCTIONS: F U N C T I O N S; +K_AGGREGATE: A G G R E G A T E; +K_SFUNC: S F U N C; +K_STYPE: S T Y P E; +K_FINALFUNC: F I N A L F U N C; +K_INITCOND: I N I T C O N D; +K_RETURNS: R E T U R N S; +K_CALLED: C A L L E D; +K_INPUT: I N P U T; +K_LANGUAGE: L A N G U A G E; +K_OR: O R; +K_REPLACE: R E P L A C E; + +K_JSON: J S O N; +K_DEFAULT: D E F A U L T; +K_UNSET: U N S E T; +K_LIKE: L I K E; + +// Case-insensitive alpha characters +fragment A: ('a'|'A'); +fragment B: ('b'|'B'); +fragment C: ('c'|'C'); +fragment D: ('d'|'D'); +fragment E: ('e'|'E'); +fragment F: ('f'|'F'); +fragment G: ('g'|'G'); +fragment H: ('h'|'H'); +fragment I: ('i'|'I'); +fragment J: ('j'|'J'); +fragment K: ('k'|'K'); +fragment L: ('l'|'L'); +fragment M: ('m'|'M'); +fragment N: ('n'|'N'); +fragment O: ('o'|'O'); +fragment P: ('p'|'P'); +fragment Q: ('q'|'Q'); +fragment R: ('r'|'R'); +fragment S: ('s'|'S'); +fragment T: ('t'|'T'); +fragment U: ('u'|'U'); +fragment V: ('v'|'V'); +fragment W: ('w'|'W'); +fragment X: ('x'|'X'); +fragment Y: ('y'|'Y'); +fragment Z: ('z'|'Z'); + +STRING_LITERAL + @init{ + StringBuilder txt = new StringBuilder(); // temporary to build pg-style-string + } + @after{ setText(txt.toString()); } + : + /* pg-style string literal */ + ( + '\$' '\$' + ( /* collect all input until '$$' is reached again */ + { (input.size() - input.index() > 1) + && !"$$".equals(input.substring(input.index(), input.index() + 1)) }? + => c=. { txt.appendCodePoint(c); } + )* + '\$' '\$' + ) + | + /* conventional quoted string literal */ + ( + '\'' (c=~('\'') { txt.appendCodePoint(c);} | '\'' '\'' { txt.appendCodePoint('\''); })* '\'' + ) + ; + +QUOTED_NAME + @init{ StringBuilder b = new StringBuilder(); } + @after{ setText(b.toString()); } + : '\"' (c=~('\"') { b.appendCodePoint(c); } | '\"' '\"' { b.appendCodePoint('\"'); })+ '\"' + ; + +fragment DIGIT + : '0'..'9' + ; + +fragment LETTER + : ('A'..'Z' | 'a'..'z') + ; + +fragment HEX + : ('A'..'F' | 'a'..'f' | '0'..'9') + ; + +fragment EXPONENT + : E ('+' | '-')? DIGIT+ + ; + +INTEGER + : '-'? DIGIT+ + ; + +QMARK + : '?' + ; + +/* + * Normally a lexer only emits one token at a time, but ours is tricked out + * to support multiple (see @lexer::members near the top of the grammar). + */ +FLOAT + : INTEGER EXPONENT + | INTEGER '.' DIGIT* EXPONENT? + ; + +/* + * This has to be before IDENT so it takes precendence over it. + */ +BOOLEAN + : T R U E | F A L S E + ; + +IDENT + : LETTER (LETTER | DIGIT | '_')* + ; + +HEXNUMBER + : '0' X HEX* + ; + +UUID + : HEX HEX HEX HEX HEX HEX HEX HEX '-' + HEX HEX HEX HEX '-' + HEX HEX HEX HEX '-' + HEX HEX HEX HEX '-' + HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX + ; + +WS + : (' ' | '\t' | '\n' | '\r')+ { $channel = HIDDEN; } + ; + +COMMENT + : ('--' | '//') .* ('\n'|'\r') { $channel = HIDDEN; } + ; + +MULTILINE_COMMENT + : '/*' .* '*/' { $channel = HIDDEN; } + ; http://git-wip-us.apache.org/repos/asf/cassandra/blob/206a7bba/src/antlr/Parser.g ---------------------------------------------------------------------- diff --cc src/antlr/Parser.g index b3a9f5c,0000000..7d3c93b mode 100644,000000..100644 --- a/src/antlr/Parser.g +++ b/src/antlr/Parser.g @@@ -1,1670 -1,0 +1,1670 @@@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +parser grammar Parser; + +options { + language = Java; +} + +@members { + private final List<ErrorListener> listeners = new ArrayList<ErrorListener>(); + protected final List<ColumnIdentifier> bindVariables = new ArrayList<ColumnIdentifier>(); + + public static final Set<String> reservedTypeNames = new HashSet<String>() + {{ + add("byte"); + add("complex"); + add("enum"); + add("date"); + add("interval"); + add("macaddr"); + add("bitstring"); + }}; + + public AbstractMarker.Raw newBindVariables(ColumnIdentifier name) + { + AbstractMarker.Raw marker = new AbstractMarker.Raw(bindVariables.size()); + bindVariables.add(name); + return marker; + } + + public AbstractMarker.INRaw newINBindVariables(ColumnIdentifier name) + { + AbstractMarker.INRaw marker = new AbstractMarker.INRaw(bindVariables.size()); + bindVariables.add(name); + return marker; + } + + public Tuples.Raw newTupleBindVariables(ColumnIdentifier name) + { + Tuples.Raw marker = new Tuples.Raw(bindVariables.size()); + bindVariables.add(name); + return marker; + } + + public Tuples.INRaw newTupleINBindVariables(ColumnIdentifier name) + { + Tuples.INRaw marker = new Tuples.INRaw(bindVariables.size()); + bindVariables.add(name); + return marker; + } + + public Json.Marker newJsonBindVariables(ColumnIdentifier name) + { + Json.Marker marker = new Json.Marker(bindVariables.size()); + bindVariables.add(name); + return marker; + } + + public void addErrorListener(ErrorListener listener) + { + this.listeners.add(listener); + } + + public void removeErrorListener(ErrorListener listener) + { + this.listeners.remove(listener); + } + + public void displayRecognitionError(String[] tokenNames, RecognitionException e) + { + for (int i = 0, m = listeners.size(); i < m; i++) + listeners.get(i).syntaxError(this, tokenNames, e); + } + + protected void addRecognitionError(String msg) + { + for (int i = 0, m = listeners.size(); i < m; i++) + listeners.get(i).syntaxError(this, msg); + } + + public Map<String, String> convertPropertyMap(Maps.Literal map) + { + if (map == null || map.entries == null || map.entries.isEmpty()) + return Collections.<String, String>emptyMap(); + + Map<String, String> res = new HashMap<>(map.entries.size()); + + for (Pair<Term.Raw, Term.Raw> entry : map.entries) + { + // Because the parser tries to be smart and recover on error (to + // allow displaying more than one error I suppose), we have null + // entries in there. Just skip those, a proper error will be thrown in the end. + if (entry.left == null || entry.right == null) + break; + + if (!(entry.left instanceof Constants.Literal)) + { + String msg = "Invalid property name: " + entry.left; + if (entry.left instanceof AbstractMarker.Raw) + msg += " (bind variables are not supported in DDL queries)"; + addRecognitionError(msg); + break; + } + if (!(entry.right instanceof Constants.Literal)) + { + String msg = "Invalid property value: " + entry.right + " for property: " + entry.left; + if (entry.right instanceof AbstractMarker.Raw) + msg += " (bind variables are not supported in DDL queries)"; + addRecognitionError(msg); + break; + } + + res.put(((Constants.Literal)entry.left).getRawText(), ((Constants.Literal)entry.right).getRawText()); + } + + return res; + } + + public void addRawUpdate(List<Pair<ColumnDefinition.Raw, Operation.RawUpdate>> operations, ColumnDefinition.Raw key, Operation.RawUpdate update) + { + for (Pair<ColumnDefinition.Raw, Operation.RawUpdate> p : operations) + { + if (p.left.equals(key) && !p.right.isCompatibleWith(update)) + addRecognitionError("Multiple incompatible setting of column " + key); + } + operations.add(Pair.create(key, update)); + } + + public Set<Permission> filterPermissions(Set<Permission> permissions, IResource resource) + { + if (resource == null) + return Collections.emptySet(); + Set<Permission> filtered = new HashSet<>(permissions); + filtered.retainAll(resource.applicablePermissions()); + if (filtered.isEmpty()) + addRecognitionError("Resource type " + resource.getClass().getSimpleName() + + " does not support any of the requested permissions"); + + return filtered; + } + + public String canonicalizeObjectName(String s, boolean enforcePattern) + { + // these two conditions are here because technically they are valid + // ObjectNames, but we want to restrict their use without adding unnecessary + // work to JMXResource construction as that also happens on hotter code paths + if ("".equals(s)) + addRecognitionError("Empty JMX object name supplied"); + + if ("*:*".equals(s)) + addRecognitionError("Please use ALL MBEANS instead of wildcard pattern"); + + try + { + javax.management.ObjectName objectName = javax.management.ObjectName.getInstance(s); + if (enforcePattern && !objectName.isPattern()) + addRecognitionError("Plural form used, but non-pattern JMX object name specified (" + s + ")"); + return objectName.getCanonicalName(); + } + catch (javax.management.MalformedObjectNameException e) + { + addRecognitionError(s + " is not a valid JMX object name"); + return s; + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Recovery methods are overridden to avoid wasting work on recovering from errors when the result will be + // ignored anyway. + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + protected Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow) throws RecognitionException + { + throw new MismatchedTokenException(ttype, input); + } + + @Override + public void recover(IntStream input, RecognitionException re) + { + // Do nothing. + } +} + +/** STATEMENTS **/ + +cqlStatement returns [ParsedStatement stmt] + @after{ if (stmt != null) stmt.setBoundVariables(bindVariables); } + : st1= selectStatement { $stmt = st1; } + | st2= insertStatement { $stmt = st2; } + | st3= updateStatement { $stmt = st3; } + | st4= batchStatement { $stmt = st4; } + | st5= deleteStatement { $stmt = st5; } + | st6= useStatement { $stmt = st6; } + | st7= truncateStatement { $stmt = st7; } + | st8= createKeyspaceStatement { $stmt = st8; } + | st9= createTableStatement { $stmt = st9; } + | st10=createIndexStatement { $stmt = st10; } + | st11=dropKeyspaceStatement { $stmt = st11; } + | st12=dropTableStatement { $stmt = st12; } + | st13=dropIndexStatement { $stmt = st13; } + | st14=alterTableStatement { $stmt = st14; } + | st15=alterKeyspaceStatement { $stmt = st15; } + | st16=grantPermissionsStatement { $stmt = st16; } + | st17=revokePermissionsStatement { $stmt = st17; } + | st18=listPermissionsStatement { $stmt = st18; } + | st19=createUserStatement { $stmt = st19; } + | st20=alterUserStatement { $stmt = st20; } + | st21=dropUserStatement { $stmt = st21; } + | st22=listUsersStatement { $stmt = st22; } + | st23=createTriggerStatement { $stmt = st23; } + | st24=dropTriggerStatement { $stmt = st24; } + | st25=createTypeStatement { $stmt = st25; } + | st26=alterTypeStatement { $stmt = st26; } + | st27=dropTypeStatement { $stmt = st27; } + | st28=createFunctionStatement { $stmt = st28; } + | st29=dropFunctionStatement { $stmt = st29; } + | st30=createAggregateStatement { $stmt = st30; } + | st31=dropAggregateStatement { $stmt = st31; } + | st32=createRoleStatement { $stmt = st32; } + | st33=alterRoleStatement { $stmt = st33; } + | st34=dropRoleStatement { $stmt = st34; } + | st35=listRolesStatement { $stmt = st35; } + | st36=grantRoleStatement { $stmt = st36; } + | st37=revokeRoleStatement { $stmt = st37; } + | st38=createMaterializedViewStatement { $stmt = st38; } + | st39=dropMaterializedViewStatement { $stmt = st39; } + | st40=alterMaterializedViewStatement { $stmt = st40; } + ; + +/* + * USE <KEYSPACE>; + */ +useStatement returns [UseStatement stmt] + : K_USE ks=keyspaceName { $stmt = new UseStatement(ks); } + ; + +/** + * SELECT <expression> + * FROM <CF> + * WHERE KEY = "key1" AND COL > 1 AND COL < 100 + * LIMIT <NUMBER>; + */ +selectStatement returns [SelectStatement.RawStatement expr] + @init { + boolean isDistinct = false; + Term.Raw limit = null; + Term.Raw perPartitionLimit = null; + Map<ColumnDefinition.Raw, Boolean> orderings = new LinkedHashMap<>(); + List<ColumnDefinition.Raw> groups = new ArrayList<>(); + boolean allowFiltering = false; + boolean isJson = false; + } + : K_SELECT + ( K_JSON { isJson = true; } )? + ( ( K_DISTINCT { isDistinct = true; } )? sclause=selectClause ) + K_FROM cf=columnFamilyName + ( K_WHERE wclause=whereClause )? + ( K_GROUP K_BY groupByClause[groups] ( ',' groupByClause[groups] )* )? + ( K_ORDER K_BY orderByClause[orderings] ( ',' orderByClause[orderings] )* )? + ( K_PER K_PARTITION K_LIMIT rows=intValue { perPartitionLimit = rows; } )? + ( K_LIMIT rows=intValue { limit = rows; } )? + ( K_ALLOW K_FILTERING { allowFiltering = true; } )? + { + SelectStatement.Parameters params = new SelectStatement.Parameters(orderings, + groups, + isDistinct, + allowFiltering, + isJson); + WhereClause where = wclause == null ? WhereClause.empty() : wclause.build(); + $expr = new SelectStatement.RawStatement(cf, params, sclause, where, limit, perPartitionLimit); + } + ; + +selectClause returns [List<RawSelector> expr] + : t1=selector { $expr = new ArrayList<RawSelector>(); $expr.add(t1); } (',' tN=selector { $expr.add(tN); })* + | '\*' { $expr = Collections.<RawSelector>emptyList();} + ; + +selector returns [RawSelector s] + @init{ ColumnIdentifier alias = null; } + : us=unaliasedSelector (K_AS c=noncol_ident { alias = c; })? { $s = new RawSelector(us, alias); } + ; + +/* + * A single selection. The core of it is selecting a column, but we also allow any term and function, as well as + * sub-element selection for UDT. + */ +unaliasedSelector returns [Selectable.Raw s] + @init { Selectable.Raw tmp = null; } + : ( c=cident { tmp = c; } + | v=value { tmp = new Selectable.WithTerm.Raw(v); } + | '(' ct=comparatorType ')' v=value { tmp = new Selectable.WithTerm.Raw(new TypeCast(ct, v)); } + | K_COUNT '(' '\*' ')' { tmp = Selectable.WithFunction.Raw.newCountRowsFunction(); } + | K_WRITETIME '(' c=cident ')' { tmp = new Selectable.WritetimeOrTTL.Raw(c, true); } + | K_TTL '(' c=cident ')' { tmp = new Selectable.WritetimeOrTTL.Raw(c, false); } + | K_CAST '(' sn=unaliasedSelector K_AS t=native_type ')' {tmp = new Selectable.WithCast.Raw(sn, t);} + | f=functionName args=selectionFunctionArgs { tmp = new Selectable.WithFunction.Raw(f, args); } + ) ( '.' fi=fident { tmp = new Selectable.WithFieldSelection.Raw(tmp, fi); } )* { $s = tmp; } + ; + +selectionFunctionArgs returns [List<Selectable.Raw> a] + : '(' ')' { $a = Collections.emptyList(); } + | '(' s1=unaliasedSelector { List<Selectable.Raw> args = new ArrayList<Selectable.Raw>(); args.add(s1); } + ( ',' sn=unaliasedSelector { args.add(sn); } )* + ')' { $a = args; } + ; + +whereClause returns [WhereClause.Builder clause] + @init{ $clause = new WhereClause.Builder(); } + : relationOrExpression[$clause] (K_AND relationOrExpression[$clause])* + ; + +relationOrExpression [WhereClause.Builder clause] + : relation[$clause] + | customIndexExpression[$clause] + ; + +customIndexExpression [WhereClause.Builder clause] + @init{IndexName name = new IndexName();} + : 'expr(' idxName[name] ',' t=term ')' { clause.add(new CustomIndexExpression(name, t));} + ; + +orderByClause[Map<ColumnDefinition.Raw, Boolean> orderings] + @init{ + boolean reversed = false; + } + : c=cident (K_ASC | K_DESC { reversed = true; })? { orderings.put(c, reversed); } + ; + +groupByClause[List<ColumnDefinition.Raw> groups] + : c=cident { groups.add(c); } + ; + +/** + * INSERT INTO <CF> (<column>, <column>, <column>, ...) + * VALUES (<value>, <value>, <value>, ...) + * USING TIMESTAMP <long>; + * + */ +insertStatement returns [ModificationStatement.Parsed expr] + : K_INSERT K_INTO cf=columnFamilyName + ( st1=normalInsertStatement[cf] { $expr = st1; } + | K_JSON st2=jsonInsertStatement[cf] { $expr = st2; }) + ; + +normalInsertStatement [CFName cf] returns [UpdateStatement.ParsedInsert expr] + @init { + Attributes.Raw attrs = new Attributes.Raw(); + List<ColumnDefinition.Raw> columnNames = new ArrayList<>(); + List<Term.Raw> values = new ArrayList<>(); + boolean ifNotExists = false; + } + : '(' c1=cident { columnNames.add(c1); } ( ',' cn=cident { columnNames.add(cn); } )* ')' + K_VALUES + '(' v1=term { values.add(v1); } ( ',' vn=term { values.add(vn); } )* ')' + ( K_IF K_NOT K_EXISTS { ifNotExists = true; } )? + ( usingClause[attrs] )? + { + $expr = new UpdateStatement.ParsedInsert(cf, attrs, columnNames, values, ifNotExists); + } + ; + +jsonInsertStatement [CFName cf] returns [UpdateStatement.ParsedInsertJson expr] + @init { + Attributes.Raw attrs = new Attributes.Raw(); + boolean ifNotExists = false; + boolean defaultUnset = false; + } + : val=jsonValue + ( K_DEFAULT ( K_NULL | ( { defaultUnset = true; } K_UNSET) ) )? + ( K_IF K_NOT K_EXISTS { ifNotExists = true; } )? + ( usingClause[attrs] )? + { + $expr = new UpdateStatement.ParsedInsertJson(cf, attrs, val, defaultUnset, ifNotExists); + } + ; + +jsonValue returns [Json.Raw value] + : s=STRING_LITERAL { $value = new Json.Literal($s.text); } + | ':' id=noncol_ident { $value = newJsonBindVariables(id); } + | QMARK { $value = newJsonBindVariables(null); } + ; + +usingClause[Attributes.Raw attrs] + : K_USING usingClauseObjective[attrs] ( K_AND usingClauseObjective[attrs] )* + ; + +usingClauseObjective[Attributes.Raw attrs] + : K_TIMESTAMP ts=intValue { attrs.timestamp = ts; } + | K_TTL t=intValue { attrs.timeToLive = t; } + ; + +/** + * UPDATE <CF> + * USING TIMESTAMP <long> + * SET name1 = value1, name2 = value2 + * WHERE key = value; + * [IF (EXISTS | name = value, ...)]; + */ +updateStatement returns [UpdateStatement.ParsedUpdate expr] + @init { + Attributes.Raw attrs = new Attributes.Raw(); + List<Pair<ColumnDefinition.Raw, Operation.RawUpdate>> operations = new ArrayList<>(); + boolean ifExists = false; + } + : K_UPDATE cf=columnFamilyName + ( usingClause[attrs] )? + K_SET columnOperation[operations] (',' columnOperation[operations])* + K_WHERE wclause=whereClause + ( K_IF ( K_EXISTS { ifExists = true; } | conditions=updateConditions ))? + { + $expr = new UpdateStatement.ParsedUpdate(cf, + attrs, + operations, + wclause.build(), + conditions == null ? Collections.<Pair<ColumnDefinition.Raw, ColumnCondition.Raw>>emptyList() : conditions, + ifExists); + } + ; + +updateConditions returns [List<Pair<ColumnDefinition.Raw, ColumnCondition.Raw>> conditions] + @init { conditions = new ArrayList<Pair<ColumnDefinition.Raw, ColumnCondition.Raw>>(); } + : columnCondition[conditions] ( K_AND columnCondition[conditions] )* + ; + + +/** + * DELETE name1, name2 + * FROM <CF> + * USING TIMESTAMP <long> + * WHERE KEY = keyname + [IF (EXISTS | name = value, ...)]; + */ +deleteStatement returns [DeleteStatement.Parsed expr] + @init { + Attributes.Raw attrs = new Attributes.Raw(); + List<Operation.RawDeletion> columnDeletions = Collections.emptyList(); + boolean ifExists = false; + } + : K_DELETE ( dels=deleteSelection { columnDeletions = dels; } )? + K_FROM cf=columnFamilyName + ( usingClauseDelete[attrs] )? + K_WHERE wclause=whereClause + ( K_IF ( K_EXISTS { ifExists = true; } | conditions=updateConditions ))? + { + $expr = new DeleteStatement.Parsed(cf, + attrs, + columnDeletions, + wclause.build(), + conditions == null ? Collections.<Pair<ColumnDefinition.Raw, ColumnCondition.Raw>>emptyList() : conditions, + ifExists); + } + ; + +deleteSelection returns [List<Operation.RawDeletion> operations] + : { $operations = new ArrayList<Operation.RawDeletion>(); } + t1=deleteOp { $operations.add(t1); } + (',' tN=deleteOp { $operations.add(tN); })* + ; + +deleteOp returns [Operation.RawDeletion op] + : c=cident { $op = new Operation.ColumnDeletion(c); } + | c=cident '[' t=term ']' { $op = new Operation.ElementDeletion(c, t); } + | c=cident '.' field=fident { $op = new Operation.FieldDeletion(c, field); } + ; + +usingClauseDelete[Attributes.Raw attrs] + : K_USING K_TIMESTAMP ts=intValue { attrs.timestamp = ts; } + ; + +/** + * BEGIN BATCH + * UPDATE <CF> SET name1 = value1 WHERE KEY = keyname1; + * UPDATE <CF> SET name2 = value2 WHERE KEY = keyname2; + * UPDATE <CF> SET name3 = value3 WHERE KEY = keyname3; + * ... + * APPLY BATCH + * + * OR + * + * BEGIN BATCH + * INSERT INTO <CF> (KEY, <name>) VALUES ('<key>', '<value>'); + * INSERT INTO <CF> (KEY, <name>) VALUES ('<key>', '<value>'); + * ... + * APPLY BATCH + * + * OR + * + * BEGIN BATCH + * DELETE name1, name2 FROM <CF> WHERE key = <key> + * DELETE name3, name4 FROM <CF> WHERE key = <key> + * ... + * APPLY BATCH + */ +batchStatement returns [BatchStatement.Parsed expr] + @init { + BatchStatement.Type type = BatchStatement.Type.LOGGED; + List<ModificationStatement.Parsed> statements = new ArrayList<ModificationStatement.Parsed>(); + Attributes.Raw attrs = new Attributes.Raw(); + } + : K_BEGIN + ( K_UNLOGGED { type = BatchStatement.Type.UNLOGGED; } | K_COUNTER { type = BatchStatement.Type.COUNTER; } )? + K_BATCH ( usingClause[attrs] )? + ( s=batchStatementObjective ';'? { statements.add(s); } )* + K_APPLY K_BATCH + { + $expr = new BatchStatement.Parsed(type, attrs, statements); + } + ; + +batchStatementObjective returns [ModificationStatement.Parsed statement] + : i=insertStatement { $statement = i; } + | u=updateStatement { $statement = u; } + | d=deleteStatement { $statement = d; } + ; + +createAggregateStatement returns [CreateAggregateStatement expr] + @init { + boolean orReplace = false; + boolean ifNotExists = false; + + List<CQL3Type.Raw> argsTypes = new ArrayList<>(); + } + : K_CREATE (K_OR K_REPLACE { orReplace = true; })? + K_AGGREGATE + (K_IF K_NOT K_EXISTS { ifNotExists = true; })? + fn=functionName + '(' + ( + v=comparatorType { argsTypes.add(v); } + ( ',' v=comparatorType { argsTypes.add(v); } )* + )? + ')' + K_SFUNC sfunc = allowedFunctionName + K_STYPE stype = comparatorType + ( + K_FINALFUNC ffunc = allowedFunctionName + )? + ( + K_INITCOND ival = term + )? + { $expr = new CreateAggregateStatement(fn, argsTypes, sfunc, stype, ffunc, ival, orReplace, ifNotExists); } + ; + +dropAggregateStatement returns [DropAggregateStatement expr] + @init { + boolean ifExists = false; + List<CQL3Type.Raw> argsTypes = new ArrayList<>(); + boolean argsPresent = false; + } + : K_DROP K_AGGREGATE + (K_IF K_EXISTS { ifExists = true; } )? + fn=functionName + ( + '(' + ( + v=comparatorType { argsTypes.add(v); } + ( ',' v=comparatorType { argsTypes.add(v); } )* + )? + ')' + { argsPresent = true; } + )? + { $expr = new DropAggregateStatement(fn, argsTypes, argsPresent, ifExists); } + ; + +createFunctionStatement returns [CreateFunctionStatement expr] + @init { + boolean orReplace = false; + boolean ifNotExists = false; + + List<ColumnIdentifier> argsNames = new ArrayList<>(); + List<CQL3Type.Raw> argsTypes = new ArrayList<>(); + boolean calledOnNullInput = false; + } + : K_CREATE (K_OR K_REPLACE { orReplace = true; })? + K_FUNCTION + (K_IF K_NOT K_EXISTS { ifNotExists = true; })? + fn=functionName + '(' + ( + k=noncol_ident v=comparatorType { argsNames.add(k); argsTypes.add(v); } + ( ',' k=noncol_ident v=comparatorType { argsNames.add(k); argsTypes.add(v); } )* + )? + ')' + ( (K_RETURNS K_NULL) | (K_CALLED { calledOnNullInput=true; })) K_ON K_NULL K_INPUT + K_RETURNS rt = comparatorType + K_LANGUAGE language = IDENT + K_AS body = STRING_LITERAL + { $expr = new CreateFunctionStatement(fn, $language.text.toLowerCase(), $body.text, + argsNames, argsTypes, rt, calledOnNullInput, orReplace, ifNotExists); } + ; + +dropFunctionStatement returns [DropFunctionStatement expr] + @init { + boolean ifExists = false; + List<CQL3Type.Raw> argsTypes = new ArrayList<>(); + boolean argsPresent = false; + } + : K_DROP K_FUNCTION + (K_IF K_EXISTS { ifExists = true; } )? + fn=functionName + ( + '(' + ( + v=comparatorType { argsTypes.add(v); } + ( ',' v=comparatorType { argsTypes.add(v); } )* + )? + ')' + { argsPresent = true; } + )? + { $expr = new DropFunctionStatement(fn, argsTypes, argsPresent, ifExists); } + ; + +/** + * CREATE KEYSPACE [IF NOT EXISTS] <KEYSPACE> WITH attr1 = value1 AND attr2 = value2; + */ +createKeyspaceStatement returns [CreateKeyspaceStatement expr] + @init { + KeyspaceAttributes attrs = new KeyspaceAttributes(); + boolean ifNotExists = false; + } + : K_CREATE K_KEYSPACE (K_IF K_NOT K_EXISTS { ifNotExists = true; } )? ks=keyspaceName + K_WITH properties[attrs] { $expr = new CreateKeyspaceStatement(ks, attrs, ifNotExists); } + ; + +/** + * CREATE COLUMNFAMILY [IF NOT EXISTS] <CF> ( + * <name1> <type>, + * <name2> <type>, + * <name3> <type> + * ) WITH <property> = <value> AND ...; + */ +createTableStatement returns [CreateTableStatement.RawStatement expr] + @init { boolean ifNotExists = false; } + : K_CREATE K_COLUMNFAMILY (K_IF K_NOT K_EXISTS { ifNotExists = true; } )? + cf=columnFamilyName { $expr = new CreateTableStatement.RawStatement(cf, ifNotExists); } + cfamDefinition[expr] + ; + +cfamDefinition[CreateTableStatement.RawStatement expr] + : '(' cfamColumns[expr] ( ',' cfamColumns[expr]? )* ')' + ( K_WITH cfamProperty[expr.properties] ( K_AND cfamProperty[expr.properties] )*)? + ; + +cfamColumns[CreateTableStatement.RawStatement expr] + : k=ident v=comparatorType { boolean isStatic=false; } (K_STATIC {isStatic = true;})? { $expr.addDefinition(k, v, isStatic); } + (K_PRIMARY K_KEY { $expr.addKeyAliases(Collections.singletonList(k)); })? + | K_PRIMARY K_KEY '(' pkDef[expr] (',' c=ident { $expr.addColumnAlias(c); } )* ')' + ; + +pkDef[CreateTableStatement.RawStatement expr] + : k=ident { $expr.addKeyAliases(Collections.singletonList(k)); } + | '(' { List<ColumnIdentifier> l = new ArrayList<ColumnIdentifier>(); } k1=ident { l.add(k1); } ( ',' kn=ident { l.add(kn); } )* ')' { $expr.addKeyAliases(l); } + ; + +cfamProperty[CFProperties props] + : property[props.properties] + | K_COMPACT K_STORAGE { $props.setCompactStorage(); } + | K_CLUSTERING K_ORDER K_BY '(' cfamOrdering[props] (',' cfamOrdering[props])* ')' + ; + +cfamOrdering[CFProperties props] + @init{ boolean reversed=false; } + : k=ident (K_ASC | K_DESC { reversed=true;} ) { $props.setOrdering(k, reversed); } + ; + + +/** + * CREATE TYPE foo ( + * <name1> <type1>, + * <name2> <type2>, + * .... + * ) + */ +createTypeStatement returns [CreateTypeStatement expr] + @init { boolean ifNotExists = false; } + : K_CREATE K_TYPE (K_IF K_NOT K_EXISTS { ifNotExists = true; } )? + tn=userTypeName { $expr = new CreateTypeStatement(tn, ifNotExists); } + '(' typeColumns[expr] ( ',' typeColumns[expr]? )* ')' + ; + +typeColumns[CreateTypeStatement expr] + : k=fident v=comparatorType { $expr.addDefinition(k, v); } + ; + + +/** + * CREATE INDEX [IF NOT EXISTS] [indexName] ON <columnFamily> (<columnName>); + * CREATE CUSTOM INDEX [IF NOT EXISTS] [indexName] ON <columnFamily> (<columnName>) USING <indexClass>; + */ +createIndexStatement returns [CreateIndexStatement expr] + @init { + IndexPropDefs props = new IndexPropDefs(); + boolean ifNotExists = false; + IndexName name = new IndexName(); + List<IndexTarget.Raw> targets = new ArrayList<>(); + } + : K_CREATE (K_CUSTOM { props.isCustom = true; })? K_INDEX (K_IF K_NOT K_EXISTS { ifNotExists = true; } )? + (idxName[name])? K_ON cf=columnFamilyName '(' (indexIdent[targets] (',' indexIdent[targets])*)? ')' + (K_USING cls=STRING_LITERAL { props.customClass = $cls.text; })? + (K_WITH properties[props])? + { $expr = new CreateIndexStatement(cf, name, targets, props, ifNotExists); } + ; + +indexIdent [List<IndexTarget.Raw> targets] + : c=cident { $targets.add(IndexTarget.Raw.simpleIndexOn(c)); } + | K_VALUES '(' c=cident ')' { $targets.add(IndexTarget.Raw.valuesOf(c)); } + | K_KEYS '(' c=cident ')' { $targets.add(IndexTarget.Raw.keysOf(c)); } + | K_ENTRIES '(' c=cident ')' { $targets.add(IndexTarget.Raw.keysAndValuesOf(c)); } + | K_FULL '(' c=cident ')' { $targets.add(IndexTarget.Raw.fullCollection(c)); } + ; + +/** + * CREATE MATERIALIZED VIEW <viewName> AS + * SELECT <columns> + * FROM <CF> + * WHERE <pkColumns> IS NOT NULL + * PRIMARY KEY (<pkColumns>) + * WITH <property> = <value> AND ...; + */ +createMaterializedViewStatement returns [CreateViewStatement expr] + @init { + boolean ifNotExists = false; + List<ColumnDefinition.Raw> partitionKeys = new ArrayList<>(); + List<ColumnDefinition.Raw> compositeKeys = new ArrayList<>(); + } + : K_CREATE K_MATERIALIZED K_VIEW (K_IF K_NOT K_EXISTS { ifNotExists = true; })? cf=columnFamilyName K_AS + K_SELECT sclause=selectClause K_FROM basecf=columnFamilyName + (K_WHERE wclause=whereClause)? + K_PRIMARY K_KEY ( + '(' '(' k1=cident { partitionKeys.add(k1); } ( ',' kn=cident { partitionKeys.add(kn); } )* ')' ( ',' c1=cident { compositeKeys.add(c1); } )* ')' + | '(' k1=cident { partitionKeys.add(k1); } ( ',' cn=cident { compositeKeys.add(cn); } )* ')' + ) + { + WhereClause where = wclause == null ? WhereClause.empty() : wclause.build(); + $expr = new CreateViewStatement(cf, basecf, sclause, where, partitionKeys, compositeKeys, ifNotExists); + } + ( K_WITH cfamProperty[expr.properties] ( K_AND cfamProperty[expr.properties] )*)? + ; + +/** + * CREATE TRIGGER triggerName ON columnFamily USING 'triggerClass'; + */ +createTriggerStatement returns [CreateTriggerStatement expr] + @init { + boolean ifNotExists = false; + } + : K_CREATE K_TRIGGER (K_IF K_NOT K_EXISTS { ifNotExists = true; } )? (name=cident) + K_ON cf=columnFamilyName K_USING cls=STRING_LITERAL + { $expr = new CreateTriggerStatement(cf, name.rawText(), $cls.text, ifNotExists); } + ; + +/** + * DROP TRIGGER [IF EXISTS] triggerName ON columnFamily; + */ +dropTriggerStatement returns [DropTriggerStatement expr] + @init { boolean ifExists = false; } + : K_DROP K_TRIGGER (K_IF K_EXISTS { ifExists = true; } )? (name=cident) K_ON cf=columnFamilyName + { $expr = new DropTriggerStatement(cf, name.rawText(), ifExists); } + ; + +/** + * ALTER KEYSPACE <KS> WITH <property> = <value>; + */ +alterKeyspaceStatement returns [AlterKeyspaceStatement expr] + @init { KeyspaceAttributes attrs = new KeyspaceAttributes(); } + : K_ALTER K_KEYSPACE ks=keyspaceName + K_WITH properties[attrs] { $expr = new AlterKeyspaceStatement(ks, attrs); } + ; + +/** + * ALTER COLUMN FAMILY <CF> ALTER <column> TYPE <newtype>; + * ALTER COLUMN FAMILY <CF> ADD <column> <newtype>; | ALTER COLUMN FAMILY <CF> ADD (<column> <newtype>,<column1> <newtype1>..... <column n> <newtype n>) + * ALTER COLUMN FAMILY <CF> DROP <column>; | ALTER COLUMN FAMILY <CF> DROP ( <column>,<column1>.....<column n>) + * ALTER COLUMN FAMILY <CF> WITH <property> = <value>; + * ALTER COLUMN FAMILY <CF> RENAME <column> TO <column>; + */ +alterTableStatement returns [AlterTableStatement expr] + @init { + AlterTableStatement.Type type = null; + TableAttributes attrs = new TableAttributes(); + Map<ColumnDefinition.Raw, ColumnDefinition.Raw> renames = new HashMap<ColumnDefinition.Raw, ColumnDefinition.Raw>(); + List<AlterTableStatementColumn> colNameList = new ArrayList<AlterTableStatementColumn>(); + Long deleteTimestamp = null; + } + : K_ALTER K_COLUMNFAMILY cf=columnFamilyName + ( K_ALTER id=cident K_TYPE v=comparatorType { type = AlterTableStatement.Type.ALTER; } { colNameList.add(new AlterTableStatementColumn(id,v)); } + | K_ADD ( (id=cident v=comparatorType b1=cfisStatic { colNameList.add(new AlterTableStatementColumn(id,v,b1)); }) + | ('(' id1=cident v1=comparatorType b1=cfisStatic { colNameList.add(new AlterTableStatementColumn(id1,v1,b1)); } + ( ',' idn=cident vn=comparatorType bn=cfisStatic { colNameList.add(new AlterTableStatementColumn(idn,vn,bn)); } )* ')' ) ) { type = AlterTableStatement.Type.ADD; } + | K_DROP ( ( id=cident { colNameList.add(new AlterTableStatementColumn(id)); } + | ('(' id1=cident { colNameList.add(new AlterTableStatementColumn(id1)); } + ( ',' idn=cident { colNameList.add(new AlterTableStatementColumn(idn)); } )* ')') ) + ( K_USING K_TIMESTAMP t=INTEGER { deleteTimestamp = Long.parseLong(Constants.Literal.integer($t.text).getText()); })? ) { type = AlterTableStatement.Type.DROP; } + | K_WITH properties[attrs] { type = AlterTableStatement.Type.OPTS; } + | K_RENAME { type = AlterTableStatement.Type.RENAME; } + id1=cident K_TO toId1=cident { renames.put(id1, toId1); } + ( K_AND idn=cident K_TO toIdn=cident { renames.put(idn, toIdn); } )* + ) + { + $expr = new AlterTableStatement(cf, type, colNameList, attrs, renames, deleteTimestamp); + } + ; + +cfisStatic returns [boolean isStaticColumn] + @init{ + boolean isStatic = false; + } + : (K_STATIC { isStatic=true; })? { $isStaticColumn = isStatic; + } + ; + +alterMaterializedViewStatement returns [AlterViewStatement expr] + @init { + TableAttributes attrs = new TableAttributes(); + } + : K_ALTER K_MATERIALIZED K_VIEW name=columnFamilyName + K_WITH properties[attrs] + { + $expr = new AlterViewStatement(name, attrs); + } + ; + + +/** + * ALTER TYPE <name> ALTER <field> TYPE <newtype>; + * ALTER TYPE <name> ADD <field> <newtype>; + * ALTER TYPE <name> RENAME <field> TO <newtype> AND ...; + */ +alterTypeStatement returns [AlterTypeStatement expr] + : K_ALTER K_TYPE name=userTypeName + ( K_ALTER f=fident K_TYPE v=comparatorType { $expr = AlterTypeStatement.alter(name, f, v); } + | K_ADD f=fident v=comparatorType { $expr = AlterTypeStatement.addition(name, f, v); } + | K_RENAME + { Map<FieldIdentifier, FieldIdentifier> renames = new HashMap<>(); } + id1=fident K_TO toId1=fident { renames.put(id1, toId1); } + ( K_AND idn=fident K_TO toIdn=fident { renames.put(idn, toIdn); } )* + { $expr = AlterTypeStatement.renames(name, renames); } + ) + ; + + +/** + * DROP KEYSPACE [IF EXISTS] <KSP>; + */ +dropKeyspaceStatement returns [DropKeyspaceStatement ksp] + @init { boolean ifExists = false; } + : K_DROP K_KEYSPACE (K_IF K_EXISTS { ifExists = true; } )? ks=keyspaceName { $ksp = new DropKeyspaceStatement(ks, ifExists); } + ; + +/** + * DROP COLUMNFAMILY [IF EXISTS] <CF>; + */ +dropTableStatement returns [DropTableStatement stmt] + @init { boolean ifExists = false; } + : K_DROP K_COLUMNFAMILY (K_IF K_EXISTS { ifExists = true; } )? cf=columnFamilyName { $stmt = new DropTableStatement(cf, ifExists); } + ; + +/** + * DROP TYPE <name>; + */ +dropTypeStatement returns [DropTypeStatement stmt] + @init { boolean ifExists = false; } + : K_DROP K_TYPE (K_IF K_EXISTS { ifExists = true; } )? name=userTypeName { $stmt = new DropTypeStatement(name, ifExists); } + ; + +/** + * DROP INDEX [IF EXISTS] <INDEX_NAME> + */ +dropIndexStatement returns [DropIndexStatement expr] + @init { boolean ifExists = false; } + : K_DROP K_INDEX (K_IF K_EXISTS { ifExists = true; } )? index=indexName + { $expr = new DropIndexStatement(index, ifExists); } + ; + +/** + * DROP MATERIALIZED VIEW [IF EXISTS] <view_name> + */ +dropMaterializedViewStatement returns [DropViewStatement expr] + @init { boolean ifExists = false; } + : K_DROP K_MATERIALIZED K_VIEW (K_IF K_EXISTS { ifExists = true; } )? cf=columnFamilyName + { $expr = new DropViewStatement(cf, ifExists); } + ; + +/** + * TRUNCATE <CF>; + */ +truncateStatement returns [TruncateStatement stmt] + : K_TRUNCATE (K_COLUMNFAMILY)? cf=columnFamilyName { $stmt = new TruncateStatement(cf); } + ; + +/** + * GRANT <permission> ON <resource> TO <rolename> + */ +grantPermissionsStatement returns [GrantPermissionsStatement stmt] + : K_GRANT + permissionOrAll + K_ON + resource + K_TO + grantee=userOrRoleName + { $stmt = new GrantPermissionsStatement(filterPermissions($permissionOrAll.perms, $resource.res), $resource.res, grantee); } + ; + +/** + * REVOKE <permission> ON <resource> FROM <rolename> + */ +revokePermissionsStatement returns [RevokePermissionsStatement stmt] + : K_REVOKE + permissionOrAll + K_ON + resource + K_FROM + revokee=userOrRoleName + { $stmt = new RevokePermissionsStatement(filterPermissions($permissionOrAll.perms, $resource.res), $resource.res, revokee); } + ; + +/** + * GRANT ROLE <rolename> TO <grantee> + */ +grantRoleStatement returns [GrantRoleStatement stmt] + : K_GRANT + role=userOrRoleName + K_TO + grantee=userOrRoleName + { $stmt = new GrantRoleStatement(role, grantee); } + ; + +/** + * REVOKE ROLE <rolename> FROM <revokee> + */ +revokeRoleStatement returns [RevokeRoleStatement stmt] + : K_REVOKE + role=userOrRoleName + K_FROM + revokee=userOrRoleName + { $stmt = new RevokeRoleStatement(role, revokee); } + ; + +listPermissionsStatement returns [ListPermissionsStatement stmt] + @init { + IResource resource = null; + boolean recursive = true; + RoleName grantee = new RoleName(); + } + : K_LIST + permissionOrAll + ( K_ON resource { resource = $resource.res; } )? + ( K_OF roleName[grantee] )? + ( K_NORECURSIVE { recursive = false; } )? + { $stmt = new ListPermissionsStatement($permissionOrAll.perms, resource, grantee, recursive); } + ; + +permission returns [Permission perm] + : p=(K_CREATE | K_ALTER | K_DROP | K_SELECT | K_MODIFY | K_AUTHORIZE | K_DESCRIBE | K_EXECUTE) + { $perm = Permission.valueOf($p.text.toUpperCase()); } + ; + +permissionOrAll returns [Set<Permission> perms] + : K_ALL ( K_PERMISSIONS )? { $perms = Permission.ALL; } + | p=permission ( K_PERMISSION )? { $perms = EnumSet.of($p.perm); } + ; + +resource returns [IResource res] + : d=dataResource { $res = $d.res; } + | r=roleResource { $res = $r.res; } + | f=functionResource { $res = $f.res; } + | j=jmxResource { $res = $j.res; } + ; + +dataResource returns [DataResource res] + : K_ALL K_KEYSPACES { $res = DataResource.root(); } + | K_KEYSPACE ks = keyspaceName { $res = DataResource.keyspace($ks.id); } + | ( K_COLUMNFAMILY )? cf = columnFamilyName + { $res = DataResource.table($cf.name.getKeyspace(), $cf.name.getColumnFamily()); } + ; + +jmxResource returns [JMXResource res] + : K_ALL K_MBEANS { $res = JMXResource.root(); } + // when a bean name (or pattern) is supplied, validate that it's a legal ObjectName + // also, just to be picky, if the "MBEANS" form is used, only allow a pattern style names + | K_MBEAN mbean { $res = JMXResource.mbean(canonicalizeObjectName($mbean.text, false)); } + | K_MBEANS mbean { $res = JMXResource.mbean(canonicalizeObjectName($mbean.text, true)); } + ; + +roleResource returns [RoleResource res] + : K_ALL K_ROLES { $res = RoleResource.root(); } + | K_ROLE role = userOrRoleName { $res = RoleResource.role($role.name.getName()); } + ; + +functionResource returns [FunctionResource res] + @init { + List<CQL3Type.Raw> argsTypes = new ArrayList<>(); + } + : K_ALL K_FUNCTIONS { $res = FunctionResource.root(); } + | K_ALL K_FUNCTIONS K_IN K_KEYSPACE ks = keyspaceName { $res = FunctionResource.keyspace($ks.id); } + // Arg types are mandatory for DCL statements on Functions + | K_FUNCTION fn=functionName + ( + '(' + ( + v=comparatorType { argsTypes.add(v); } + ( ',' v=comparatorType { argsTypes.add(v); } )* + )? + ')' + ) + { $res = FunctionResource.functionFromCql($fn.s.keyspace, $fn.s.name, argsTypes); } + ; + +/** + * CREATE USER [IF NOT EXISTS] <username> [WITH PASSWORD <password>] [SUPERUSER|NOSUPERUSER] + */ +createUserStatement returns [CreateRoleStatement stmt] + @init { + RoleOptions opts = new RoleOptions(); + opts.setOption(IRoleManager.Option.LOGIN, true); + boolean superuser = false; + boolean ifNotExists = false; + RoleName name = new RoleName(); + } + : K_CREATE K_USER (K_IF K_NOT K_EXISTS { ifNotExists = true; })? u=username { name.setName($u.text, true); } + ( K_WITH userPassword[opts] )? + ( K_SUPERUSER { superuser = true; } | K_NOSUPERUSER { superuser = false; } )? + { opts.setOption(IRoleManager.Option.SUPERUSER, superuser); + $stmt = new CreateRoleStatement(name, opts, ifNotExists); } + ; + +/** + * ALTER USER <username> [WITH PASSWORD <password>] [SUPERUSER|NOSUPERUSER] + */ +alterUserStatement returns [AlterRoleStatement stmt] + @init { + RoleOptions opts = new RoleOptions(); + RoleName name = new RoleName(); + } + : K_ALTER K_USER u=username { name.setName($u.text, true); } + ( K_WITH userPassword[opts] )? + ( K_SUPERUSER { opts.setOption(IRoleManager.Option.SUPERUSER, true); } + | K_NOSUPERUSER { opts.setOption(IRoleManager.Option.SUPERUSER, false); } ) ? + { $stmt = new AlterRoleStatement(name, opts); } + ; + +/** + * DROP USER [IF EXISTS] <username> + */ +dropUserStatement returns [DropRoleStatement stmt] + @init { + boolean ifExists = false; + RoleName name = new RoleName(); + } + : K_DROP K_USER (K_IF K_EXISTS { ifExists = true; })? u=username { name.setName($u.text, true); $stmt = new DropRoleStatement(name, ifExists); } + ; + +/** + * LIST USERS + */ +listUsersStatement returns [ListRolesStatement stmt] + : K_LIST K_USERS { $stmt = new ListUsersStatement(); } + ; + +/** + * CREATE ROLE [IF NOT EXISTS] <rolename> [ [WITH] option [ [AND] option ]* ] + * + * where option can be: + * PASSWORD = '<password>' + * SUPERUSER = (true|false) + * LOGIN = (true|false) + * OPTIONS = { 'k1':'v1', 'k2':'v2'} + */ +createRoleStatement returns [CreateRoleStatement stmt] + @init { + RoleOptions opts = new RoleOptions(); + boolean ifNotExists = false; + } + : K_CREATE K_ROLE (K_IF K_NOT K_EXISTS { ifNotExists = true; })? name=userOrRoleName + ( K_WITH roleOptions[opts] )? + { + // set defaults if they weren't explictly supplied + if (!opts.getLogin().isPresent()) + { + opts.setOption(IRoleManager.Option.LOGIN, false); + } + if (!opts.getSuperuser().isPresent()) + { + opts.setOption(IRoleManager.Option.SUPERUSER, false); + } + $stmt = new CreateRoleStatement(name, opts, ifNotExists); + } + ; + +/** + * ALTER ROLE <rolename> [ [WITH] option [ [AND] option ]* ] + * + * where option can be: + * PASSWORD = '<password>' + * SUPERUSER = (true|false) + * LOGIN = (true|false) + * OPTIONS = { 'k1':'v1', 'k2':'v2'} + */ +alterRoleStatement returns [AlterRoleStatement stmt] + @init { + RoleOptions opts = new RoleOptions(); + } + : K_ALTER K_ROLE name=userOrRoleName + ( K_WITH roleOptions[opts] )? + { $stmt = new AlterRoleStatement(name, opts); } + ; + +/** + * DROP ROLE [IF EXISTS] <rolename> + */ +dropRoleStatement returns [DropRoleStatement stmt] + @init { + boolean ifExists = false; + } + : K_DROP K_ROLE (K_IF K_EXISTS { ifExists = true; })? name=userOrRoleName + { $stmt = new DropRoleStatement(name, ifExists); } + ; + +/** + * LIST ROLES [OF <rolename>] [NORECURSIVE] + */ +listRolesStatement returns [ListRolesStatement stmt] + @init { + boolean recursive = true; + RoleName grantee = new RoleName(); + } + : K_LIST K_ROLES + ( K_OF roleName[grantee])? + ( K_NORECURSIVE { recursive = false; } )? + { $stmt = new ListRolesStatement(grantee, recursive); } + ; + +roleOptions[RoleOptions opts] + : roleOption[opts] (K_AND roleOption[opts])* + ; + +roleOption[RoleOptions opts] + : K_PASSWORD '=' v=STRING_LITERAL { opts.setOption(IRoleManager.Option.PASSWORD, $v.text); } + | K_OPTIONS '=' m=mapLiteral { opts.setOption(IRoleManager.Option.OPTIONS, convertPropertyMap(m)); } + | K_SUPERUSER '=' b=BOOLEAN { opts.setOption(IRoleManager.Option.SUPERUSER, Boolean.valueOf($b.text)); } + | K_LOGIN '=' b=BOOLEAN { opts.setOption(IRoleManager.Option.LOGIN, Boolean.valueOf($b.text)); } + ; + +// for backwards compatibility in CREATE/ALTER USER, this has no '=' +userPassword[RoleOptions opts] + : K_PASSWORD v=STRING_LITERAL { opts.setOption(IRoleManager.Option.PASSWORD, $v.text); } + ; + +/** DEFINITIONS **/ + +// Column Identifiers. These need to be treated differently from other +// identifiers because the underlying comparator is not necessarily text. See +// CASSANDRA-8178 for details. +cident returns [ColumnDefinition.Raw id] + : t=IDENT { $id = ColumnDefinition.Raw.forUnquoted($t.text); } + | t=QUOTED_NAME { $id = ColumnDefinition.Raw.forQuoted($t.text); } + | k=unreserved_keyword { $id = ColumnDefinition.Raw.forUnquoted(k); } + ; + +// Column identifiers where the comparator is known to be text +ident returns [ColumnIdentifier id] + : t=IDENT { $id = ColumnIdentifier.getInterned($t.text, false); } + | t=QUOTED_NAME { $id = ColumnIdentifier.getInterned($t.text, true); } + | k=unreserved_keyword { $id = ColumnIdentifier.getInterned(k, false); } + ; + +fident returns [FieldIdentifier id] + : t=IDENT { $id = FieldIdentifier.forUnquoted($t.text); } + | t=QUOTED_NAME { $id = FieldIdentifier.forQuoted($t.text); } + | k=unreserved_keyword { $id = FieldIdentifier.forUnquoted(k); } + ; + +// Identifiers that do not refer to columns +noncol_ident returns [ColumnIdentifier id] + : t=IDENT { $id = new ColumnIdentifier($t.text, false); } + | t=QUOTED_NAME { $id = new ColumnIdentifier($t.text, true); } + | k=unreserved_keyword { $id = new ColumnIdentifier(k, false); } + ; + +// Keyspace & Column family names +keyspaceName returns [String id] + @init { CFName name = new CFName(); } + : ksName[name] { $id = name.getKeyspace(); } + ; + +indexName returns [IndexName name] + @init { $name = new IndexName(); } + : (ksName[name] '.')? idxName[name] + ; + +columnFamilyName returns [CFName name] + @init { $name = new CFName(); } + : (ksName[name] '.')? cfName[name] + ; + +userTypeName returns [UTName name] + : (ks=noncol_ident '.')? ut=non_type_ident { $name = new UTName(ks, ut); } + ; + +userOrRoleName returns [RoleName name] + @init { RoleName role = new RoleName(); } + : roleName[role] {$name = role;} + ; + +ksName[KeyspaceElementName name] + : t=IDENT { $name.setKeyspace($t.text, false);} + | t=QUOTED_NAME { $name.setKeyspace($t.text, true);} + | k=unreserved_keyword { $name.setKeyspace(k, false);} + | QMARK {addRecognitionError("Bind variables cannot be used for keyspace names");} + ; + +cfName[CFName name] + : t=IDENT { $name.setColumnFamily($t.text, false); } + | t=QUOTED_NAME { $name.setColumnFamily($t.text, true); } + | k=unreserved_keyword { $name.setColumnFamily(k, false); } + | QMARK {addRecognitionError("Bind variables cannot be used for table names");} + ; + +idxName[IndexName name] + : t=IDENT { $name.setIndex($t.text, false); } + | t=QUOTED_NAME { $name.setIndex($t.text, true);} + | k=unreserved_keyword { $name.setIndex(k, false); } + | QMARK {addRecognitionError("Bind variables cannot be used for index names");} + ; + +roleName[RoleName name] + : t=IDENT { $name.setName($t.text, false); } + | s=STRING_LITERAL { $name.setName($s.text, true); } + | t=QUOTED_NAME { $name.setName($t.text, true); } + | k=unreserved_keyword { $name.setName(k, false); } + | QMARK {addRecognitionError("Bind variables cannot be used for role names");} + ; + +constant returns [Constants.Literal constant] + : t=STRING_LITERAL { $constant = Constants.Literal.string($t.text); } + | t=INTEGER { $constant = Constants.Literal.integer($t.text); } + | t=FLOAT { $constant = Constants.Literal.floatingPoint($t.text); } + | t=BOOLEAN { $constant = Constants.Literal.bool($t.text); } + | t=UUID { $constant = Constants.Literal.uuid($t.text); } + | t=HEXNUMBER { $constant = Constants.Literal.hex($t.text); } + | { String sign=""; } ('-' {sign = "-"; } )? t=(K_NAN | K_INFINITY) { $constant = Constants.Literal.floatingPoint(sign + $t.text); } + ; + +mapLiteral returns [Maps.Literal map] + : '{' { List<Pair<Term.Raw, Term.Raw>> m = new ArrayList<Pair<Term.Raw, Term.Raw>>(); } + ( k1=term ':' v1=term { m.add(Pair.create(k1, v1)); } ( ',' kn=term ':' vn=term { m.add(Pair.create(kn, vn)); } )* )? + '}' { $map = new Maps.Literal(m); } + ; + +setOrMapLiteral[Term.Raw t] returns [Term.Raw value] + : ':' v=term { List<Pair<Term.Raw, Term.Raw>> m = new ArrayList<Pair<Term.Raw, Term.Raw>>(); m.add(Pair.create(t, v)); } + ( ',' kn=term ':' vn=term { m.add(Pair.create(kn, vn)); } )* + { $value = new Maps.Literal(m); } + | { List<Term.Raw> s = new ArrayList<Term.Raw>(); s.add(t); } + ( ',' tn=term { s.add(tn); } )* + { $value = new Sets.Literal(s); } + ; + +collectionLiteral returns [Term.Raw value] + : '[' { List<Term.Raw> l = new ArrayList<Term.Raw>(); } + ( t1=term { l.add(t1); } ( ',' tn=term { l.add(tn); } )* )? + ']' { $value = new Lists.Literal(l); } + | '{' t=term v=setOrMapLiteral[t] { $value = v; } '}' + // Note that we have an ambiguity between maps and set for "{}". So we force it to a set literal, + // and deal with it later based on the type of the column (SetLiteral.java). + | '{' '}' { $value = new Sets.Literal(Collections.<Term.Raw>emptyList()); } + ; + +usertypeLiteral returns [UserTypes.Literal ut] + @init{ Map<FieldIdentifier, Term.Raw> m = new HashMap<>(); } + @after{ $ut = new UserTypes.Literal(m); } + // We don't allow empty literals because that conflicts with sets/maps and is currently useless since we don't allow empty user types + : '{' k1=fident ':' v1=term { m.put(k1, v1); } ( ',' kn=fident ':' vn=term { m.put(kn, vn); } )* '}' + ; + +tupleLiteral returns [Tuples.Literal tt] + @init{ List<Term.Raw> l = new ArrayList<Term.Raw>(); } + @after{ $tt = new Tuples.Literal(l); } + : '(' t1=term { l.add(t1); } ( ',' tn=term { l.add(tn); } )* ')' + ; + +value returns [Term.Raw value] + : c=constant { $value = c; } + | l=collectionLiteral { $value = l; } + | u=usertypeLiteral { $value = u; } + | t=tupleLiteral { $value = t; } + | K_NULL { $value = Constants.NULL_LITERAL; } + | ':' id=noncol_ident { $value = newBindVariables(id); } + | QMARK { $value = newBindVariables(null); } + ; + +intValue returns [Term.Raw value] + : t=INTEGER { $value = Constants.Literal.integer($t.text); } + | ':' id=noncol_ident { $value = newBindVariables(id); } + | QMARK { $value = newBindVariables(null); } + ; + +functionName returns [FunctionName s] + : (ks=keyspaceName '.')? f=allowedFunctionName { $s = new FunctionName(ks, f); } + ; + +allowedFunctionName returns [String s] + : f=IDENT { $s = $f.text.toLowerCase(); } + | f=QUOTED_NAME { $s = $f.text; } + | u=unreserved_function_keyword { $s = u; } + | K_TOKEN { $s = "token"; } + | K_COUNT { $s = "count"; } + ; + +function returns [Term.Raw t] + : f=functionName '(' ')' { $t = new FunctionCall.Raw(f, Collections.<Term.Raw>emptyList()); } + | f=functionName '(' args=functionArgs ')' { $t = new FunctionCall.Raw(f, args); } + ; + +functionArgs returns [List<Term.Raw> args] + @init{ $args = new ArrayList<Term.Raw>(); } + : t1=term {args.add(t1); } ( ',' tn=term { args.add(tn); } )* + ; + +term returns [Term.Raw term] + : v=value { $term = v; } + | f=function { $term = f; } + | '(' c=comparatorType ')' t=term { $term = new TypeCast(c, t); } + ; + +columnOperation[List<Pair<ColumnDefinition.Raw, Operation.RawUpdate>> operations] + : key=cident columnOperationDifferentiator[operations, key] + ; + +columnOperationDifferentiator[List<Pair<ColumnDefinition.Raw, Operation.RawUpdate>> operations, ColumnDefinition.Raw key] + : '=' normalColumnOperation[operations, key] + | shorthandColumnOperation[operations, key] + | '[' k=term ']' collectionColumnOperation[operations, key, k] + | '.' field=fident udtColumnOperation[operations, key, field] + ; + +normalColumnOperation[List<Pair<ColumnDefinition.Raw, Operation.RawUpdate>> operations, ColumnDefinition.Raw key] + : t=term ('+' c=cident )? + { + if (c == null) + { + addRawUpdate(operations, key, new Operation.SetValue(t)); + } + else + { + if (!key.equals(c)) + addRecognitionError("Only expressions of the form X = <value> + X are supported."); + addRawUpdate(operations, key, new Operation.Prepend(t)); + } + } + | c=cident sig=('+' | '-') t=term + { + if (!key.equals(c)) + addRecognitionError("Only expressions of the form X = X " + $sig.text + "<value> are supported."); + addRawUpdate(operations, key, $sig.text.equals("+") ? new Operation.Addition(t) : new Operation.Substraction(t)); + } + | c=cident i=INTEGER + { + // Note that this production *is* necessary because X = X - 3 will in fact be lexed as [ X, '=', X, INTEGER]. + if (!key.equals(c)) + // We don't yet allow a '+' in front of an integer, but we could in the future really, so let's be future-proof in our error message + addRecognitionError("Only expressions of the form X = X " + ($i.text.charAt(0) == '-' ? '-' : '+') + " <value> are supported."); + addRawUpdate(operations, key, new Operation.Addition(Constants.Literal.integer($i.text))); + } + ; + +shorthandColumnOperation[List<Pair<ColumnDefinition.Raw, Operation.RawUpdate>> operations, ColumnDefinition.Raw key] + : sig=('+=' | '-=') t=term + { + addRawUpdate(operations, key, $sig.text.equals("+=") ? new Operation.Addition(t) : new Operation.Substraction(t)); + } + ; + +collectionColumnOperation[List<Pair<ColumnDefinition.Raw, Operation.RawUpdate>> operations, ColumnDefinition.Raw key, Term.Raw k] + : '=' t=term + { + addRawUpdate(operations, key, new Operation.SetElement(k, t)); + } + ; + +udtColumnOperation[List<Pair<ColumnDefinition.Raw, Operation.RawUpdate>> operations, ColumnDefinition.Raw key, FieldIdentifier field] + : '=' t=term + { + addRawUpdate(operations, key, new Operation.SetField(field, t)); + } + ; + +columnCondition[List<Pair<ColumnDefinition.Raw, ColumnCondition.Raw>> conditions] + // Note: we'll reject duplicates later + : key=cident + ( op=relationType t=term { conditions.add(Pair.create(key, ColumnCondition.Raw.simpleCondition(t, op))); } + | K_IN + ( values=singleColumnInValues { conditions.add(Pair.create(key, ColumnCondition.Raw.simpleInCondition(values))); } + | marker=inMarker { conditions.add(Pair.create(key, ColumnCondition.Raw.simpleInCondition(marker))); } + ) + | '[' element=term ']' + ( op=relationType t=term { conditions.add(Pair.create(key, ColumnCondition.Raw.collectionCondition(t, element, op))); } + | K_IN + ( values=singleColumnInValues { conditions.add(Pair.create(key, ColumnCondition.Raw.collectionInCondition(element, values))); } + | marker=inMarker { conditions.add(Pair.create(key, ColumnCondition.Raw.collectionInCondition(element, marker))); } + ) + ) + | '.' field=fident + ( op=relationType t=term { conditions.add(Pair.create(key, ColumnCondition.Raw.udtFieldCondition(t, field, op))); } + | K_IN + ( values=singleColumnInValues { conditions.add(Pair.create(key, ColumnCondition.Raw.udtFieldInCondition(field, values))); } + | marker=inMarker { conditions.add(Pair.create(key, ColumnCondition.Raw.udtFieldInCondition(field, marker))); } + ) + ) + ) + ; + +properties[PropertyDefinitions props] + : property[props] (K_AND property[props])* + ; + +property[PropertyDefinitions props] + : k=noncol_ident '=' simple=propertyValue { try { $props.addProperty(k.toString(), simple); } catch (SyntaxException e) { addRecognitionError(e.getMessage()); } } + | k=noncol_ident '=' map=mapLiteral { try { $props.addProperty(k.toString(), convertPropertyMap(map)); } catch (SyntaxException e) { addRecognitionError(e.getMessage()); } } + ; + +propertyValue returns [String str] + : c=constant { $str = c.getRawText(); } + | u=unreserved_keyword { $str = u; } + ; + +relationType returns [Operator op] + : '=' { $op = Operator.EQ; } + | '<' { $op = Operator.LT; } + | '<=' { $op = Operator.LTE; } + | '>' { $op = Operator.GT; } + | '>=' { $op = Operator.GTE; } + | '!=' { $op = Operator.NEQ; } + ; + +relation[WhereClause.Builder clauses] + : name=cident type=relationType t=term { $clauses.add(new SingleColumnRelation(name, type, t)); } + | name=cident K_LIKE t=term { $clauses.add(new SingleColumnRelation(name, Operator.LIKE, t)); } + | name=cident K_IS K_NOT K_NULL { $clauses.add(new SingleColumnRelation(name, Operator.IS_NOT, Constants.NULL_LITERAL)); } + | K_TOKEN l=tupleOfIdentifiers type=relationType t=term + { $clauses.add(new TokenRelation(l, type, t)); } + | name=cident K_IN marker=inMarker + { $clauses.add(new SingleColumnRelation(name, Operator.IN, marker)); } + | name=cident K_IN inValues=singleColumnInValues + { $clauses.add(SingleColumnRelation.createInRelation($name.id, inValues)); } + | name=cident K_CONTAINS { Operator rt = Operator.CONTAINS; } (K_KEY { rt = Operator.CONTAINS_KEY; })? + t=term { $clauses.add(new SingleColumnRelation(name, rt, t)); } + | name=cident '[' key=term ']' type=relationType t=term { $clauses.add(new SingleColumnRelation(name, key, type, t)); } + | ids=tupleOfIdentifiers + ( K_IN + ( '(' ')' + { $clauses.add(MultiColumnRelation.createInRelation(ids, new ArrayList<Tuples.Literal>())); } + | tupleInMarker=inMarkerForTuple /* (a, b, c) IN ? */ + { $clauses.add(MultiColumnRelation.createSingleMarkerInRelation(ids, tupleInMarker)); } + | literals=tupleOfTupleLiterals /* (a, b, c) IN ((1, 2, 3), (4, 5, 6), ...) */ + { + $clauses.add(MultiColumnRelation.createInRelation(ids, literals)); + } + | markers=tupleOfMarkersForTuples /* (a, b, c) IN (?, ?, ...) */ + { $clauses.add(MultiColumnRelation.createInRelation(ids, markers)); } + ) + | type=relationType literal=tupleLiteral /* (a, b, c) > (1, 2, 3) or (a, b, c) > (?, ?, ?) */ + { + $clauses.add(MultiColumnRelation.createNonInRelation(ids, type, literal)); + } + | type=relationType tupleMarker=markerForTuple /* (a, b, c) >= ? */ + { $clauses.add(MultiColumnRelation.createNonInRelation(ids, type, tupleMarker)); } + ) + | '(' relation[$clauses] ')' + ; + +inMarker returns [AbstractMarker.INRaw marker] + : QMARK { $marker = newINBindVariables(null); } + | ':' name=noncol_ident { $marker = newINBindVariables(name); } + ; + +tupleOfIdentifiers returns [List<ColumnDefinition.Raw> ids] + @init { $ids = new ArrayList<ColumnDefinition.Raw>(); } + : '(' n1=cident { $ids.add(n1); } (',' ni=cident { $ids.add(ni); })* ')' + ; + +singleColumnInValues returns [List<Term.Raw> terms] + @init { $terms = new ArrayList<Term.Raw>(); } + : '(' ( t1 = term { $terms.add(t1); } (',' ti=term { $terms.add(ti); })* )? ')' + ; + +tupleOfTupleLiterals returns [List<Tuples.Literal> literals] + @init { $literals = new ArrayList<>(); } + : '(' t1=tupleLiteral { $literals.add(t1); } (',' ti=tupleLiteral { $literals.add(ti); })* ')' + ; + +markerForTuple returns [Tuples.Raw marker] + : QMARK { $marker = newTupleBindVariables(null); } + | ':' name=noncol_ident { $marker = newTupleBindVariables(name); } + ; + +tupleOfMarkersForTuples returns [List<Tuples.Raw> markers] + @init { $markers = new ArrayList<Tuples.Raw>(); } + : '(' m1=markerForTuple { $markers.add(m1); } (',' mi=markerForTuple { $markers.add(mi); })* ')' + ; + +inMarkerForTuple returns [Tuples.INRaw marker] + : QMARK { $marker = newTupleINBindVariables(null); } + | ':' name=noncol_ident { $marker = newTupleINBindVariables(name); } + ; + +comparatorType returns [CQL3Type.Raw t] + : n=native_type { $t = CQL3Type.Raw.from(n); } + | c=collection_type { $t = c; } + | tt=tuple_type { $t = tt; } + | id=userTypeName { $t = CQL3Type.Raw.userType(id); } + | K_FROZEN '<' f=comparatorType '>' + { + try { + $t = CQL3Type.Raw.frozen(f); + } catch (InvalidRequestException e) { + addRecognitionError(e.getMessage()); + } + } + | s=STRING_LITERAL + { + try { + $t = CQL3Type.Raw.from(new CQL3Type.Custom($s.text)); + } catch (SyntaxException e) { + addRecognitionError("Cannot parse type " + $s.text + ": " + e.getMessage()); + } catch (ConfigurationException e) { + addRecognitionError("Error setting type " + $s.text + ": " + e.getMessage()); + } + } + ; + +native_type returns [CQL3Type t] + : K_ASCII { $t = CQL3Type.Native.ASCII; } + | K_BIGINT { $t = CQL3Type.Native.BIGINT; } + | K_BLOB { $t = CQL3Type.Native.BLOB; } + | K_BOOLEAN { $t = CQL3Type.Native.BOOLEAN; } + | K_COUNTER { $t = CQL3Type.Native.COUNTER; } + | K_DECIMAL { $t = CQL3Type.Native.DECIMAL; } + | K_DOUBLE { $t = CQL3Type.Native.DOUBLE; } + | K_FLOAT { $t = CQL3Type.Native.FLOAT; } + | K_INET { $t = CQL3Type.Native.INET;} + | K_INT { $t = CQL3Type.Native.INT; } + | K_SMALLINT { $t = CQL3Type.Native.SMALLINT; } + | K_TEXT { $t = CQL3Type.Native.TEXT; } + | K_TIMESTAMP { $t = CQL3Type.Native.TIMESTAMP; } + | K_TINYINT { $t = CQL3Type.Native.TINYINT; } + | K_UUID { $t = CQL3Type.Native.UUID; } + | K_VARCHAR { $t = CQL3Type.Native.VARCHAR; } + | K_VARINT { $t = CQL3Type.Native.VARINT; } + | K_TIMEUUID { $t = CQL3Type.Native.TIMEUUID; } + | K_DATE { $t = CQL3Type.Native.DATE; } + | K_TIME { $t = CQL3Type.Native.TIME; } + ; + +collection_type returns [CQL3Type.Raw pt] + : K_MAP '<' t1=comparatorType ',' t2=comparatorType '>' + { + // if we can't parse either t1 or t2, antlr will "recover" and we may have t1 or t2 null. + if (t1 != null && t2 != null) + $pt = CQL3Type.Raw.map(t1, t2); + } + | K_LIST '<' t=comparatorType '>' + { if (t != null) $pt = CQL3Type.Raw.list(t); } + | K_SET '<' t=comparatorType '>' + { if (t != null) $pt = CQL3Type.Raw.set(t); } + ; + +tuple_type returns [CQL3Type.Raw t] + : K_TUPLE '<' { List<CQL3Type.Raw> types = new ArrayList<>(); } + t1=comparatorType { types.add(t1); } (',' tn=comparatorType { types.add(tn); })* + '>' { $t = CQL3Type.Raw.tuple(types); } + ; + +username + : IDENT + | STRING_LITERAL + | QUOTED_NAME { addRecognitionError("Quoted strings are are not supported for user names and USER is deprecated, please use ROLE");} + ; + +mbean + : STRING_LITERAL + ; + +// Basically the same as cident, but we need to exlude existing CQL3 types +// (which for some reason are not reserved otherwise) +non_type_ident returns [ColumnIdentifier id] + : t=IDENT { if (reservedTypeNames.contains($t.text)) addRecognitionError("Invalid (reserved) user type name " + $t.text); $id = new ColumnIdentifier($t.text, false); } + | t=QUOTED_NAME { $id = new ColumnIdentifier($t.text, true); } + | k=basic_unreserved_keyword { $id = new ColumnIdentifier(k, false); } + | kk=K_KEY { $id = new ColumnIdentifier($kk.text, false); } + ; + +unreserved_keyword returns [String str] + : u=unreserved_function_keyword { $str = u; } + | k=(K_TTL | K_COUNT | K_WRITETIME | K_KEY | K_CAST | K_JSON | K_DISTINCT) { $str = $k.text; } + ; + +unreserved_function_keyword returns [String str] + : u=basic_unreserved_keyword { $str = u; } + | t=native_type { $str = t.toString(); } + ; + +basic_unreserved_keyword returns [String str] + : k=( K_KEYS + | K_AS + | K_CLUSTERING + | K_COMPACT + | K_STORAGE + | K_TYPE + | K_VALUES + | K_MAP + | K_LIST + | K_FILTERING + | K_PERMISSION + | K_PERMISSIONS + | K_KEYSPACES + | K_ALL + | K_USER + | K_USERS + | K_ROLE + | K_ROLES + | K_SUPERUSER + | K_NOSUPERUSER + | K_LOGIN + | K_NOLOGIN + | K_OPTIONS + | K_PASSWORD + | K_EXISTS + | K_CUSTOM + | K_TRIGGER + | K_CONTAINS + | K_STATIC + | K_FROZEN + | K_TUPLE + | K_FUNCTION + | K_FUNCTIONS + | K_AGGREGATE + | K_SFUNC + | K_STYPE + | K_FINALFUNC + | K_INITCOND + | K_RETURNS + | K_LANGUAGE + | K_CALLED + | K_INPUT + | K_LIKE + | K_PER + | K_PARTITION + | K_GROUP + ) { $str = $k.text; } - ; ++ ;