├── .gitignore ├── src ├── main │ ├── java │ │ └── com │ │ │ └── kunalanand │ │ │ └── ksql │ │ │ ├── StatementType.java │ │ │ ├── Main.java │ │ │ └── Intelligence.java │ └── antlr4 │ │ └── com │ │ └── kunalanand │ │ └── ksql │ │ ├── KSQL.tokens │ │ ├── KSQLLexer.tokens │ │ ├── KSQL.g4 │ │ ├── KSQLListener.java │ │ ├── KSQLBaseListener.java │ │ ├── KSQLLexer.java │ │ └── KSQLParser.java └── test │ └── java │ └── com │ └── kunalanand │ └── ksql │ ├── SecurityTest.java │ └── IntelligenceTest.java ├── LICENSE ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea/ 3 | *.iml 4 | *.DS_Store 5 | *.class -------------------------------------------------------------------------------- /src/main/java/com/kunalanand/ksql/StatementType.java: -------------------------------------------------------------------------------- 1 | package com.kunalanand.ksql; 2 | 3 | public enum StatementType { 4 | SELECT, 5 | DELETE 6 | } 7 | -------------------------------------------------------------------------------- /src/main/antlr4/com/kunalanand/ksql/KSQL.tokens: -------------------------------------------------------------------------------- 1 | SELECT=1 2 | DELETE=2 3 | FROM=3 4 | WHERE=4 5 | AND=5 6 | OR=6 7 | STAR=7 8 | EQ=8 9 | ID=9 10 | INT=10 11 | COMMA=11 12 | SEMICOLON=12 13 | WS=13 14 | '*'=7 15 | '='=8 16 | ','=11 17 | ';'=12 18 | -------------------------------------------------------------------------------- /src/main/antlr4/com/kunalanand/ksql/KSQLLexer.tokens: -------------------------------------------------------------------------------- 1 | SELECT=1 2 | DELETE=2 3 | FROM=3 4 | WHERE=4 5 | AND=5 6 | OR=6 7 | STAR=7 8 | EQ=8 9 | ID=9 10 | INT=10 11 | COMMA=11 12 | SEMICOLON=12 13 | WS=13 14 | '*'=7 15 | '='=8 16 | ','=11 17 | ';'=12 18 | -------------------------------------------------------------------------------- /src/main/java/com/kunalanand/ksql/Main.java: -------------------------------------------------------------------------------- 1 | package com.kunalanand.ksql; 2 | 3 | public class Main { 4 | public static void main( String[] args ) throws Exception { 5 | String sql = "SELECT hello FROM world"; 6 | System.out.println( Intelligence.build( sql ) ); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/antlr4/com/kunalanand/ksql/KSQL.g4: -------------------------------------------------------------------------------- 1 | grammar KSQL; 2 | 3 | expr : deleteExpr | selectExpr ; 4 | 5 | deleteExpr : DELETE FROM tableName ; 6 | selectExpr : SELECT columnList FROM tableName (WHERE whereExpr)? (SEMICOLON)? ; 7 | 8 | columnList : wildcard | columnName ( COMMA columnName )* ; 9 | columnName : ID ( ID )? ; 10 | whereExpr : dynamicComparison | staticComparison ; 11 | dynamicComparison : ID EQ INT ; 12 | staticComparison : INT EQ INT ; 13 | 14 | wildcard : STAR ; 15 | tableName : ID ; 16 | 17 | // Keywords & Reserved 18 | SELECT : 'SELECT' | 'select' ; 19 | DELETE : 'DELETE' | 'delete' ; 20 | FROM : 'FROM' | 'from' ; 21 | WHERE : 'WHERE' | 'where' ; 22 | AND : 'AND' | 'and' ; 23 | OR : 'OR' | 'or' ; 24 | STAR : '*' ; 25 | EQ : '=' ; 26 | 27 | // Identifiers 28 | ID : ( 'a'..'z' | 'A'..'Z' )+ ; 29 | INT : [0-9]+ ; 30 | 31 | // Punctuation 32 | COMMA : ',' -> skip ; 33 | SEMICOLON : ';' -> skip ; 34 | WS : [ \t\r\n]+ -> skip ; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Kunal Anand (kunalanand.com) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KSQL 2 | 3 | A minimalist SQL designed for my AppSec USA 2016 talk about LANGSEC. 4 | 5 | Project includes an ANTLR4 grammar and Java code. 6 | 7 | ## Install 8 | 9 | To install this library, do the following: 10 | 11 | ```bash 12 | git clone https://github.com/anandkunal/ksql 13 | cd ksql 14 | mvn clean install 15 | ``` 16 | 17 | ## Example Queries 18 | 19 | The following queries are valid KSQL: 20 | 21 | ### SELECT 22 | 23 | ```sql 24 | SELECT * FROM users 25 | SELECT username FROM users 26 | SELECT username, password FROM users 27 | SELECT username, password FROM users WHERE id=1 28 | SELECT username, password FROM users WHERE 1=1 29 | SELECT username, password FROM users WHERE 1=2 30 | ``` 31 | 32 | ### DELETE 33 | 34 | ```sql 35 | DELETE FROM users 36 | ``` 37 | 38 | A terminating semicolon (`;`) is optional for all of the example queries. 39 | 40 | 41 | ## Grammar Visualization 42 | 43 | ANTLR4 ships with a lexer/parser visualization tool to guide the construction of a grammar. The following is an example command that can be run from the KSQL grammar directory: 44 | 45 | ```bash 46 | antlr4 KSQL.g4 && javac KSQL*.java; grun KSQL expr -gui 47 | ``` 48 | 49 | As a fun test, you can run the above example queries through this visualization tool. 50 | 51 | ## Use Cases 52 | 53 | - Look for bad statement types 54 | - Look for access to bad columns 55 | - Look for access to bad tables 56 | - Look for tautologies and contradictions 57 | -------------------------------------------------------------------------------- /src/test/java/com/kunalanand/ksql/SecurityTest.java: -------------------------------------------------------------------------------- 1 | package com.kunalanand.ksql; 2 | 3 | import java.util.HashSet; 4 | 5 | import org.junit.Test; 6 | 7 | import junit.framework.Assert; 8 | 9 | public class SecurityTest { 10 | @Test 11 | public void testGoodStatement() { 12 | String sql = "SELECT * FROM users"; 13 | Assert.assertTrue( Intelligence.build( sql ) 14 | .secure( new HashSet(), new HashSet(), new HashSet(), false ) ); 15 | } 16 | 17 | @Test 18 | public void testBadStatement() { 19 | HashSet badStatements = new HashSet(); 20 | badStatements.add( StatementType.DELETE ); 21 | 22 | String sql = "DELETE FROM users"; 23 | Assert.assertFalse( Intelligence.build( sql ) 24 | .secure( badStatements, new HashSet(), new HashSet(), false ) ); 25 | } 26 | 27 | @Test 28 | public void testGoodTable() { 29 | String sql = "SELECT * FROM users"; 30 | Assert.assertTrue( Intelligence.build( sql ) 31 | .secure( new HashSet(), new HashSet(), new HashSet(), false ) ); 32 | } 33 | 34 | @Test 35 | public void testBadTable() { 36 | HashSet badTables = new HashSet(); 37 | badTables.add( "users" ); 38 | 39 | String sql = "SELECT * FROM users"; 40 | Assert.assertFalse( Intelligence.build( sql ) 41 | .secure( new HashSet(), badTables, new HashSet(), false ) ); 42 | } 43 | 44 | @Test 45 | public void testGoodColumns() { 46 | String sql = "SELECT username, password FROM users"; 47 | Assert.assertTrue( Intelligence.build( sql ) 48 | .secure( new HashSet(), new HashSet(), new HashSet(), false ) ); 49 | } 50 | 51 | @Test 52 | public void testBadColumns() { 53 | HashSet badColumns = new HashSet(); 54 | badColumns.add( "password" ); 55 | 56 | String sql = "SELECT username, password FROM users"; 57 | Assert.assertFalse( Intelligence.build( sql ) 58 | .secure( new HashSet(), new HashSet(), badColumns, false ) ); 59 | } 60 | 61 | @Test 62 | public void testTautologiesSafe() { 63 | String sql = "SELECT * FROM users WHERE 1=1"; 64 | Assert.assertTrue( Intelligence.build( sql ) 65 | .secure( new HashSet(), new HashSet(), new HashSet(), false ) ); 66 | } 67 | 68 | @Test 69 | public void testTautologiesUnsafe() { 70 | String sql = "SELECT * FROM users WHERE 1=1"; 71 | Assert.assertFalse( Intelligence.build( sql ) 72 | .secure( new HashSet(), new HashSet(), new HashSet(), true ) ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/com/kunalanand/ksql/IntelligenceTest.java: -------------------------------------------------------------------------------- 1 | package com.kunalanand.ksql; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class IntelligenceTest { 7 | @Test 8 | public void testWildcardQuery() { 9 | String sql = "SELECT * FROM users"; 10 | Intelligence intelligence = Intelligence.build( sql ); 11 | Assert.assertEquals( StatementType.SELECT, intelligence.getStatementType() ); 12 | Assert.assertEquals( 1, intelligence.getColumns().size() ); 13 | Assert.assertTrue( intelligence.getColumns().contains( "*" ) ); 14 | Assert.assertEquals( "users", intelligence.getTable() ); 15 | } 16 | 17 | @Test 18 | public void testSingleColumnQuery() { 19 | String sql = "SELECT username FROM users"; 20 | Intelligence intelligence = Intelligence.build( sql ); 21 | Assert.assertEquals( StatementType.SELECT, intelligence.getStatementType() ); 22 | Assert.assertEquals( 1, intelligence.getColumns().size() ); 23 | Assert.assertTrue( intelligence.getColumns().contains( "username" ) ); 24 | Assert.assertEquals( "users", intelligence.getTable() ); 25 | } 26 | 27 | @Test 28 | public void testMultiColumnQuery() { 29 | String sql = "SELECT username, password FROM users"; 30 | Intelligence intelligence = Intelligence.build( sql ); 31 | Assert.assertEquals( StatementType.SELECT, intelligence.getStatementType() ); 32 | Assert.assertEquals( 2, intelligence.getColumns().size() ); 33 | Assert.assertTrue( intelligence.getColumns().contains( "username" ) ); 34 | Assert.assertTrue( intelligence.getColumns().contains( "password" ) ); 35 | Assert.assertEquals( "users", intelligence.getTable() ); 36 | } 37 | 38 | @Test 39 | public void testDynamicComparison() { 40 | String sql = "SELECT username, password FROM users WHERE id=1"; 41 | Intelligence intelligence = Intelligence.build( sql ); 42 | Assert.assertEquals( StatementType.SELECT, intelligence.getStatementType() ); 43 | Assert.assertEquals( 2, intelligence.getColumns().size() ); 44 | Assert.assertTrue( intelligence.getColumns().contains( "username" ) ); 45 | Assert.assertTrue( intelligence.getColumns().contains( "password" ) ); 46 | Assert.assertEquals( "users", intelligence.getTable() ); 47 | Assert.assertEquals( "id", intelligence.getDynamicColumn() ); 48 | Assert.assertEquals( "1", intelligence.getDynamicValue().getText() ); 49 | } 50 | 51 | @Test 52 | public void testStaticComparison() { 53 | String sql = "SELECT username, password FROM users WHERE 1=1"; 54 | Intelligence intelligence = Intelligence.build( sql ); 55 | Assert.assertEquals( StatementType.SELECT, intelligence.getStatementType() ); 56 | Assert.assertEquals( 2, intelligence.getColumns().size() ); 57 | Assert.assertTrue( intelligence.getColumns().contains( "username" ) ); 58 | Assert.assertTrue( intelligence.getColumns().contains( "password" ) ); 59 | Assert.assertEquals( "users", intelligence.getTable() ); 60 | Assert.assertEquals( "1", intelligence.getStaticValue1().getText() ); 61 | Assert.assertEquals( "1", intelligence.getStaticValue2().getText() ); 62 | } 63 | 64 | @Test 65 | public void testDelete() { 66 | String sql = "DELETE FROM users"; 67 | Intelligence intelligence = Intelligence.build( sql ); 68 | Assert.assertEquals( StatementType.DELETE, intelligence.getStatementType() ); 69 | Assert.assertEquals( "users", intelligence.getTable() ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/antlr4/com/kunalanand/ksql/KSQLListener.java: -------------------------------------------------------------------------------- 1 | // Generated from KSQL.g4 by ANTLR 4.5 2 | import org.antlr.v4.runtime.misc.NotNull; 3 | import org.antlr.v4.runtime.tree.ParseTreeListener; 4 | 5 | /** 6 | * This interface defines a complete listener for a parse tree produced by 7 | * {@link KSQLParser}. 8 | */ 9 | public interface KSQLListener extends ParseTreeListener { 10 | /** 11 | * Enter a parse tree produced by {@link KSQLParser#expr}. 12 | * @param ctx the parse tree 13 | */ 14 | void enterExpr(KSQLParser.ExprContext ctx); 15 | /** 16 | * Exit a parse tree produced by {@link KSQLParser#expr}. 17 | * @param ctx the parse tree 18 | */ 19 | void exitExpr(KSQLParser.ExprContext ctx); 20 | /** 21 | * Enter a parse tree produced by {@link KSQLParser#deleteExpr}. 22 | * @param ctx the parse tree 23 | */ 24 | void enterDeleteExpr(KSQLParser.DeleteExprContext ctx); 25 | /** 26 | * Exit a parse tree produced by {@link KSQLParser#deleteExpr}. 27 | * @param ctx the parse tree 28 | */ 29 | void exitDeleteExpr(KSQLParser.DeleteExprContext ctx); 30 | /** 31 | * Enter a parse tree produced by {@link KSQLParser#selectExpr}. 32 | * @param ctx the parse tree 33 | */ 34 | void enterSelectExpr(KSQLParser.SelectExprContext ctx); 35 | /** 36 | * Exit a parse tree produced by {@link KSQLParser#selectExpr}. 37 | * @param ctx the parse tree 38 | */ 39 | void exitSelectExpr(KSQLParser.SelectExprContext ctx); 40 | /** 41 | * Enter a parse tree produced by {@link KSQLParser#columnList}. 42 | * @param ctx the parse tree 43 | */ 44 | void enterColumnList(KSQLParser.ColumnListContext ctx); 45 | /** 46 | * Exit a parse tree produced by {@link KSQLParser#columnList}. 47 | * @param ctx the parse tree 48 | */ 49 | void exitColumnList(KSQLParser.ColumnListContext ctx); 50 | /** 51 | * Enter a parse tree produced by {@link KSQLParser#columnName}. 52 | * @param ctx the parse tree 53 | */ 54 | void enterColumnName(KSQLParser.ColumnNameContext ctx); 55 | /** 56 | * Exit a parse tree produced by {@link KSQLParser#columnName}. 57 | * @param ctx the parse tree 58 | */ 59 | void exitColumnName(KSQLParser.ColumnNameContext ctx); 60 | /** 61 | * Enter a parse tree produced by {@link KSQLParser#whereExpr}. 62 | * @param ctx the parse tree 63 | */ 64 | void enterWhereExpr(KSQLParser.WhereExprContext ctx); 65 | /** 66 | * Exit a parse tree produced by {@link KSQLParser#whereExpr}. 67 | * @param ctx the parse tree 68 | */ 69 | void exitWhereExpr(KSQLParser.WhereExprContext ctx); 70 | /** 71 | * Enter a parse tree produced by {@link KSQLParser#dynamicComparison}. 72 | * @param ctx the parse tree 73 | */ 74 | void enterDynamicComparison(KSQLParser.DynamicComparisonContext ctx); 75 | /** 76 | * Exit a parse tree produced by {@link KSQLParser#dynamicComparison}. 77 | * @param ctx the parse tree 78 | */ 79 | void exitDynamicComparison(KSQLParser.DynamicComparisonContext ctx); 80 | /** 81 | * Enter a parse tree produced by {@link KSQLParser#staticComparison}. 82 | * @param ctx the parse tree 83 | */ 84 | void enterStaticComparison(KSQLParser.StaticComparisonContext ctx); 85 | /** 86 | * Exit a parse tree produced by {@link KSQLParser#staticComparison}. 87 | * @param ctx the parse tree 88 | */ 89 | void exitStaticComparison(KSQLParser.StaticComparisonContext ctx); 90 | /** 91 | * Enter a parse tree produced by {@link KSQLParser#wildcard}. 92 | * @param ctx the parse tree 93 | */ 94 | void enterWildcard(KSQLParser.WildcardContext ctx); 95 | /** 96 | * Exit a parse tree produced by {@link KSQLParser#wildcard}. 97 | * @param ctx the parse tree 98 | */ 99 | void exitWildcard(KSQLParser.WildcardContext ctx); 100 | /** 101 | * Enter a parse tree produced by {@link KSQLParser#tableName}. 102 | * @param ctx the parse tree 103 | */ 104 | void enterTableName(KSQLParser.TableNameContext ctx); 105 | /** 106 | * Exit a parse tree produced by {@link KSQLParser#tableName}. 107 | * @param ctx the parse tree 108 | */ 109 | void exitTableName(KSQLParser.TableNameContext ctx); 110 | } -------------------------------------------------------------------------------- /src/main/antlr4/com/kunalanand/ksql/KSQLBaseListener.java: -------------------------------------------------------------------------------- 1 | // Generated from KSQL.g4 by ANTLR 4.5 2 | 3 | import org.antlr.v4.runtime.ParserRuleContext; 4 | import org.antlr.v4.runtime.misc.NotNull; 5 | import org.antlr.v4.runtime.tree.ErrorNode; 6 | import org.antlr.v4.runtime.tree.TerminalNode; 7 | 8 | /** 9 | * This class provides an empty implementation of {@link KSQLListener}, 10 | * which can be extended to create a listener which only needs to handle a subset 11 | * of the available methods. 12 | */ 13 | public class KSQLBaseListener implements KSQLListener { 14 | /** 15 | * {@inheritDoc} 16 | * 17 | *

The default implementation does nothing.

18 | */ 19 | @Override public void enterExpr(KSQLParser.ExprContext ctx) { } 20 | /** 21 | * {@inheritDoc} 22 | * 23 | *

The default implementation does nothing.

24 | */ 25 | @Override public void exitExpr(KSQLParser.ExprContext ctx) { } 26 | /** 27 | * {@inheritDoc} 28 | * 29 | *

The default implementation does nothing.

30 | */ 31 | @Override public void enterDeleteExpr(KSQLParser.DeleteExprContext ctx) { } 32 | /** 33 | * {@inheritDoc} 34 | * 35 | *

The default implementation does nothing.

36 | */ 37 | @Override public void exitDeleteExpr(KSQLParser.DeleteExprContext ctx) { } 38 | /** 39 | * {@inheritDoc} 40 | * 41 | *

The default implementation does nothing.

42 | */ 43 | @Override public void enterSelectExpr(KSQLParser.SelectExprContext ctx) { } 44 | /** 45 | * {@inheritDoc} 46 | * 47 | *

The default implementation does nothing.

48 | */ 49 | @Override public void exitSelectExpr(KSQLParser.SelectExprContext ctx) { } 50 | /** 51 | * {@inheritDoc} 52 | * 53 | *

The default implementation does nothing.

54 | */ 55 | @Override public void enterColumnList(KSQLParser.ColumnListContext ctx) { } 56 | /** 57 | * {@inheritDoc} 58 | * 59 | *

The default implementation does nothing.

60 | */ 61 | @Override public void exitColumnList(KSQLParser.ColumnListContext ctx) { } 62 | /** 63 | * {@inheritDoc} 64 | * 65 | *

The default implementation does nothing.

66 | */ 67 | @Override public void enterColumnName(KSQLParser.ColumnNameContext ctx) { } 68 | /** 69 | * {@inheritDoc} 70 | * 71 | *

The default implementation does nothing.

72 | */ 73 | @Override public void exitColumnName(KSQLParser.ColumnNameContext ctx) { } 74 | /** 75 | * {@inheritDoc} 76 | * 77 | *

The default implementation does nothing.

78 | */ 79 | @Override public void enterWhereExpr(KSQLParser.WhereExprContext ctx) { } 80 | /** 81 | * {@inheritDoc} 82 | * 83 | *

The default implementation does nothing.

84 | */ 85 | @Override public void exitWhereExpr(KSQLParser.WhereExprContext ctx) { } 86 | /** 87 | * {@inheritDoc} 88 | * 89 | *

The default implementation does nothing.

90 | */ 91 | @Override public void enterDynamicComparison(KSQLParser.DynamicComparisonContext ctx) { } 92 | /** 93 | * {@inheritDoc} 94 | * 95 | *

The default implementation does nothing.

96 | */ 97 | @Override public void exitDynamicComparison(KSQLParser.DynamicComparisonContext ctx) { } 98 | /** 99 | * {@inheritDoc} 100 | * 101 | *

The default implementation does nothing.

102 | */ 103 | @Override public void enterStaticComparison(KSQLParser.StaticComparisonContext ctx) { } 104 | /** 105 | * {@inheritDoc} 106 | * 107 | *

The default implementation does nothing.

108 | */ 109 | @Override public void exitStaticComparison(KSQLParser.StaticComparisonContext ctx) { } 110 | /** 111 | * {@inheritDoc} 112 | * 113 | *

The default implementation does nothing.

114 | */ 115 | @Override public void enterWildcard(KSQLParser.WildcardContext ctx) { } 116 | /** 117 | * {@inheritDoc} 118 | * 119 | *

The default implementation does nothing.

120 | */ 121 | @Override public void exitWildcard(KSQLParser.WildcardContext ctx) { } 122 | /** 123 | * {@inheritDoc} 124 | * 125 | *

The default implementation does nothing.

126 | */ 127 | @Override public void enterTableName(KSQLParser.TableNameContext ctx) { } 128 | /** 129 | * {@inheritDoc} 130 | * 131 | *

The default implementation does nothing.

132 | */ 133 | @Override public void exitTableName(KSQLParser.TableNameContext ctx) { } 134 | 135 | /** 136 | * {@inheritDoc} 137 | * 138 | *

The default implementation does nothing.

139 | */ 140 | @Override public void enterEveryRule(ParserRuleContext ctx) { } 141 | /** 142 | * {@inheritDoc} 143 | * 144 | *

The default implementation does nothing.

145 | */ 146 | @Override public void exitEveryRule(ParserRuleContext ctx) { } 147 | /** 148 | * {@inheritDoc} 149 | * 150 | *

The default implementation does nothing.

151 | */ 152 | @Override public void visitTerminal(TerminalNode node) { } 153 | /** 154 | * {@inheritDoc} 155 | * 156 | *

The default implementation does nothing.

157 | */ 158 | @Override public void visitErrorNode(ErrorNode node) { } 159 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 4.0.0 6 | com.kunalanand 7 | ksql 8 | jar 9 | 1.0.0 10 | ksql 11 | 12 | 13 | 4.5.3 14 | 15 | 16 | 17 | 18 | org.antlr 19 | antlr4-runtime 20 | ${antlr4.version} 21 | compile 22 | 23 | 24 | org.antlr 25 | antlr4-maven-plugin 26 | ${antlr4.version} 27 | compile 28 | 29 | 30 | junit 31 | junit 32 | 4.13.1 33 | test 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.antlr 41 | antlr4-maven-plugin 42 | ${antlr4.version} 43 | 44 | 45 | -visitor 46 | 47 | 48 | 49 | 50 | 51 | antlr4 52 | 53 | 54 | 55 | 56 | 57 | org.codehaus.mojo 58 | exec-maven-plugin 59 | 1.2.1 60 | 61 | mvn 62 | 63 | clean 64 | antlr4:antlr4 65 | install 66 | exec:java 67 | 68 | com.kunalanand.ksql.Main 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-shade-plugin 74 | 1.6 75 | 76 | true 77 | 78 | 79 | *:* 80 | 81 | META-INF/*.SF 82 | META-INF/*.DSA 83 | META-INF/*.RSA 84 | 85 | 86 | 87 | 88 | 89 | 90 | package 91 | 92 | shade 93 | 94 | 95 | 96 | 98 | 100 | com.kunalanand.ksql.Main 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | org.apache.maven.plugins 109 | maven-jar-plugin 110 | 2.3.2 111 | 112 | 113 | 114 | true 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/main/antlr4/com/kunalanand/ksql/KSQLLexer.java: -------------------------------------------------------------------------------- 1 | // Generated from KSQL.g4 by ANTLR 4.5 2 | import org.antlr.v4.runtime.Lexer; 3 | import org.antlr.v4.runtime.CharStream; 4 | import org.antlr.v4.runtime.Token; 5 | import org.antlr.v4.runtime.TokenStream; 6 | import org.antlr.v4.runtime.*; 7 | import org.antlr.v4.runtime.atn.*; 8 | import org.antlr.v4.runtime.dfa.DFA; 9 | import org.antlr.v4.runtime.misc.*; 10 | 11 | @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) 12 | public class KSQLLexer extends Lexer { 13 | static { RuntimeMetaData.checkVersion("4.5", RuntimeMetaData.VERSION); } 14 | 15 | protected static final DFA[] _decisionToDFA; 16 | protected static final PredictionContextCache _sharedContextCache = 17 | new PredictionContextCache(); 18 | public static final int 19 | SELECT=1, DELETE=2, FROM=3, WHERE=4, AND=5, OR=6, STAR=7, EQ=8, ID=9, 20 | INT=10, COMMA=11, SEMICOLON=12, WS=13; 21 | public static String[] modeNames = { 22 | "DEFAULT_MODE" 23 | }; 24 | 25 | public static final String[] ruleNames = { 26 | "SELECT", "DELETE", "FROM", "WHERE", "AND", "OR", "STAR", "EQ", "ID", 27 | "INT", "COMMA", "SEMICOLON", "WS" 28 | }; 29 | 30 | private static final String[] _LITERAL_NAMES = { 31 | null, null, null, null, null, null, null, "'*'", "'='", null, null, "','", 32 | "';'" 33 | }; 34 | private static final String[] _SYMBOLIC_NAMES = { 35 | null, "SELECT", "DELETE", "FROM", "WHERE", "AND", "OR", "STAR", "EQ", 36 | "ID", "INT", "COMMA", "SEMICOLON", "WS" 37 | }; 38 | public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); 39 | 40 | /** 41 | * @deprecated Use {@link #VOCABULARY} instead. 42 | */ 43 | @Deprecated 44 | public static final String[] tokenNames; 45 | static { 46 | tokenNames = new String[_SYMBOLIC_NAMES.length]; 47 | for (int i = 0; i < tokenNames.length; i++) { 48 | tokenNames[i] = VOCABULARY.getLiteralName(i); 49 | if (tokenNames[i] == null) { 50 | tokenNames[i] = VOCABULARY.getSymbolicName(i); 51 | } 52 | 53 | if (tokenNames[i] == null) { 54 | tokenNames[i] = ""; 55 | } 56 | } 57 | } 58 | 59 | @Override 60 | @Deprecated 61 | public String[] getTokenNames() { 62 | return tokenNames; 63 | } 64 | 65 | @Override 66 | 67 | public Vocabulary getVocabulary() { 68 | return VOCABULARY; 69 | } 70 | 71 | 72 | public KSQLLexer(CharStream input) { 73 | super(input); 74 | _interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); 75 | } 76 | 77 | @Override 78 | public String getGrammarFileName() { return "KSQL.g4"; } 79 | 80 | @Override 81 | public String[] getRuleNames() { return ruleNames; } 82 | 83 | @Override 84 | public String getSerializedATN() { return _serializedATN; } 85 | 86 | @Override 87 | public String[] getModeNames() { return modeNames; } 88 | 89 | @Override 90 | public ATN getATN() { return _ATN; } 91 | 92 | public static final String _serializedATN = 93 | "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2\17z\b\1\4\2\t\2\4"+ 94 | "\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t"+ 95 | "\13\4\f\t\f\4\r\t\r\4\16\t\16\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2\3\2"+ 96 | "\3\2\3\2\5\2*\n\2\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\5\3"+ 97 | "8\n\3\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\5\4B\n\4\3\5\3\5\3\5\3\5\3\5\3\5"+ 98 | "\3\5\3\5\3\5\3\5\5\5N\n\5\3\6\3\6\3\6\3\6\3\6\3\6\5\6V\n\6\3\7\3\7\3\7"+ 99 | "\3\7\5\7\\\n\7\3\b\3\b\3\t\3\t\3\n\6\nc\n\n\r\n\16\nd\3\13\6\13h\n\13"+ 100 | "\r\13\16\13i\3\f\3\f\3\f\3\f\3\r\3\r\3\r\3\r\3\16\6\16u\n\16\r\16\16\16"+ 101 | "v\3\16\3\16\2\2\17\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r"+ 102 | "\31\16\33\17\3\2\5\4\2C\\c|\3\2\62;\5\2\13\f\17\17\"\"\u0082\2\3\3\2\2"+ 103 | "\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3"+ 104 | "\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2"+ 105 | "\2\2\33\3\2\2\2\3)\3\2\2\2\5\67\3\2\2\2\7A\3\2\2\2\tM\3\2\2\2\13U\3\2"+ 106 | "\2\2\r[\3\2\2\2\17]\3\2\2\2\21_\3\2\2\2\23b\3\2\2\2\25g\3\2\2\2\27k\3"+ 107 | "\2\2\2\31o\3\2\2\2\33t\3\2\2\2\35\36\7U\2\2\36\37\7G\2\2\37 \7N\2\2 !"+ 108 | "\7G\2\2!\"\7E\2\2\"*\7V\2\2#$\7u\2\2$%\7g\2\2%&\7n\2\2&\'\7g\2\2\'(\7"+ 109 | "e\2\2(*\7v\2\2)\35\3\2\2\2)#\3\2\2\2*\4\3\2\2\2+,\7F\2\2,-\7G\2\2-.\7"+ 110 | "N\2\2./\7G\2\2/\60\7V\2\2\608\7G\2\2\61\62\7f\2\2\62\63\7g\2\2\63\64\7"+ 111 | "n\2\2\64\65\7g\2\2\65\66\7v\2\2\668\7g\2\2\67+\3\2\2\2\67\61\3\2\2\28"+ 112 | "\6\3\2\2\29:\7H\2\2:;\7T\2\2;<\7Q\2\2\7h\2\2>?\7t\2\2?@\7q"+ 113 | "\2\2@B\7o\2\2A9\3\2\2\2A=\3\2\2\2B\b\3\2\2\2CD\7Y\2\2DE\7J\2\2EF\7G\2"+ 114 | "\2FG\7T\2\2GN\7G\2\2HI\7y\2\2IJ\7j\2\2JK\7g\2\2KL\7t\2\2LN\7g\2\2MC\3"+ 115 | "\2\2\2MH\3\2\2\2N\n\3\2\2\2OP\7C\2\2PQ\7P\2\2QV\7F\2\2RS\7c\2\2ST\7p\2"+ 116 | "\2TV\7f\2\2UO\3\2\2\2UR\3\2\2\2V\f\3\2\2\2WX\7Q\2\2X\\\7T\2\2YZ\7q\2\2"+ 117 | "Z\\\7t\2\2[W\3\2\2\2[Y\3\2\2\2\\\16\3\2\2\2]^\7,\2\2^\20\3\2\2\2_`\7?"+ 118 | "\2\2`\22\3\2\2\2ac\t\2\2\2ba\3\2\2\2cd\3\2\2\2db\3\2\2\2de\3\2\2\2e\24"+ 119 | "\3\2\2\2fh\t\3\2\2gf\3\2\2\2hi\3\2\2\2ig\3\2\2\2ij\3\2\2\2j\26\3\2\2\2"+ 120 | "kl\7.\2\2lm\3\2\2\2mn\b\f\2\2n\30\3\2\2\2op\7=\2\2pq\3\2\2\2qr\b\r\2\2"+ 121 | "r\32\3\2\2\2su\t\4\2\2ts\3\2\2\2uv\3\2\2\2vt\3\2\2\2vw\3\2\2\2wx\3\2\2"+ 122 | "\2xy\b\16\2\2y\34\3\2\2\2\f\2)\67AMU[div\3\b\2\2"; 123 | public static final ATN _ATN = 124 | new ATNDeserializer().deserialize(_serializedATN.toCharArray()); 125 | static { 126 | _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; 127 | for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { 128 | _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /src/main/java/com/kunalanand/ksql/Intelligence.java: -------------------------------------------------------------------------------- 1 | package com.kunalanand.ksql; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | import org.antlr.v4.runtime.ANTLRInputStream; 7 | import org.antlr.v4.runtime.CommonTokenStream; 8 | import org.antlr.v4.runtime.tree.TerminalNode; 9 | 10 | public class Intelligence { 11 | private StatementType statementType; 12 | private String table; 13 | private Set columns; 14 | private String dynamicColumn; 15 | private TerminalNode dynamicValue; 16 | private TerminalNode staticValue1; 17 | private TerminalNode staticValue2; 18 | 19 | public Intelligence() { 20 | } 21 | 22 | public static Intelligence build( String sql ) { 23 | // Create a lexer and parser for the input 24 | KSQLLexer lexer = new KSQLLexer( new ANTLRInputStream( sql ) ); 25 | KSQLParser parser = new KSQLParser( new CommonTokenStream( lexer ) ); 26 | 27 | // Start at the highest level: expression (expr) 28 | KSQLParser.ExprContext context = parser.expr(); 29 | 30 | Intelligence intelligence = new Intelligence(); 31 | 32 | if( context.selectExpr() != null ) { 33 | intelligence.setStatementType( StatementType.SELECT ); 34 | intelligence.setTable( context.selectExpr().tableName().getText() ); 35 | // Build up columns 36 | HashSet columns = new HashSet(); 37 | if( context.selectExpr().columnList().wildcard() != null ) { 38 | columns.add( "*" ); 39 | } else { 40 | for( TerminalNode columnName : context.selectExpr().columnList().columnName( 0 ).ID() ) { 41 | columns.add( columnName.getText() ); 42 | } 43 | } 44 | intelligence.setColumns( columns ); 45 | // Where 46 | if( context.selectExpr().whereExpr() != null ) { 47 | // Static 48 | if( context.selectExpr().whereExpr().staticComparison() != null ) { 49 | intelligence.setStaticValue1( context.selectExpr().whereExpr().staticComparison().INT( 0 ) ); 50 | intelligence.setStaticValue2( context.selectExpr().whereExpr().staticComparison().INT( 1 ) ); 51 | } 52 | // Dynamic 53 | if( context.selectExpr().whereExpr().dynamicComparison() != null ) { 54 | intelligence 55 | .setDynamicColumn( context.selectExpr().whereExpr().dynamicComparison().ID().getText() ); 56 | intelligence.setDynamicValue( context.selectExpr().whereExpr().dynamicComparison().INT() ); 57 | } 58 | } 59 | } else if( context.deleteExpr() != null ) { 60 | intelligence.setStatementType( StatementType.DELETE ); 61 | intelligence.setTable( context.deleteExpr().tableName().getText() ); 62 | } 63 | 64 | return intelligence; 65 | } 66 | 67 | public StatementType getStatementType() { 68 | return statementType; 69 | } 70 | 71 | public void setStatementType( StatementType statementType ) { 72 | this.statementType = statementType; 73 | } 74 | 75 | public String getTable() { 76 | return table; 77 | } 78 | 79 | public void setTable( String table ) { 80 | this.table = table; 81 | } 82 | 83 | public Set getColumns() { 84 | return columns; 85 | } 86 | 87 | public void setColumns( Set columns ) { 88 | this.columns = columns; 89 | } 90 | 91 | public String getDynamicColumn() { 92 | return dynamicColumn; 93 | } 94 | 95 | public void setDynamicColumn( String dynamicColumn ) { 96 | this.dynamicColumn = dynamicColumn; 97 | } 98 | 99 | public TerminalNode getDynamicValue() { 100 | return dynamicValue; 101 | } 102 | 103 | public void setDynamicValue( TerminalNode dynamicValue ) { 104 | this.dynamicValue = dynamicValue; 105 | } 106 | 107 | public TerminalNode getStaticValue1() { 108 | return staticValue1; 109 | } 110 | 111 | public void setStaticValue1( TerminalNode staticValue1 ) { 112 | this.staticValue1 = staticValue1; 113 | } 114 | 115 | public TerminalNode getStaticValue2() { 116 | return staticValue2; 117 | } 118 | 119 | public void setStaticValue2( TerminalNode staticValue2 ) { 120 | this.staticValue2 = staticValue2; 121 | } 122 | 123 | public boolean secure( Set badStatements, Set badTables, Set badColumns, 124 | boolean blockStaticTautologies ) { 125 | // Statement type 126 | if( badStatements.contains( statementType ) ) { 127 | return false; 128 | } 129 | // Table 130 | if( badTables.contains( table ) ) { 131 | return false; 132 | } 133 | // Columns 134 | for( String column : columns ) { 135 | if( badColumns.contains( column ) ) { 136 | return false; 137 | } 138 | } 139 | // Static Tautologies 140 | if( blockStaticTautologies && staticValue1.getText().equals( staticValue2.getText() ) ) { 141 | return false; 142 | } 143 | return true; 144 | } 145 | 146 | @Override 147 | public String toString() { 148 | return "Intelligence{" + 149 | "statementType=" + statementType + 150 | ", table='" + table + '\'' + 151 | ", columns=" + columns + 152 | ", dynamicColumn='" + dynamicColumn + '\'' + 153 | ", dynamicValue=" + dynamicValue + 154 | ", staticValue1=" + staticValue1 + 155 | ", staticValue2=" + staticValue2 + 156 | '}'; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/antlr4/com/kunalanand/ksql/KSQLParser.java: -------------------------------------------------------------------------------- 1 | // Generated from KSQL.g4 by ANTLR 4.5 2 | import org.antlr.v4.runtime.atn.*; 3 | import org.antlr.v4.runtime.dfa.DFA; 4 | import org.antlr.v4.runtime.*; 5 | import org.antlr.v4.runtime.misc.*; 6 | import org.antlr.v4.runtime.tree.*; 7 | import java.util.List; 8 | import java.util.Iterator; 9 | import java.util.ArrayList; 10 | 11 | @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) 12 | public class KSQLParser extends Parser { 13 | static { RuntimeMetaData.checkVersion("4.5", RuntimeMetaData.VERSION); } 14 | 15 | protected static final DFA[] _decisionToDFA; 16 | protected static final PredictionContextCache _sharedContextCache = 17 | new PredictionContextCache(); 18 | public static final int 19 | SELECT=1, DELETE=2, FROM=3, WHERE=4, AND=5, OR=6, STAR=7, EQ=8, ID=9, 20 | INT=10, COMMA=11, SEMICOLON=12, WS=13; 21 | public static final int 22 | RULE_expr = 0, RULE_deleteExpr = 1, RULE_selectExpr = 2, RULE_columnList = 3, 23 | RULE_columnName = 4, RULE_whereExpr = 5, RULE_dynamicComparison = 6, RULE_staticComparison = 7, 24 | RULE_wildcard = 8, RULE_tableName = 9; 25 | public static final String[] ruleNames = { 26 | "expr", "deleteExpr", "selectExpr", "columnList", "columnName", "whereExpr", 27 | "dynamicComparison", "staticComparison", "wildcard", "tableName" 28 | }; 29 | 30 | private static final String[] _LITERAL_NAMES = { 31 | null, null, null, null, null, null, null, "'*'", "'='", null, null, "','", 32 | "';'" 33 | }; 34 | private static final String[] _SYMBOLIC_NAMES = { 35 | null, "SELECT", "DELETE", "FROM", "WHERE", "AND", "OR", "STAR", "EQ", 36 | "ID", "INT", "COMMA", "SEMICOLON", "WS" 37 | }; 38 | public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); 39 | 40 | /** 41 | * @deprecated Use {@link #VOCABULARY} instead. 42 | */ 43 | @Deprecated 44 | public static final String[] tokenNames; 45 | static { 46 | tokenNames = new String[_SYMBOLIC_NAMES.length]; 47 | for (int i = 0; i < tokenNames.length; i++) { 48 | tokenNames[i] = VOCABULARY.getLiteralName(i); 49 | if (tokenNames[i] == null) { 50 | tokenNames[i] = VOCABULARY.getSymbolicName(i); 51 | } 52 | 53 | if (tokenNames[i] == null) { 54 | tokenNames[i] = ""; 55 | } 56 | } 57 | } 58 | 59 | @Override 60 | @Deprecated 61 | public String[] getTokenNames() { 62 | return tokenNames; 63 | } 64 | 65 | @Override 66 | 67 | public Vocabulary getVocabulary() { 68 | return VOCABULARY; 69 | } 70 | 71 | @Override 72 | public String getGrammarFileName() { return "KSQL.g4"; } 73 | 74 | @Override 75 | public String[] getRuleNames() { return ruleNames; } 76 | 77 | @Override 78 | public String getSerializedATN() { return _serializedATN; } 79 | 80 | @Override 81 | public ATN getATN() { return _ATN; } 82 | 83 | public KSQLParser(TokenStream input) { 84 | super(input); 85 | _interp = new ParserATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache); 86 | } 87 | public static class ExprContext extends ParserRuleContext { 88 | public SelectExprContext selectExpr() { 89 | return getRuleContext(SelectExprContext.class,0); 90 | } 91 | public DeleteExprContext deleteExpr() { 92 | return getRuleContext(DeleteExprContext.class,0); 93 | } 94 | public ExprContext(ParserRuleContext parent, int invokingState) { 95 | super(parent, invokingState); 96 | } 97 | @Override public int getRuleIndex() { return RULE_expr; } 98 | @Override 99 | public void enterRule(ParseTreeListener listener) { 100 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).enterExpr(this); 101 | } 102 | @Override 103 | public void exitRule(ParseTreeListener listener) { 104 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).exitExpr(this); 105 | } 106 | } 107 | 108 | public final ExprContext expr() throws RecognitionException { 109 | ExprContext _localctx = new ExprContext(_ctx, getState()); 110 | enterRule(_localctx, 0, RULE_expr); 111 | try { 112 | setState(22); 113 | switch (_input.LA(1)) { 114 | case SELECT: 115 | enterOuterAlt(_localctx, 1); 116 | { 117 | setState(20); 118 | selectExpr(); 119 | } 120 | break; 121 | case DELETE: 122 | enterOuterAlt(_localctx, 2); 123 | { 124 | setState(21); 125 | deleteExpr(); 126 | } 127 | break; 128 | default: 129 | throw new NoViableAltException(this); 130 | } 131 | } 132 | catch (RecognitionException re) { 133 | _localctx.exception = re; 134 | _errHandler.reportError(this, re); 135 | _errHandler.recover(this, re); 136 | } 137 | finally { 138 | exitRule(); 139 | } 140 | return _localctx; 141 | } 142 | 143 | public static class DeleteExprContext extends ParserRuleContext { 144 | public TerminalNode DELETE() { return getToken(KSQLParser.DELETE, 0); } 145 | public TerminalNode FROM() { return getToken(KSQLParser.FROM, 0); } 146 | public TableNameContext tableName() { 147 | return getRuleContext(TableNameContext.class,0); 148 | } 149 | public DeleteExprContext(ParserRuleContext parent, int invokingState) { 150 | super(parent, invokingState); 151 | } 152 | @Override public int getRuleIndex() { return RULE_deleteExpr; } 153 | @Override 154 | public void enterRule(ParseTreeListener listener) { 155 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).enterDeleteExpr(this); 156 | } 157 | @Override 158 | public void exitRule(ParseTreeListener listener) { 159 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).exitDeleteExpr(this); 160 | } 161 | } 162 | 163 | public final DeleteExprContext deleteExpr() throws RecognitionException { 164 | DeleteExprContext _localctx = new DeleteExprContext(_ctx, getState()); 165 | enterRule(_localctx, 2, RULE_deleteExpr); 166 | try { 167 | enterOuterAlt(_localctx, 1); 168 | { 169 | setState(24); 170 | match(DELETE); 171 | setState(25); 172 | match(FROM); 173 | setState(26); 174 | tableName(); 175 | } 176 | } 177 | catch (RecognitionException re) { 178 | _localctx.exception = re; 179 | _errHandler.reportError(this, re); 180 | _errHandler.recover(this, re); 181 | } 182 | finally { 183 | exitRule(); 184 | } 185 | return _localctx; 186 | } 187 | 188 | public static class SelectExprContext extends ParserRuleContext { 189 | public TerminalNode SELECT() { return getToken(KSQLParser.SELECT, 0); } 190 | public ColumnListContext columnList() { 191 | return getRuleContext(ColumnListContext.class,0); 192 | } 193 | public TerminalNode FROM() { return getToken(KSQLParser.FROM, 0); } 194 | public TableNameContext tableName() { 195 | return getRuleContext(TableNameContext.class,0); 196 | } 197 | public TerminalNode WHERE() { return getToken(KSQLParser.WHERE, 0); } 198 | public WhereExprContext whereExpr() { 199 | return getRuleContext(WhereExprContext.class,0); 200 | } 201 | public TerminalNode SEMICOLON() { return getToken(KSQLParser.SEMICOLON, 0); } 202 | public SelectExprContext(ParserRuleContext parent, int invokingState) { 203 | super(parent, invokingState); 204 | } 205 | @Override public int getRuleIndex() { return RULE_selectExpr; } 206 | @Override 207 | public void enterRule(ParseTreeListener listener) { 208 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).enterSelectExpr(this); 209 | } 210 | @Override 211 | public void exitRule(ParseTreeListener listener) { 212 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).exitSelectExpr(this); 213 | } 214 | } 215 | 216 | public final SelectExprContext selectExpr() throws RecognitionException { 217 | SelectExprContext _localctx = new SelectExprContext(_ctx, getState()); 218 | enterRule(_localctx, 4, RULE_selectExpr); 219 | int _la; 220 | try { 221 | enterOuterAlt(_localctx, 1); 222 | { 223 | setState(28); 224 | match(SELECT); 225 | setState(29); 226 | columnList(); 227 | setState(30); 228 | match(FROM); 229 | setState(31); 230 | tableName(); 231 | setState(34); 232 | _la = _input.LA(1); 233 | if (_la==WHERE) { 234 | { 235 | setState(32); 236 | match(WHERE); 237 | setState(33); 238 | whereExpr(); 239 | } 240 | } 241 | 242 | setState(37); 243 | _la = _input.LA(1); 244 | if (_la==SEMICOLON) { 245 | { 246 | setState(36); 247 | match(SEMICOLON); 248 | } 249 | } 250 | 251 | } 252 | } 253 | catch (RecognitionException re) { 254 | _localctx.exception = re; 255 | _errHandler.reportError(this, re); 256 | _errHandler.recover(this, re); 257 | } 258 | finally { 259 | exitRule(); 260 | } 261 | return _localctx; 262 | } 263 | 264 | public static class ColumnListContext extends ParserRuleContext { 265 | public WildcardContext wildcard() { 266 | return getRuleContext(WildcardContext.class,0); 267 | } 268 | public List columnName() { 269 | return getRuleContexts(ColumnNameContext.class); 270 | } 271 | public ColumnNameContext columnName(int i) { 272 | return getRuleContext(ColumnNameContext.class,i); 273 | } 274 | public List COMMA() { return getTokens(KSQLParser.COMMA); } 275 | public TerminalNode COMMA(int i) { 276 | return getToken(KSQLParser.COMMA, i); 277 | } 278 | public ColumnListContext(ParserRuleContext parent, int invokingState) { 279 | super(parent, invokingState); 280 | } 281 | @Override public int getRuleIndex() { return RULE_columnList; } 282 | @Override 283 | public void enterRule(ParseTreeListener listener) { 284 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).enterColumnList(this); 285 | } 286 | @Override 287 | public void exitRule(ParseTreeListener listener) { 288 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).exitColumnList(this); 289 | } 290 | } 291 | 292 | public final ColumnListContext columnList() throws RecognitionException { 293 | ColumnListContext _localctx = new ColumnListContext(_ctx, getState()); 294 | enterRule(_localctx, 6, RULE_columnList); 295 | int _la; 296 | try { 297 | setState(48); 298 | switch (_input.LA(1)) { 299 | case STAR: 300 | enterOuterAlt(_localctx, 1); 301 | { 302 | setState(39); 303 | wildcard(); 304 | } 305 | break; 306 | case ID: 307 | enterOuterAlt(_localctx, 2); 308 | { 309 | setState(40); 310 | columnName(); 311 | setState(45); 312 | _errHandler.sync(this); 313 | _la = _input.LA(1); 314 | while (_la==COMMA) { 315 | { 316 | { 317 | setState(41); 318 | match(COMMA); 319 | setState(42); 320 | columnName(); 321 | } 322 | } 323 | setState(47); 324 | _errHandler.sync(this); 325 | _la = _input.LA(1); 326 | } 327 | } 328 | break; 329 | default: 330 | throw new NoViableAltException(this); 331 | } 332 | } 333 | catch (RecognitionException re) { 334 | _localctx.exception = re; 335 | _errHandler.reportError(this, re); 336 | _errHandler.recover(this, re); 337 | } 338 | finally { 339 | exitRule(); 340 | } 341 | return _localctx; 342 | } 343 | 344 | public static class ColumnNameContext extends ParserRuleContext { 345 | public List ID() { return getTokens(KSQLParser.ID); } 346 | public TerminalNode ID(int i) { 347 | return getToken(KSQLParser.ID, i); 348 | } 349 | public ColumnNameContext(ParserRuleContext parent, int invokingState) { 350 | super(parent, invokingState); 351 | } 352 | @Override public int getRuleIndex() { return RULE_columnName; } 353 | @Override 354 | public void enterRule(ParseTreeListener listener) { 355 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).enterColumnName(this); 356 | } 357 | @Override 358 | public void exitRule(ParseTreeListener listener) { 359 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).exitColumnName(this); 360 | } 361 | } 362 | 363 | public final ColumnNameContext columnName() throws RecognitionException { 364 | ColumnNameContext _localctx = new ColumnNameContext(_ctx, getState()); 365 | enterRule(_localctx, 8, RULE_columnName); 366 | int _la; 367 | try { 368 | enterOuterAlt(_localctx, 1); 369 | { 370 | setState(50); 371 | match(ID); 372 | setState(52); 373 | _la = _input.LA(1); 374 | if (_la==ID) { 375 | { 376 | setState(51); 377 | match(ID); 378 | } 379 | } 380 | 381 | } 382 | } 383 | catch (RecognitionException re) { 384 | _localctx.exception = re; 385 | _errHandler.reportError(this, re); 386 | _errHandler.recover(this, re); 387 | } 388 | finally { 389 | exitRule(); 390 | } 391 | return _localctx; 392 | } 393 | 394 | public static class WhereExprContext extends ParserRuleContext { 395 | public DynamicComparisonContext dynamicComparison() { 396 | return getRuleContext(DynamicComparisonContext.class,0); 397 | } 398 | public StaticComparisonContext staticComparison() { 399 | return getRuleContext(StaticComparisonContext.class,0); 400 | } 401 | public WhereExprContext(ParserRuleContext parent, int invokingState) { 402 | super(parent, invokingState); 403 | } 404 | @Override public int getRuleIndex() { return RULE_whereExpr; } 405 | @Override 406 | public void enterRule(ParseTreeListener listener) { 407 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).enterWhereExpr(this); 408 | } 409 | @Override 410 | public void exitRule(ParseTreeListener listener) { 411 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).exitWhereExpr(this); 412 | } 413 | } 414 | 415 | public final WhereExprContext whereExpr() throws RecognitionException { 416 | WhereExprContext _localctx = new WhereExprContext(_ctx, getState()); 417 | enterRule(_localctx, 10, RULE_whereExpr); 418 | try { 419 | setState(56); 420 | switch (_input.LA(1)) { 421 | case ID: 422 | enterOuterAlt(_localctx, 1); 423 | { 424 | setState(54); 425 | dynamicComparison(); 426 | } 427 | break; 428 | case INT: 429 | enterOuterAlt(_localctx, 2); 430 | { 431 | setState(55); 432 | staticComparison(); 433 | } 434 | break; 435 | default: 436 | throw new NoViableAltException(this); 437 | } 438 | } 439 | catch (RecognitionException re) { 440 | _localctx.exception = re; 441 | _errHandler.reportError(this, re); 442 | _errHandler.recover(this, re); 443 | } 444 | finally { 445 | exitRule(); 446 | } 447 | return _localctx; 448 | } 449 | 450 | public static class DynamicComparisonContext extends ParserRuleContext { 451 | public TerminalNode ID() { return getToken(KSQLParser.ID, 0); } 452 | public TerminalNode EQ() { return getToken(KSQLParser.EQ, 0); } 453 | public TerminalNode INT() { return getToken(KSQLParser.INT, 0); } 454 | public DynamicComparisonContext(ParserRuleContext parent, int invokingState) { 455 | super(parent, invokingState); 456 | } 457 | @Override public int getRuleIndex() { return RULE_dynamicComparison; } 458 | @Override 459 | public void enterRule(ParseTreeListener listener) { 460 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).enterDynamicComparison(this); 461 | } 462 | @Override 463 | public void exitRule(ParseTreeListener listener) { 464 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).exitDynamicComparison(this); 465 | } 466 | } 467 | 468 | public final DynamicComparisonContext dynamicComparison() throws RecognitionException { 469 | DynamicComparisonContext _localctx = new DynamicComparisonContext(_ctx, getState()); 470 | enterRule(_localctx, 12, RULE_dynamicComparison); 471 | try { 472 | enterOuterAlt(_localctx, 1); 473 | { 474 | setState(58); 475 | match(ID); 476 | setState(59); 477 | match(EQ); 478 | setState(60); 479 | match(INT); 480 | } 481 | } 482 | catch (RecognitionException re) { 483 | _localctx.exception = re; 484 | _errHandler.reportError(this, re); 485 | _errHandler.recover(this, re); 486 | } 487 | finally { 488 | exitRule(); 489 | } 490 | return _localctx; 491 | } 492 | 493 | public static class StaticComparisonContext extends ParserRuleContext { 494 | public List INT() { return getTokens(KSQLParser.INT); } 495 | public TerminalNode INT(int i) { 496 | return getToken(KSQLParser.INT, i); 497 | } 498 | public TerminalNode EQ() { return getToken(KSQLParser.EQ, 0); } 499 | public StaticComparisonContext(ParserRuleContext parent, int invokingState) { 500 | super(parent, invokingState); 501 | } 502 | @Override public int getRuleIndex() { return RULE_staticComparison; } 503 | @Override 504 | public void enterRule(ParseTreeListener listener) { 505 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).enterStaticComparison(this); 506 | } 507 | @Override 508 | public void exitRule(ParseTreeListener listener) { 509 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).exitStaticComparison(this); 510 | } 511 | } 512 | 513 | public final StaticComparisonContext staticComparison() throws RecognitionException { 514 | StaticComparisonContext _localctx = new StaticComparisonContext(_ctx, getState()); 515 | enterRule(_localctx, 14, RULE_staticComparison); 516 | try { 517 | enterOuterAlt(_localctx, 1); 518 | { 519 | setState(62); 520 | match(INT); 521 | setState(63); 522 | match(EQ); 523 | setState(64); 524 | match(INT); 525 | } 526 | } 527 | catch (RecognitionException re) { 528 | _localctx.exception = re; 529 | _errHandler.reportError(this, re); 530 | _errHandler.recover(this, re); 531 | } 532 | finally { 533 | exitRule(); 534 | } 535 | return _localctx; 536 | } 537 | 538 | public static class WildcardContext extends ParserRuleContext { 539 | public TerminalNode STAR() { return getToken(KSQLParser.STAR, 0); } 540 | public WildcardContext(ParserRuleContext parent, int invokingState) { 541 | super(parent, invokingState); 542 | } 543 | @Override public int getRuleIndex() { return RULE_wildcard; } 544 | @Override 545 | public void enterRule(ParseTreeListener listener) { 546 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).enterWildcard(this); 547 | } 548 | @Override 549 | public void exitRule(ParseTreeListener listener) { 550 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).exitWildcard(this); 551 | } 552 | } 553 | 554 | public final WildcardContext wildcard() throws RecognitionException { 555 | WildcardContext _localctx = new WildcardContext(_ctx, getState()); 556 | enterRule(_localctx, 16, RULE_wildcard); 557 | try { 558 | enterOuterAlt(_localctx, 1); 559 | { 560 | setState(66); 561 | match(STAR); 562 | } 563 | } 564 | catch (RecognitionException re) { 565 | _localctx.exception = re; 566 | _errHandler.reportError(this, re); 567 | _errHandler.recover(this, re); 568 | } 569 | finally { 570 | exitRule(); 571 | } 572 | return _localctx; 573 | } 574 | 575 | public static class TableNameContext extends ParserRuleContext { 576 | public TerminalNode ID() { return getToken(KSQLParser.ID, 0); } 577 | public TableNameContext(ParserRuleContext parent, int invokingState) { 578 | super(parent, invokingState); 579 | } 580 | @Override public int getRuleIndex() { return RULE_tableName; } 581 | @Override 582 | public void enterRule(ParseTreeListener listener) { 583 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).enterTableName(this); 584 | } 585 | @Override 586 | public void exitRule(ParseTreeListener listener) { 587 | if ( listener instanceof KSQLListener ) ((KSQLListener)listener).exitTableName(this); 588 | } 589 | } 590 | 591 | public final TableNameContext tableName() throws RecognitionException { 592 | TableNameContext _localctx = new TableNameContext(_ctx, getState()); 593 | enterRule(_localctx, 18, RULE_tableName); 594 | try { 595 | enterOuterAlt(_localctx, 1); 596 | { 597 | setState(68); 598 | match(ID); 599 | } 600 | } 601 | catch (RecognitionException re) { 602 | _localctx.exception = re; 603 | _errHandler.reportError(this, re); 604 | _errHandler.recover(this, re); 605 | } 606 | finally { 607 | exitRule(); 608 | } 609 | return _localctx; 610 | } 611 | 612 | public static final String _serializedATN = 613 | "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3\17I\4\2\t\2\4\3\t"+ 614 | "\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\3"+ 615 | "\2\3\2\5\2\31\n\2\3\3\3\3\3\3\3\3\3\4\3\4\3\4\3\4\3\4\3\4\5\4%\n\4\3\4"+ 616 | "\5\4(\n\4\3\5\3\5\3\5\3\5\7\5.\n\5\f\5\16\5\61\13\5\5\5\63\n\5\3\6\3\6"+ 617 | "\5\6\67\n\6\3\7\3\7\5\7;\n\7\3\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t\3\n\3\n\3"+ 618 | "\13\3\13\3\13\2\2\f\2\4\6\b\n\f\16\20\22\24\2\2E\2\30\3\2\2\2\4\32\3\2"+ 619 | "\2\2\6\36\3\2\2\2\b\62\3\2\2\2\n\64\3\2\2\2\f:\3\2\2\2\16<\3\2\2\2\20"+ 620 | "@\3\2\2\2\22D\3\2\2\2\24F\3\2\2\2\26\31\5\6\4\2\27\31\5\4\3\2\30\26\3"+ 621 | "\2\2\2\30\27\3\2\2\2\31\3\3\2\2\2\32\33\7\4\2\2\33\34\7\5\2\2\34\35\5"+ 622 | "\24\13\2\35\5\3\2\2\2\36\37\7\3\2\2\37 \5\b\5\2 !\7\5\2\2!$\5\24\13\2"+ 623 | "\"#\7\6\2\2#%\5\f\7\2$\"\3\2\2\2$%\3\2\2\2%\'\3\2\2\2&(\7\16\2\2\'&\3"+ 624 | "\2\2\2\'(\3\2\2\2(\7\3\2\2\2)\63\5\22\n\2*/\5\n\6\2+,\7\r\2\2,.\5\n\6"+ 625 | "\2-+\3\2\2\2.\61\3\2\2\2/-\3\2\2\2/\60\3\2\2\2\60\63\3\2\2\2\61/\3\2\2"+ 626 | "\2\62)\3\2\2\2\62*\3\2\2\2\63\t\3\2\2\2\64\66\7\13\2\2\65\67\7\13\2\2"+ 627 | "\66\65\3\2\2\2\66\67\3\2\2\2\67\13\3\2\2\28;\5\16\b\29;\5\20\t\2:8\3\2"+ 628 | "\2\2:9\3\2\2\2;\r\3\2\2\2<=\7\13\2\2=>\7\n\2\2>?\7\f\2\2?\17\3\2\2\2@"+ 629 | "A\7\f\2\2AB\7\n\2\2BC\7\f\2\2C\21\3\2\2\2DE\7\t\2\2E\23\3\2\2\2FG\7\13"+ 630 | "\2\2G\25\3\2\2\2\t\30$\'/\62\66:"; 631 | public static final ATN _ATN = 632 | new ATNDeserializer().deserialize(_serializedATN.toCharArray()); 633 | static { 634 | _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; 635 | for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) { 636 | _decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i); 637 | } 638 | } 639 | } --------------------------------------------------------------------------------