mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-17 18:35:25 +00:00
SQL: Support aliases and indices pattern (elastic/x-pack-elasticsearch#3438)
Add support for aliases and indices pattern Enhance ShowTable info to differentiate between aliases and indices Add regex filtering of index names Handle security exceptions (in case of no privileges or no matching) Original commit: elastic/x-pack-elasticsearch@91e3674ca7
This commit is contained in:
parent
b769007798
commit
ba81321d0d
@ -345,7 +345,7 @@ public abstract class SqlSecurityTestCase extends ESRestTestCase {
|
||||
public void testShowTablesWorksAsFullAccess() throws Exception {
|
||||
createUser("full_access", "read_all");
|
||||
|
||||
actions.expectMatchesAdmin("SHOW TABLES LIKE '*t'", "full_access", "SHOW TABLES");
|
||||
actions.expectMatchesAdmin("SHOW TABLES LIKE '%t'", "full_access", "SHOW TABLES");
|
||||
new AuditLogAsserter()
|
||||
.expectSqlCompositeAction("test_admin", "bort", "test")
|
||||
.expectSqlCompositeAction("full_access", "bort", "test")
|
||||
@ -374,7 +374,7 @@ public abstract class SqlSecurityTestCase extends ESRestTestCase {
|
||||
public void testShowTablesWithLimitedAccessUnaccessableIndex() throws Exception {
|
||||
createUser("read_bort", "read_bort");
|
||||
|
||||
actions.expectMatchesAdmin("SHOW TABLES LIKE 'not_created'", "read_bort", "SHOW TABLES LIKE 'test'");
|
||||
actions.expectMatchesAdmin("SHOW TABLES LIKE 'not-created'", "read_bort", "SHOW TABLES LIKE 'test'");
|
||||
new AuditLogAsserter()
|
||||
.expect(true, SQL_ACTION_NAME, "test_admin", empty())
|
||||
.expect(true, GetIndexAction.NAME, "test_admin", contains("*", "-*"))
|
||||
|
@ -16,7 +16,7 @@ public abstract class ShowTestCase extends CliIntegrationTestCase {
|
||||
public void testShowTables() throws IOException {
|
||||
index("test1", body -> body.field("test_field", "test_value"));
|
||||
index("test2", body -> body.field("test_field", "test_value"));
|
||||
assertThat(command("SHOW TABLES"), RegexMatcher.matches("\\s*table\\s*"));
|
||||
assertThat(command("SHOW TABLES"), RegexMatcher.matches("\\s*name\\s*"));
|
||||
assertThat(readLine(), containsString("----------"));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*test[12]\\s*"));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*test[12]\\s*"));
|
||||
|
@ -46,7 +46,8 @@ public abstract class CsvSpecTestCase extends SpecBaseIntegrationTestCase {
|
||||
readScriptSpec("/fulltext.csv-spec", parser),
|
||||
readScriptSpec("/agg.csv-spec", parser),
|
||||
readScriptSpec("/columns.csv-spec", parser),
|
||||
readScriptSpec("/datetime.csv-spec", parser)
|
||||
readScriptSpec("/datetime.csv-spec", parser),
|
||||
readScriptSpec("/alias.csv-spec", parser)
|
||||
);
|
||||
}
|
||||
|
||||
@ -148,6 +149,7 @@ public abstract class CsvSpecTestCase extends SpecBaseIntegrationTestCase {
|
||||
case "l": return "long";
|
||||
case "f": return "float";
|
||||
case "d": return "double";
|
||||
case "ts": return "timestamp";
|
||||
default: return type;
|
||||
}
|
||||
}
|
||||
@ -164,7 +166,7 @@ public abstract class CsvSpecTestCase extends SpecBaseIntegrationTestCase {
|
||||
public Object parse(String line) {
|
||||
// beginning of the section
|
||||
if (testCase == null) {
|
||||
// pick up the query
|
||||
// pick up the query
|
||||
testCase = new CsvTestCase();
|
||||
testCase.query = line.endsWith(";") ? line.substring(0, line.length() - 1) : line;
|
||||
}
|
||||
|
@ -37,6 +37,13 @@ public class DataLoader {
|
||||
}
|
||||
|
||||
protected static void loadDatasetIntoEs(RestClient client) throws Exception {
|
||||
loadDatasetIntoEs(client, "test_emp");
|
||||
loadDatasetIntoEs(client, "test_emp_copy");
|
||||
makeAlias(client, "test_alias", "test_emp", "test_emp_copy");
|
||||
makeAlias(client, "test_alias_emp", "test_emp", "test_emp_copy");
|
||||
}
|
||||
|
||||
protected static void loadDatasetIntoEs(RestClient client, String index) throws Exception {
|
||||
XContentBuilder createIndex = JsonXContent.contentBuilder().startObject();
|
||||
createIndex.startObject("settings");
|
||||
{
|
||||
@ -63,7 +70,7 @@ public class DataLoader {
|
||||
createIndex.endObject();
|
||||
}
|
||||
createIndex.endObject().endObject();
|
||||
client.performRequest("PUT", "/test_emp", emptyMap(), new StringEntity(createIndex.string(), ContentType.APPLICATION_JSON));
|
||||
client.performRequest("PUT", "/" + index, emptyMap(), new StringEntity(createIndex.string(), ContentType.APPLICATION_JSON));
|
||||
|
||||
StringBuilder bulk = new StringBuilder();
|
||||
csvToLines("employees", (titles, fields) -> {
|
||||
@ -77,10 +84,16 @@ public class DataLoader {
|
||||
}
|
||||
bulk.append("}\n");
|
||||
});
|
||||
client.performRequest("POST", "/test_emp/emp/_bulk", singletonMap("refresh", "true"),
|
||||
client.performRequest("POST", "/" + index + "/emp/_bulk", singletonMap("refresh", "true"),
|
||||
new StringEntity(bulk.toString(), ContentType.APPLICATION_JSON));
|
||||
}
|
||||
|
||||
protected static void makeAlias(RestClient client, String aliasName, String... indices) throws Exception {
|
||||
for (String index : indices) {
|
||||
client.performRequest("POST", "/" + index + "/_alias/" + aliasName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void csvToLines(String name, CheckedBiConsumer<List<String>, List<String>, Exception> consumeLine) throws Exception {
|
||||
String location = "/" + name + ".csv";
|
||||
URL dataSet = SqlSpecTestCase.class.getResource(location);
|
||||
|
@ -67,7 +67,7 @@ public class DatabaseMetaDataTestCase extends JdbcIntegrationTestCase {
|
||||
assertResultSets(all.get(), es.getMetaData().getTables("%", "%", "te%", null));
|
||||
assertResultSets(
|
||||
h2.createStatement().executeQuery("SELECT '" + clusterName() + "' AS TABLE_CAT, * FROM mock WHERE TABLE_NAME = 'test1'"),
|
||||
es.getMetaData().getTables("%", "%", "test1.d%", null));
|
||||
es.getMetaData().getTables("%", "%", "test1", null));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,9 +34,9 @@ public class DebugCsvSpec extends CsvSpecTestCase {
|
||||
//
|
||||
// uncomment this to printout the result set and create new CSV tests
|
||||
//
|
||||
//JdbcTestUtils.logResultSetMetadata(elastic, log);
|
||||
//JdbcTestUtils.logResultSetData(elastic, log);
|
||||
JdbcAssert.assertResultSets(expected, elastic, log);
|
||||
JdbcTestUtils.logResultSetMetadata(elastic, log);
|
||||
JdbcTestUtils.logResultSetData(elastic, log);
|
||||
//JdbcAssert.assertResultSets(expected, elastic, log);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -16,12 +16,10 @@ import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.test.rest.ESRestTestCase;
|
||||
import org.elasticsearch.xpack.qa.sql.embed.EmbeddedJdbcServer;
|
||||
import org.elasticsearch.xpack.qa.sql.rest.RestSqlTestCase;
|
||||
import org.elasticsearch.xpack.sql.jdbc.jdbc.JdbcConfiguration;
|
||||
import org.elasticsearch.xpack.sql.jdbc.jdbcx.JdbcDataSource;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -30,10 +30,10 @@ public class ShowTablesTestCase extends JdbcIntegrationTestCase {
|
||||
for (int i = 0; i < indices; i++) {
|
||||
String index = String.format(Locale.ROOT, "test%02d", i);
|
||||
index(index, builder -> builder.field("name", "bob"));
|
||||
h2.createStatement().executeUpdate("INSERT INTO mock VALUES ('" + index + "');");
|
||||
h2.createStatement().executeUpdate("INSERT INTO mock VALUES ('" + index + "', 'INDEX');");
|
||||
}
|
||||
|
||||
ResultSet expected = h2.createStatement().executeQuery("SELECT * FROM mock ORDER BY table");
|
||||
ResultSet expected = h2.createStatement().executeQuery("SELECT * FROM mock ORDER BY name");
|
||||
assertResultSets(expected, es.createStatement().executeQuery("SHOW TABLES"));
|
||||
}
|
||||
}
|
||||
|
93
qa/sql/src/main/resources/alias.csv-spec
Normal file
93
qa/sql/src/main/resources/alias.csv-spec
Normal file
@ -0,0 +1,93 @@
|
||||
tableAlias
|
||||
SELECT emp_no, first_name FROM test_alias ORDER BY emp_no LIMIT 6;
|
||||
|
||||
emp_no:i | first_name:s
|
||||
|
||||
10001 | Georgi
|
||||
10001 | Georgi
|
||||
10002 | Bezalel
|
||||
10002 | Bezalel
|
||||
10003 | Parto
|
||||
10003 | Parto
|
||||
;
|
||||
|
||||
tablePattern
|
||||
SELECT emp_no, first_name FROM test_alias ORDER BY emp_no LIMIT 6;
|
||||
emp_no:i | first_name:s
|
||||
|
||||
10001 | Georgi
|
||||
10001 | Georgi
|
||||
10002 | Bezalel
|
||||
10002 | Bezalel
|
||||
10003 | Parto
|
||||
10003 | Parto
|
||||
;
|
||||
|
||||
describeAlias
|
||||
DESCRIBE "test_alias";
|
||||
|
||||
column:s | type:s
|
||||
|
||||
birth_date | TIMESTAMP
|
||||
emp_no | INTEGER
|
||||
first_name | VARCHAR
|
||||
gender | VARCHAR
|
||||
hire_date | TIMESTAMP
|
||||
languages | TINYINT
|
||||
last_name | VARCHAR
|
||||
salary | INTEGER
|
||||
;
|
||||
|
||||
describePattern
|
||||
DESCRIBE "test_alias";
|
||||
|
||||
column:s | type:s
|
||||
|
||||
birth_date | TIMESTAMP
|
||||
emp_no | INTEGER
|
||||
first_name | VARCHAR
|
||||
gender | VARCHAR
|
||||
hire_date | TIMESTAMP
|
||||
languages | TINYINT
|
||||
last_name | VARCHAR
|
||||
salary | INTEGER
|
||||
;
|
||||
|
||||
//showAlias
|
||||
//SHOW TABLES 'test_alias';
|
||||
|
||||
//name:s | type:s
|
||||
//
|
||||
//test_alias | ALIAS
|
||||
//test_emp | INDEX
|
||||
//test_employees | INDEX
|
||||
//;
|
||||
|
||||
showPattern
|
||||
SHOW TABLES 'test_%';
|
||||
|
||||
name:s | type:s
|
||||
|
||||
test_alias | ALIAS
|
||||
test_alias_emp | ALIAS
|
||||
test_emp | INDEX
|
||||
test_emp_copy | INDEX
|
||||
;
|
||||
|
||||
testGroupByOnAlias
|
||||
SELECT gender, PERCENTILE(emp_no, 97) p1 FROM test_alias GROUP BY gender;
|
||||
|
||||
gender:s | p1:d
|
||||
|
||||
M | 10095.25
|
||||
F | 10099.0
|
||||
;
|
||||
|
||||
testGroupByOnPattern
|
||||
SELECT gender, PERCENTILE(emp_no, 97) p1 FROM test_alias GROUP BY gender;
|
||||
|
||||
gender:s | p1:d
|
||||
|
||||
M | 10095.25
|
||||
F | 10099.0
|
||||
;
|
@ -107,10 +107,10 @@ MINUTE_OF_DAY |SCALAR
|
||||
;
|
||||
|
||||
showTables
|
||||
SHOW TABLES;
|
||||
SHOW TABLES 'test_emp';
|
||||
|
||||
table:s
|
||||
test_emp
|
||||
name:s | type:s
|
||||
test_emp |INDEX
|
||||
;
|
||||
|
||||
// DESCRIBE
|
||||
|
@ -3,7 +3,8 @@
|
||||
//
|
||||
|
||||
debug
|
||||
SELECT 5 + 2 AS a;
|
||||
SHOW TABLES test_emp;
|
||||
//DESCRIBE test_alias;
|
||||
|
||||
table:s
|
||||
test_emp
|
||||
|
@ -1,3 +1,4 @@
|
||||
CREATE TABLE mock (
|
||||
"table" VARCHAR
|
||||
"name" VARCHAR,
|
||||
"type" VARCHAR
|
||||
);
|
||||
|
@ -51,10 +51,10 @@ statement
|
||||
)*
|
||||
')')?
|
||||
statement #debug
|
||||
| SHOW TABLES (LIKE? pattern=STRING)? #showTables
|
||||
| SHOW TABLES (LIKE? pattern)? #showTables
|
||||
| SHOW COLUMNS (FROM | IN) tableIdentifier #showColumns
|
||||
| (DESCRIBE | DESC) tableIdentifier #showColumns
|
||||
| SHOW FUNCTIONS (LIKE? pattern=STRING)? #showFunctions
|
||||
| SHOW FUNCTIONS (LIKE? pattern)? #showFunctions
|
||||
| SHOW SCHEMAS #showSchemas
|
||||
;
|
||||
|
||||
@ -171,10 +171,15 @@ predicate
|
||||
: NOT? kind=BETWEEN lower=valueExpression AND upper=valueExpression
|
||||
| NOT? kind=IN '(' expression (',' expression)* ')'
|
||||
| NOT? kind=IN '(' query ')'
|
||||
| NOT? kind=(LIKE | RLIKE) pattern=valueExpression
|
||||
| NOT? kind=LIKE pattern
|
||||
| NOT? kind=RLIKE regex=STRING
|
||||
| IS NOT? kind=NULL
|
||||
;
|
||||
|
||||
pattern
|
||||
: value=STRING (ESCAPE escape=STRING)?
|
||||
;
|
||||
|
||||
valueExpression
|
||||
: primaryExpression #valueExpressionDefault
|
||||
| operator=(MINUS | PLUS) valueExpression #arithmeticUnary
|
||||
@ -188,7 +193,7 @@ primaryExpression
|
||||
| EXTRACT '(' field=identifier FROM valueExpression ')' #extract
|
||||
| constant #constantDefault
|
||||
| ASTERISK #star
|
||||
| (qualifiedName '.')? ASTERISK #star
|
||||
| (qualifiedName DOT)? ASTERISK #star
|
||||
| identifier '(' (setQuantifier? expression (',' expression)*)? ')' #functionCall
|
||||
| '(' query ')' #subqueryExpression
|
||||
| identifier #columnReference
|
||||
@ -199,7 +204,6 @@ primaryExpression
|
||||
|
||||
constant
|
||||
: NULL #nullLiteral
|
||||
| identifier STRING #typeConstructor
|
||||
| number #numericLiteral
|
||||
| booleanValue #booleanLiteral
|
||||
| STRING+ #stringLiteral
|
||||
@ -218,11 +222,11 @@ dataType
|
||||
;
|
||||
|
||||
qualifiedName
|
||||
: (path=identifier '.')* name=identifier
|
||||
: (identifier DOT)* identifier
|
||||
;
|
||||
|
||||
tableIdentifier
|
||||
: index=identifier
|
||||
: identifier
|
||||
;
|
||||
|
||||
identifier
|
||||
@ -252,7 +256,7 @@ nonReserved
|
||||
| COLUMNS
|
||||
| DEBUG
|
||||
| EXECUTABLE | EXPLAIN
|
||||
| FORMAT | FUNCTIONS | FROM
|
||||
| FORMAT | FUNCTIONS
|
||||
| GRAPHVIZ
|
||||
| MAPPED
|
||||
| OPTIMIZED
|
||||
@ -279,6 +283,7 @@ DEBUG: 'DEBUG';
|
||||
DESC: 'DESC';
|
||||
DESCRIBE: 'DESCRIBE';
|
||||
DISTINCT: 'DISTINCT';
|
||||
ESCAPE: 'ESCAPE';
|
||||
EXECUTABLE: 'EXECUTABLE';
|
||||
EXISTS: 'EXISTS';
|
||||
EXPLAIN: 'EXPLAIN';
|
||||
@ -338,6 +343,7 @@ ASTERISK: '*';
|
||||
SLASH: '/';
|
||||
PERCENT: '%';
|
||||
CONCAT: '||';
|
||||
DOT: '.';
|
||||
|
||||
STRING
|
||||
: '\'' ( ~'\'' | '\'\'' )* '\''
|
||||
@ -348,10 +354,10 @@ INTEGER_VALUE
|
||||
;
|
||||
|
||||
DECIMAL_VALUE
|
||||
: DIGIT+ '.' DIGIT*
|
||||
| '.' DIGIT+
|
||||
| DIGIT+ ('.' DIGIT*)? EXPONENT
|
||||
| '.' DIGIT+ EXPONENT
|
||||
: DIGIT+ DOT DIGIT*
|
||||
| DOT DIGIT+
|
||||
| DIGIT+ (DOT DIGIT*)? EXPONENT
|
||||
| DOT DIGIT+ EXPONENT
|
||||
;
|
||||
|
||||
IDENTIFIER
|
||||
|
@ -1,163 +1,165 @@
|
||||
T__0=1
|
||||
T__1=2
|
||||
T__2=3
|
||||
T__3=4
|
||||
ALL=5
|
||||
ANALYZE=6
|
||||
ANALYZED=7
|
||||
AND=8
|
||||
ANY=9
|
||||
AS=10
|
||||
ASC=11
|
||||
BETWEEN=12
|
||||
BY=13
|
||||
CAST=14
|
||||
COLUMNS=15
|
||||
DEBUG=16
|
||||
DESC=17
|
||||
DESCRIBE=18
|
||||
DISTINCT=19
|
||||
EXECUTABLE=20
|
||||
EXISTS=21
|
||||
EXPLAIN=22
|
||||
EXTRACT=23
|
||||
FALSE=24
|
||||
FORMAT=25
|
||||
FROM=26
|
||||
FULL=27
|
||||
FUNCTIONS=28
|
||||
GRAPHVIZ=29
|
||||
GROUP=30
|
||||
HAVING=31
|
||||
IN=32
|
||||
INNER=33
|
||||
IS=34
|
||||
JOIN=35
|
||||
LEFT=36
|
||||
LIKE=37
|
||||
LIMIT=38
|
||||
MAPPED=39
|
||||
MATCH=40
|
||||
NATURAL=41
|
||||
NOT=42
|
||||
NULL=43
|
||||
ON=44
|
||||
OPTIMIZED=45
|
||||
OR=46
|
||||
ORDER=47
|
||||
OUTER=48
|
||||
PARSED=49
|
||||
PHYSICAL=50
|
||||
PLAN=51
|
||||
QUERY=52
|
||||
RIGHT=53
|
||||
RLIKE=54
|
||||
SCHEMAS=55
|
||||
SELECT=56
|
||||
SHOW=57
|
||||
TABLES=58
|
||||
TEXT=59
|
||||
TRUE=60
|
||||
USING=61
|
||||
VERIFY=62
|
||||
WHERE=63
|
||||
WITH=64
|
||||
EQ=65
|
||||
NEQ=66
|
||||
LT=67
|
||||
LTE=68
|
||||
GT=69
|
||||
GTE=70
|
||||
PLUS=71
|
||||
MINUS=72
|
||||
ASTERISK=73
|
||||
SLASH=74
|
||||
PERCENT=75
|
||||
CONCAT=76
|
||||
STRING=77
|
||||
INTEGER_VALUE=78
|
||||
DECIMAL_VALUE=79
|
||||
IDENTIFIER=80
|
||||
DIGIT_IDENTIFIER=81
|
||||
QUOTED_IDENTIFIER=82
|
||||
BACKQUOTED_IDENTIFIER=83
|
||||
SIMPLE_COMMENT=84
|
||||
BRACKETED_COMMENT=85
|
||||
WS=86
|
||||
UNRECOGNIZED=87
|
||||
DELIMITER=88
|
||||
'('=1
|
||||
')'=2
|
||||
','=3
|
||||
'.'=4
|
||||
'ALL'=5
|
||||
'ANALYZE'=6
|
||||
'ANALYZED'=7
|
||||
'AND'=8
|
||||
'ANY'=9
|
||||
'AS'=10
|
||||
'ASC'=11
|
||||
'BETWEEN'=12
|
||||
'BY'=13
|
||||
'CAST'=14
|
||||
'COLUMNS'=15
|
||||
'DEBUG'=16
|
||||
'DESC'=17
|
||||
'DESCRIBE'=18
|
||||
'DISTINCT'=19
|
||||
'EXECUTABLE'=20
|
||||
'EXISTS'=21
|
||||
'EXPLAIN'=22
|
||||
'EXTRACT'=23
|
||||
'FALSE'=24
|
||||
'FORMAT'=25
|
||||
'FROM'=26
|
||||
'FULL'=27
|
||||
'FUNCTIONS'=28
|
||||
'GRAPHVIZ'=29
|
||||
'GROUP'=30
|
||||
'HAVING'=31
|
||||
'IN'=32
|
||||
'INNER'=33
|
||||
'IS'=34
|
||||
'JOIN'=35
|
||||
'LEFT'=36
|
||||
'LIKE'=37
|
||||
'LIMIT'=38
|
||||
'MAPPED'=39
|
||||
'MATCH'=40
|
||||
'NATURAL'=41
|
||||
'NOT'=42
|
||||
'NULL'=43
|
||||
'ON'=44
|
||||
'OPTIMIZED'=45
|
||||
'OR'=46
|
||||
'ORDER'=47
|
||||
'OUTER'=48
|
||||
'PARSED'=49
|
||||
'PHYSICAL'=50
|
||||
'PLAN'=51
|
||||
'QUERY'=52
|
||||
'RIGHT'=53
|
||||
'RLIKE'=54
|
||||
'SCHEMAS'=55
|
||||
'SELECT'=56
|
||||
'SHOW'=57
|
||||
'TABLES'=58
|
||||
'TEXT'=59
|
||||
'TRUE'=60
|
||||
'USING'=61
|
||||
'VERIFY'=62
|
||||
'WHERE'=63
|
||||
'WITH'=64
|
||||
'='=65
|
||||
'<'=67
|
||||
'<='=68
|
||||
'>'=69
|
||||
'>='=70
|
||||
'+'=71
|
||||
'-'=72
|
||||
'*'=73
|
||||
'/'=74
|
||||
'%'=75
|
||||
'||'=76
|
||||
T__0=1
|
||||
T__1=2
|
||||
T__2=3
|
||||
ALL=4
|
||||
ANALYZE=5
|
||||
ANALYZED=6
|
||||
AND=7
|
||||
ANY=8
|
||||
AS=9
|
||||
ASC=10
|
||||
BETWEEN=11
|
||||
BY=12
|
||||
CAST=13
|
||||
COLUMNS=14
|
||||
DEBUG=15
|
||||
DESC=16
|
||||
DESCRIBE=17
|
||||
DISTINCT=18
|
||||
ESCAPE=19
|
||||
EXECUTABLE=20
|
||||
EXISTS=21
|
||||
EXPLAIN=22
|
||||
EXTRACT=23
|
||||
FALSE=24
|
||||
FORMAT=25
|
||||
FROM=26
|
||||
FULL=27
|
||||
FUNCTIONS=28
|
||||
GRAPHVIZ=29
|
||||
GROUP=30
|
||||
HAVING=31
|
||||
IN=32
|
||||
INNER=33
|
||||
IS=34
|
||||
JOIN=35
|
||||
LEFT=36
|
||||
LIKE=37
|
||||
LIMIT=38
|
||||
MAPPED=39
|
||||
MATCH=40
|
||||
NATURAL=41
|
||||
NOT=42
|
||||
NULL=43
|
||||
ON=44
|
||||
OPTIMIZED=45
|
||||
OR=46
|
||||
ORDER=47
|
||||
OUTER=48
|
||||
PARSED=49
|
||||
PHYSICAL=50
|
||||
PLAN=51
|
||||
QUERY=52
|
||||
RIGHT=53
|
||||
RLIKE=54
|
||||
SCHEMAS=55
|
||||
SELECT=56
|
||||
SHOW=57
|
||||
TABLES=58
|
||||
TEXT=59
|
||||
TRUE=60
|
||||
USING=61
|
||||
VERIFY=62
|
||||
WHERE=63
|
||||
WITH=64
|
||||
EQ=65
|
||||
NEQ=66
|
||||
LT=67
|
||||
LTE=68
|
||||
GT=69
|
||||
GTE=70
|
||||
PLUS=71
|
||||
MINUS=72
|
||||
ASTERISK=73
|
||||
SLASH=74
|
||||
PERCENT=75
|
||||
CONCAT=76
|
||||
DOT=77
|
||||
STRING=78
|
||||
INTEGER_VALUE=79
|
||||
DECIMAL_VALUE=80
|
||||
IDENTIFIER=81
|
||||
DIGIT_IDENTIFIER=82
|
||||
QUOTED_IDENTIFIER=83
|
||||
BACKQUOTED_IDENTIFIER=84
|
||||
SIMPLE_COMMENT=85
|
||||
BRACKETED_COMMENT=86
|
||||
WS=87
|
||||
UNRECOGNIZED=88
|
||||
DELIMITER=89
|
||||
'('=1
|
||||
')'=2
|
||||
','=3
|
||||
'ALL'=4
|
||||
'ANALYZE'=5
|
||||
'ANALYZED'=6
|
||||
'AND'=7
|
||||
'ANY'=8
|
||||
'AS'=9
|
||||
'ASC'=10
|
||||
'BETWEEN'=11
|
||||
'BY'=12
|
||||
'CAST'=13
|
||||
'COLUMNS'=14
|
||||
'DEBUG'=15
|
||||
'DESC'=16
|
||||
'DESCRIBE'=17
|
||||
'DISTINCT'=18
|
||||
'ESCAPE'=19
|
||||
'EXECUTABLE'=20
|
||||
'EXISTS'=21
|
||||
'EXPLAIN'=22
|
||||
'EXTRACT'=23
|
||||
'FALSE'=24
|
||||
'FORMAT'=25
|
||||
'FROM'=26
|
||||
'FULL'=27
|
||||
'FUNCTIONS'=28
|
||||
'GRAPHVIZ'=29
|
||||
'GROUP'=30
|
||||
'HAVING'=31
|
||||
'IN'=32
|
||||
'INNER'=33
|
||||
'IS'=34
|
||||
'JOIN'=35
|
||||
'LEFT'=36
|
||||
'LIKE'=37
|
||||
'LIMIT'=38
|
||||
'MAPPED'=39
|
||||
'MATCH'=40
|
||||
'NATURAL'=41
|
||||
'NOT'=42
|
||||
'NULL'=43
|
||||
'ON'=44
|
||||
'OPTIMIZED'=45
|
||||
'OR'=46
|
||||
'ORDER'=47
|
||||
'OUTER'=48
|
||||
'PARSED'=49
|
||||
'PHYSICAL'=50
|
||||
'PLAN'=51
|
||||
'QUERY'=52
|
||||
'RIGHT'=53
|
||||
'RLIKE'=54
|
||||
'SCHEMAS'=55
|
||||
'SELECT'=56
|
||||
'SHOW'=57
|
||||
'TABLES'=58
|
||||
'TEXT'=59
|
||||
'TRUE'=60
|
||||
'USING'=61
|
||||
'VERIFY'=62
|
||||
'WHERE'=63
|
||||
'WITH'=64
|
||||
'='=65
|
||||
'<'=67
|
||||
'<='=68
|
||||
'>'=69
|
||||
'>='=70
|
||||
'+'=71
|
||||
'-'=72
|
||||
'*'=73
|
||||
'/'=74
|
||||
'%'=75
|
||||
'||'=76
|
||||
'.'=77
|
||||
|
@ -1,162 +1,164 @@
|
||||
T__0=1
|
||||
T__1=2
|
||||
T__2=3
|
||||
T__3=4
|
||||
ALL=5
|
||||
ANALYZE=6
|
||||
ANALYZED=7
|
||||
AND=8
|
||||
ANY=9
|
||||
AS=10
|
||||
ASC=11
|
||||
BETWEEN=12
|
||||
BY=13
|
||||
CAST=14
|
||||
COLUMNS=15
|
||||
DEBUG=16
|
||||
DESC=17
|
||||
DESCRIBE=18
|
||||
DISTINCT=19
|
||||
EXECUTABLE=20
|
||||
EXISTS=21
|
||||
EXPLAIN=22
|
||||
EXTRACT=23
|
||||
FALSE=24
|
||||
FORMAT=25
|
||||
FROM=26
|
||||
FULL=27
|
||||
FUNCTIONS=28
|
||||
GRAPHVIZ=29
|
||||
GROUP=30
|
||||
HAVING=31
|
||||
IN=32
|
||||
INNER=33
|
||||
IS=34
|
||||
JOIN=35
|
||||
LEFT=36
|
||||
LIKE=37
|
||||
LIMIT=38
|
||||
MAPPED=39
|
||||
MATCH=40
|
||||
NATURAL=41
|
||||
NOT=42
|
||||
NULL=43
|
||||
ON=44
|
||||
OPTIMIZED=45
|
||||
OR=46
|
||||
ORDER=47
|
||||
OUTER=48
|
||||
PARSED=49
|
||||
PHYSICAL=50
|
||||
PLAN=51
|
||||
QUERY=52
|
||||
RIGHT=53
|
||||
RLIKE=54
|
||||
SCHEMAS=55
|
||||
SELECT=56
|
||||
SHOW=57
|
||||
TABLES=58
|
||||
TEXT=59
|
||||
TRUE=60
|
||||
USING=61
|
||||
VERIFY=62
|
||||
WHERE=63
|
||||
WITH=64
|
||||
EQ=65
|
||||
NEQ=66
|
||||
LT=67
|
||||
LTE=68
|
||||
GT=69
|
||||
GTE=70
|
||||
PLUS=71
|
||||
MINUS=72
|
||||
ASTERISK=73
|
||||
SLASH=74
|
||||
PERCENT=75
|
||||
CONCAT=76
|
||||
STRING=77
|
||||
INTEGER_VALUE=78
|
||||
DECIMAL_VALUE=79
|
||||
IDENTIFIER=80
|
||||
DIGIT_IDENTIFIER=81
|
||||
QUOTED_IDENTIFIER=82
|
||||
BACKQUOTED_IDENTIFIER=83
|
||||
SIMPLE_COMMENT=84
|
||||
BRACKETED_COMMENT=85
|
||||
WS=86
|
||||
UNRECOGNIZED=87
|
||||
'('=1
|
||||
')'=2
|
||||
','=3
|
||||
'.'=4
|
||||
'ALL'=5
|
||||
'ANALYZE'=6
|
||||
'ANALYZED'=7
|
||||
'AND'=8
|
||||
'ANY'=9
|
||||
'AS'=10
|
||||
'ASC'=11
|
||||
'BETWEEN'=12
|
||||
'BY'=13
|
||||
'CAST'=14
|
||||
'COLUMNS'=15
|
||||
'DEBUG'=16
|
||||
'DESC'=17
|
||||
'DESCRIBE'=18
|
||||
'DISTINCT'=19
|
||||
'EXECUTABLE'=20
|
||||
'EXISTS'=21
|
||||
'EXPLAIN'=22
|
||||
'EXTRACT'=23
|
||||
'FALSE'=24
|
||||
'FORMAT'=25
|
||||
'FROM'=26
|
||||
'FULL'=27
|
||||
'FUNCTIONS'=28
|
||||
'GRAPHVIZ'=29
|
||||
'GROUP'=30
|
||||
'HAVING'=31
|
||||
'IN'=32
|
||||
'INNER'=33
|
||||
'IS'=34
|
||||
'JOIN'=35
|
||||
'LEFT'=36
|
||||
'LIKE'=37
|
||||
'LIMIT'=38
|
||||
'MAPPED'=39
|
||||
'MATCH'=40
|
||||
'NATURAL'=41
|
||||
'NOT'=42
|
||||
'NULL'=43
|
||||
'ON'=44
|
||||
'OPTIMIZED'=45
|
||||
'OR'=46
|
||||
'ORDER'=47
|
||||
'OUTER'=48
|
||||
'PARSED'=49
|
||||
'PHYSICAL'=50
|
||||
'PLAN'=51
|
||||
'QUERY'=52
|
||||
'RIGHT'=53
|
||||
'RLIKE'=54
|
||||
'SCHEMAS'=55
|
||||
'SELECT'=56
|
||||
'SHOW'=57
|
||||
'TABLES'=58
|
||||
'TEXT'=59
|
||||
'TRUE'=60
|
||||
'USING'=61
|
||||
'VERIFY'=62
|
||||
'WHERE'=63
|
||||
'WITH'=64
|
||||
'='=65
|
||||
'<'=67
|
||||
'<='=68
|
||||
'>'=69
|
||||
'>='=70
|
||||
'+'=71
|
||||
'-'=72
|
||||
'*'=73
|
||||
'/'=74
|
||||
'%'=75
|
||||
'||'=76
|
||||
T__0=1
|
||||
T__1=2
|
||||
T__2=3
|
||||
ALL=4
|
||||
ANALYZE=5
|
||||
ANALYZED=6
|
||||
AND=7
|
||||
ANY=8
|
||||
AS=9
|
||||
ASC=10
|
||||
BETWEEN=11
|
||||
BY=12
|
||||
CAST=13
|
||||
COLUMNS=14
|
||||
DEBUG=15
|
||||
DESC=16
|
||||
DESCRIBE=17
|
||||
DISTINCT=18
|
||||
ESCAPE=19
|
||||
EXECUTABLE=20
|
||||
EXISTS=21
|
||||
EXPLAIN=22
|
||||
EXTRACT=23
|
||||
FALSE=24
|
||||
FORMAT=25
|
||||
FROM=26
|
||||
FULL=27
|
||||
FUNCTIONS=28
|
||||
GRAPHVIZ=29
|
||||
GROUP=30
|
||||
HAVING=31
|
||||
IN=32
|
||||
INNER=33
|
||||
IS=34
|
||||
JOIN=35
|
||||
LEFT=36
|
||||
LIKE=37
|
||||
LIMIT=38
|
||||
MAPPED=39
|
||||
MATCH=40
|
||||
NATURAL=41
|
||||
NOT=42
|
||||
NULL=43
|
||||
ON=44
|
||||
OPTIMIZED=45
|
||||
OR=46
|
||||
ORDER=47
|
||||
OUTER=48
|
||||
PARSED=49
|
||||
PHYSICAL=50
|
||||
PLAN=51
|
||||
QUERY=52
|
||||
RIGHT=53
|
||||
RLIKE=54
|
||||
SCHEMAS=55
|
||||
SELECT=56
|
||||
SHOW=57
|
||||
TABLES=58
|
||||
TEXT=59
|
||||
TRUE=60
|
||||
USING=61
|
||||
VERIFY=62
|
||||
WHERE=63
|
||||
WITH=64
|
||||
EQ=65
|
||||
NEQ=66
|
||||
LT=67
|
||||
LTE=68
|
||||
GT=69
|
||||
GTE=70
|
||||
PLUS=71
|
||||
MINUS=72
|
||||
ASTERISK=73
|
||||
SLASH=74
|
||||
PERCENT=75
|
||||
CONCAT=76
|
||||
DOT=77
|
||||
STRING=78
|
||||
INTEGER_VALUE=79
|
||||
DECIMAL_VALUE=80
|
||||
IDENTIFIER=81
|
||||
DIGIT_IDENTIFIER=82
|
||||
QUOTED_IDENTIFIER=83
|
||||
BACKQUOTED_IDENTIFIER=84
|
||||
SIMPLE_COMMENT=85
|
||||
BRACKETED_COMMENT=86
|
||||
WS=87
|
||||
UNRECOGNIZED=88
|
||||
'('=1
|
||||
')'=2
|
||||
','=3
|
||||
'ALL'=4
|
||||
'ANALYZE'=5
|
||||
'ANALYZED'=6
|
||||
'AND'=7
|
||||
'ANY'=8
|
||||
'AS'=9
|
||||
'ASC'=10
|
||||
'BETWEEN'=11
|
||||
'BY'=12
|
||||
'CAST'=13
|
||||
'COLUMNS'=14
|
||||
'DEBUG'=15
|
||||
'DESC'=16
|
||||
'DESCRIBE'=17
|
||||
'DISTINCT'=18
|
||||
'ESCAPE'=19
|
||||
'EXECUTABLE'=20
|
||||
'EXISTS'=21
|
||||
'EXPLAIN'=22
|
||||
'EXTRACT'=23
|
||||
'FALSE'=24
|
||||
'FORMAT'=25
|
||||
'FROM'=26
|
||||
'FULL'=27
|
||||
'FUNCTIONS'=28
|
||||
'GRAPHVIZ'=29
|
||||
'GROUP'=30
|
||||
'HAVING'=31
|
||||
'IN'=32
|
||||
'INNER'=33
|
||||
'IS'=34
|
||||
'JOIN'=35
|
||||
'LEFT'=36
|
||||
'LIKE'=37
|
||||
'LIMIT'=38
|
||||
'MAPPED'=39
|
||||
'MATCH'=40
|
||||
'NATURAL'=41
|
||||
'NOT'=42
|
||||
'NULL'=43
|
||||
'ON'=44
|
||||
'OPTIMIZED'=45
|
||||
'OR'=46
|
||||
'ORDER'=47
|
||||
'OUTER'=48
|
||||
'PARSED'=49
|
||||
'PHYSICAL'=50
|
||||
'PLAN'=51
|
||||
'QUERY'=52
|
||||
'RIGHT'=53
|
||||
'RLIKE'=54
|
||||
'SCHEMAS'=55
|
||||
'SELECT'=56
|
||||
'SHOW'=57
|
||||
'TABLES'=58
|
||||
'TEXT'=59
|
||||
'TRUE'=60
|
||||
'USING'=61
|
||||
'VERIFY'=62
|
||||
'WHERE'=63
|
||||
'WITH'=64
|
||||
'='=65
|
||||
'<'=67
|
||||
'<='=68
|
||||
'>'=69
|
||||
'>='=70
|
||||
'+'=71
|
||||
'-'=72
|
||||
'*'=73
|
||||
'/'=74
|
||||
'%'=75
|
||||
'||'=76
|
||||
'.'=77
|
||||
|
@ -7,7 +7,7 @@ package org.elasticsearch.xpack.sql.analysis.analyzer;
|
||||
|
||||
import org.elasticsearch.xpack.sql.analysis.AnalysisException;
|
||||
import org.elasticsearch.xpack.sql.analysis.analyzer.Verifier.Failure;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.GetIndexResult;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.IndexResolution;
|
||||
import org.elasticsearch.xpack.sql.capabilities.Resolvables;
|
||||
import org.elasticsearch.xpack.sql.expression.Alias;
|
||||
import org.elasticsearch.xpack.sql.expression.Attribute;
|
||||
@ -84,16 +84,16 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
|
||||
/**
|
||||
* Information about the index against which the SQL is being analyzed.
|
||||
*/
|
||||
private final GetIndexResult getIndexResult;
|
||||
private final IndexResolution indexResolution;
|
||||
/**
|
||||
* Time zone in which we're executing this SQL. It is attached to functions
|
||||
* that deal with date and time.
|
||||
*/
|
||||
private final DateTimeZone timeZone;
|
||||
|
||||
public Analyzer(FunctionRegistry functionRegistry, GetIndexResult getIndexResult, DateTimeZone timeZone) {
|
||||
public Analyzer(FunctionRegistry functionRegistry, IndexResolution results, DateTimeZone timeZone) {
|
||||
this.functionRegistry = functionRegistry;
|
||||
this.getIndexResult = getIndexResult;
|
||||
this.indexResolution = results;
|
||||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
@ -148,7 +148,7 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
|
||||
if (e instanceof UnresolvedAttribute) {
|
||||
UnresolvedAttribute ua = (UnresolvedAttribute) e;
|
||||
Attribute a = resolveAgainstList(ua, plan.output(), lenient);
|
||||
return (a != null ? a : e);
|
||||
return a != null ? a : e;
|
||||
}
|
||||
return e;
|
||||
});
|
||||
@ -254,12 +254,12 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
|
||||
@Override
|
||||
protected LogicalPlan rule(UnresolvedRelation plan) {
|
||||
TableIdentifier table = plan.table();
|
||||
if (getIndexResult.isValid() == false) {
|
||||
return plan.unresolvedMessage().equals(getIndexResult.toString())
|
||||
? plan : new UnresolvedRelation(plan.location(), plan.table(), plan.alias(), getIndexResult.toString());
|
||||
if (indexResolution.isValid() == false) {
|
||||
return plan.unresolvedMessage().equals(indexResolution.toString()) ? plan : new UnresolvedRelation(plan.location(),
|
||||
plan.table(), plan.alias(), indexResolution.toString());
|
||||
}
|
||||
assert getIndexResult.matches(table.index());
|
||||
LogicalPlan logicalPlan = new EsRelation(plan.location(), getIndexResult.get());
|
||||
assert indexResolution.matches(table.index());
|
||||
LogicalPlan logicalPlan = new EsRelation(plan.location(), indexResolution.get());
|
||||
SubQueryAlias sa = new SubQueryAlias(plan.location(), logicalPlan, table.index());
|
||||
|
||||
if (plan.alias() != null) {
|
||||
@ -844,7 +844,7 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
|
||||
}
|
||||
|
||||
private boolean hasUnresolvedAliases(List<? extends NamedExpression> expressions) {
|
||||
return (expressions != null && expressions.stream().anyMatch(e -> e instanceof UnresolvedAlias));
|
||||
return expressions != null && expressions.stream().anyMatch(e -> e instanceof UnresolvedAlias);
|
||||
}
|
||||
|
||||
private List<NamedExpression> assignAliases(List<? extends NamedExpression> exprs) {
|
||||
@ -1050,7 +1050,7 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
|
||||
// but with a twist; only if the tree is not resolved or analyzed
|
||||
@Override
|
||||
public final LogicalPlan apply(LogicalPlan plan) {
|
||||
return plan.transformUp(t -> t.analyzed() || (skipResolved() && t.resolved()) ? t : rule(t), typeToken());
|
||||
return plan.transformUp(t -> t.analyzed() || skipResolved() && t.resolved() ? t : rule(t), typeToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -9,16 +9,16 @@ import org.elasticsearch.common.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public final class GetIndexResult {
|
||||
public static GetIndexResult valid(EsIndex index) {
|
||||
public final class IndexResolution {
|
||||
public static IndexResolution valid(EsIndex index) {
|
||||
Objects.requireNonNull(index, "index must not be null if it was found");
|
||||
return new GetIndexResult(index, null);
|
||||
return new IndexResolution(index, null);
|
||||
}
|
||||
public static GetIndexResult invalid(String invalid) {
|
||||
public static IndexResolution invalid(String invalid) {
|
||||
Objects.requireNonNull(invalid, "invalid must not be null to signal that the index is invalid");
|
||||
return new GetIndexResult(null, invalid);
|
||||
return new IndexResolution(null, invalid);
|
||||
}
|
||||
public static GetIndexResult notFound(String name) {
|
||||
public static IndexResolution notFound(String name) {
|
||||
Objects.requireNonNull(name, "name must not be null");
|
||||
return invalid("Unknown index [" + name + "]");
|
||||
}
|
||||
@ -27,7 +27,7 @@ public final class GetIndexResult {
|
||||
@Nullable
|
||||
private final String invalid;
|
||||
|
||||
private GetIndexResult(EsIndex index, @Nullable String invalid) {
|
||||
private IndexResolution(EsIndex index, @Nullable String invalid) {
|
||||
this.index = index;
|
||||
this.invalid = invalid;
|
||||
}
|
||||
@ -60,7 +60,7 @@ public final class GetIndexResult {
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
GetIndexResult other = (GetIndexResult) obj;
|
||||
IndexResolution other = (IndexResolution) obj;
|
||||
return Objects.equals(index, other.index)
|
||||
&& Objects.equals(invalid, other.invalid);
|
||||
}
|
@ -5,15 +5,21 @@
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.analysis.index;
|
||||
|
||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
||||
|
||||
import org.elasticsearch.ElasticsearchSecurityException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
|
||||
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.get.GetIndexRequest.Feature;
|
||||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.elasticsearch.cluster.metadata.MappingMetaData;
|
||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||
import org.elasticsearch.index.IndexNotFoundException;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
import org.elasticsearch.xpack.sql.type.Types;
|
||||
|
||||
@ -22,9 +28,62 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
public class IndexResolver {
|
||||
|
||||
public enum IndexType {
|
||||
INDEX, ALIAS;
|
||||
}
|
||||
|
||||
public static class IndexInfo {
|
||||
private final String name;
|
||||
private final IndexType type;
|
||||
|
||||
private IndexInfo(String name, IndexType type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public IndexType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IndexResolver.IndexInfo other = (IndexResolver.IndexInfo) obj;
|
||||
return Objects.equals(name, other.name)
|
||||
&& Objects.equals(type, other.type);
|
||||
}
|
||||
}
|
||||
|
||||
private final Client client;
|
||||
|
||||
public IndexResolver(Client client) {
|
||||
@ -32,42 +91,134 @@ public class IndexResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a single index by name.
|
||||
* Resolves only the names, differentiating between indices and aliases.
|
||||
* This method is required since the other methods rely on mapping which is tied to an index (not an alias).
|
||||
*/
|
||||
public void asIndex(final String index, ActionListener<GetIndexResult> listener) {
|
||||
GetIndexRequest getIndexRequest = createGetIndexRequest(index);
|
||||
client.admin().indices().getIndex(getIndexRequest, ActionListener.wrap(getIndexResponse -> {
|
||||
GetIndexResult result;
|
||||
if (getIndexResponse.getMappings().size() > 1) {
|
||||
result = GetIndexResult.invalid(
|
||||
"[" + index + "] is an alias pointing to more than one index which is currently incompatible with sql");
|
||||
} else if (getIndexResponse.getMappings().size() == 1){
|
||||
ObjectObjectCursor<String, ImmutableOpenMap<String, MappingMetaData>> indexMappings =
|
||||
getIndexResponse.getMappings().iterator().next();
|
||||
String concreteIndex = indexMappings.key;
|
||||
/*
|
||||
* here we don't support wildcards: we can either have an alias or an index. However names get resolved (through
|
||||
* security or not) we need to preserve the original names as they will be used in the subsequent search request.
|
||||
* With security enabled, if the user is authorized for an alias and not its corresponding concrete index, we have to
|
||||
* make sure that the search is executed against the same alias name from the original command, rather than
|
||||
* the resolved concrete index that we get back from the get index API
|
||||
*/
|
||||
result = buildGetIndexResult(concreteIndex, index, indexMappings.value);
|
||||
} else {
|
||||
result = GetIndexResult.notFound(index);
|
||||
public void resolveNames(String indexWildcard, String javaRegex, ActionListener<Set<IndexInfo>> listener) {
|
||||
// first get aliases
|
||||
GetAliasesRequest aliasRequest = new GetAliasesRequest()
|
||||
.local(true)
|
||||
.aliases(indexWildcard)
|
||||
.indicesOptions(IndicesOptions.lenientExpandOpen());
|
||||
|
||||
client.admin().indices().getAliases(aliasRequest, ActionListener.wrap(aliases ->
|
||||
resolveIndices(indexWildcard, javaRegex, aliases, listener),
|
||||
ex -> {
|
||||
// with security, two exception can be thrown:
|
||||
// INFE - if no alias matches
|
||||
// security exception is the user cannot access aliases
|
||||
|
||||
// in both cases, that is allowed and we continue with the indices request
|
||||
if (ex instanceof IndexNotFoundException || ex instanceof ElasticsearchSecurityException) {
|
||||
resolveIndices(indexWildcard, javaRegex, null, listener);
|
||||
} else {
|
||||
listener.onFailure(ex);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private void resolveIndices(String indexWildcard, String javaRegex, GetAliasesResponse aliases,
|
||||
ActionListener<Set<IndexInfo>> listener) {
|
||||
GetIndexRequest indexRequest = new GetIndexRequest()
|
||||
.local(true)
|
||||
.indices(indexWildcard)
|
||||
.indicesOptions(IndicesOptions.lenientExpandOpen());
|
||||
|
||||
client.admin().indices().getIndex(indexRequest, ActionListener.wrap(indices -> {
|
||||
String[] indicesNames = indices.indices();
|
||||
|
||||
// since the index name does not support ?, filter the results manually
|
||||
Pattern pattern = javaRegex != null ? Pattern.compile(javaRegex) : null;
|
||||
|
||||
Set<IndexInfo> result = new TreeSet<>(Comparator.comparing(IndexInfo::name));
|
||||
// unpack aliases (if present)
|
||||
if (aliases != null) {
|
||||
for (ObjectCursor<List<AliasMetaData>> cursor : aliases.getAliases().values()) {
|
||||
for (AliasMetaData amd : cursor.value) {
|
||||
String alias = amd.alias();
|
||||
if (alias != null && (pattern == null || pattern.matcher(alias).matches())) {
|
||||
result.add(new IndexInfo(alias, IndexType.ALIAS));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (indicesNames != null) {
|
||||
for (String indexName : indicesNames) {
|
||||
if (pattern == null || pattern.matcher(indexName).matches()) {
|
||||
result.add(new IndexInfo(indexName, IndexType.INDEX));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
listener.onResponse(result);
|
||||
}, listener::onFailure));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Discover (multiple) matching indices for a given name.
|
||||
* Resolves a pattern to one (potentially compound meaning that spawns multiple indices) mapping.
|
||||
*/
|
||||
public void asList(String index, ActionListener<List<EsIndex>> listener) {
|
||||
GetIndexRequest getIndexRequest = createGetIndexRequest(index);
|
||||
public void resolveWithSameMapping(String indexWildcard, String javaRegex, ActionListener<IndexResolution> listener) {
|
||||
GetIndexRequest getIndexRequest = createGetIndexRequest(indexWildcard);
|
||||
client.admin().indices().getIndex(getIndexRequest, ActionListener.wrap(response -> {
|
||||
ImmutableOpenMap<String, ImmutableOpenMap<String, MappingMetaData>> mappings = response.getMappings();
|
||||
|
||||
List<IndexResolution> resolutions;
|
||||
if (mappings.size() > 0) {
|
||||
resolutions = new ArrayList<>(mappings.size());
|
||||
Pattern pattern = javaRegex != null ? Pattern.compile(javaRegex) : null;
|
||||
for (ObjectObjectCursor<String, ImmutableOpenMap<String, MappingMetaData>> indexMappings : mappings) {
|
||||
String concreteIndex = indexMappings.key;
|
||||
if (pattern == null || pattern.matcher(concreteIndex).matches()) {
|
||||
resolutions.add(buildGetIndexResult(concreteIndex, concreteIndex, indexMappings.value));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resolutions = emptyList();
|
||||
}
|
||||
|
||||
listener.onResponse(merge(resolutions, indexWildcard));
|
||||
}, listener::onFailure));
|
||||
}
|
||||
|
||||
static IndexResolution merge(List<IndexResolution> resolutions, String indexWildcard) {
|
||||
IndexResolution merged = null;
|
||||
for (IndexResolution resolution : resolutions) {
|
||||
// everything that follows gets compared
|
||||
if (!resolution.isValid()) {
|
||||
return resolution;
|
||||
}
|
||||
// initialize resolution on first run
|
||||
if (merged == null) {
|
||||
merged = resolution;
|
||||
}
|
||||
// need the same mapping across all resolutions
|
||||
if (!merged.get().mapping().equals(resolution.get().mapping())) {
|
||||
return IndexResolution.invalid(
|
||||
"[" + indexWildcard + "] points to indices [" + resolution.get().name() + "] "
|
||||
+ "and [" + resolution.get().name() + "] which have different mappings. "
|
||||
+ "When using multiple indices, the mappings must be identical.");
|
||||
}
|
||||
}
|
||||
if (merged != null) {
|
||||
// at this point, we are sure there's the same mapping across all (if that's the case) indices
|
||||
// to keep things simple, use the given pattern as index name
|
||||
merged = IndexResolution.valid(new EsIndex(indexWildcard, merged.get().mapping()));
|
||||
} else {
|
||||
merged = IndexResolution.notFound(indexWildcard);
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a pattern to multiple, separate indices.
|
||||
*/
|
||||
public void resolveAsSeparateMappings(String indexWildcard, String javaRegex, ActionListener<List<EsIndex>> listener) {
|
||||
GetIndexRequest getIndexRequest = createGetIndexRequest(indexWildcard);
|
||||
client.admin().indices().getIndex(getIndexRequest, ActionListener.wrap(getIndexResponse -> {
|
||||
ImmutableOpenMap<String, ImmutableOpenMap<String, MappingMetaData>> mappings = getIndexResponse.getMappings();
|
||||
List<EsIndex> results = new ArrayList<>(mappings.size());
|
||||
Pattern pattern = javaRegex != null ? Pattern.compile(javaRegex) : null;
|
||||
for (ObjectObjectCursor<String, ImmutableOpenMap<String, MappingMetaData>> indexMappings : mappings) {
|
||||
/*
|
||||
* We support wildcard expressions here, and it's only for commands that only perform the get index call.
|
||||
@ -76,9 +227,11 @@ public class IndexResolver {
|
||||
* and not the concrete index: there is a well known information leak of the concrete index name in the response.
|
||||
*/
|
||||
String concreteIndex = indexMappings.key;
|
||||
GetIndexResult getIndexResult = buildGetIndexResult(concreteIndex, concreteIndex, indexMappings.value);
|
||||
if (getIndexResult.isValid()) {
|
||||
results.add(getIndexResult.get());
|
||||
if (pattern == null || pattern.matcher(concreteIndex).matches()) {
|
||||
IndexResolution getIndexResult = buildGetIndexResult(concreteIndex, concreteIndex, indexMappings.value);
|
||||
if (getIndexResult.isValid()) {
|
||||
results.add(getIndexResult.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
results.sort(Comparator.comparing(EsIndex::name));
|
||||
@ -96,7 +249,7 @@ public class IndexResolver {
|
||||
.indicesOptions(IndicesOptions.lenientExpandOpen());
|
||||
}
|
||||
|
||||
private static GetIndexResult buildGetIndexResult(String concreteIndex, String indexOrAlias,
|
||||
private static IndexResolution buildGetIndexResult(String concreteIndex, String indexOrAlias,
|
||||
ImmutableOpenMap<String, MappingMetaData> mappings) {
|
||||
|
||||
// Make sure that the index contains only a single type
|
||||
@ -119,17 +272,17 @@ public class IndexResolver {
|
||||
}
|
||||
|
||||
if (singleType == null) {
|
||||
return GetIndexResult.invalid("[" + indexOrAlias + "] doesn't have any types so it is incompatible with sql");
|
||||
return IndexResolution.invalid("[" + indexOrAlias + "] doesn't have any types so it is incompatible with sql");
|
||||
} else if (typeNames != null) {
|
||||
Collections.sort(typeNames);
|
||||
return GetIndexResult.invalid(
|
||||
return IndexResolution.invalid(
|
||||
"[" + indexOrAlias + "] contains more than one type " + typeNames + " so it is incompatible with sql");
|
||||
} else {
|
||||
try {
|
||||
Map<String, DataType> mapping = Types.fromEs(singleType.sourceAsMap());
|
||||
return GetIndexResult.valid(new EsIndex(indexOrAlias, mapping));
|
||||
return IndexResolution.valid(new EsIndex(indexOrAlias, mapping));
|
||||
} catch (MappingException ex) {
|
||||
return GetIndexResult.invalid(ex.getMessage());
|
||||
return IndexResolution.invalid(ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -221,10 +221,6 @@ public class Scroller {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(SearchResponse response) {
|
||||
super.onResponse(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleResponse(SearchResponse response, ActionListener<SchemaRowSet> listener) {
|
||||
@ -313,8 +309,9 @@ public class Scroller {
|
||||
ShardSearchFailure[] failure = response.getShardFailures();
|
||||
if (!CollectionUtils.isEmpty(failure)) {
|
||||
cleanupScroll(response, new ExecutionException(failure[0].reason(), failure[0].getCause()));
|
||||
} else {
|
||||
handleResponse(response, listener);
|
||||
}
|
||||
handleResponse(response, listener);
|
||||
} catch (Exception ex) {
|
||||
cleanupScroll(response, ex);
|
||||
}
|
||||
@ -326,11 +323,11 @@ public class Scroller {
|
||||
protected final void cleanupScroll(SearchResponse response, Exception ex) {
|
||||
if (response != null && response.getScrollId() != null) {
|
||||
client.prepareClearScroll().addScrollId(response.getScrollId())
|
||||
// in case of failure, report the initial exception instead of the one resulting from cleaning the scroll
|
||||
.execute(ActionListener.wrap(r -> listener.onFailure(ex), e -> {
|
||||
ex.addSuppressed(e);
|
||||
listener.onFailure(ex);
|
||||
}));
|
||||
// in case of failure, report the initial exception instead of the one resulting from cleaning the scroll
|
||||
.execute(ActionListener.wrap(r -> listener.onFailure(ex), e -> {
|
||||
ex.addSuppressed(e);
|
||||
listener.onFailure(ex);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,13 +13,18 @@ import org.elasticsearch.xpack.sql.type.DataTypes;
|
||||
|
||||
public class Like extends BinaryExpression {
|
||||
|
||||
public Like(Location location, Expression left, Expression right) {
|
||||
public Like(Location location, Expression left, LikePattern right) {
|
||||
super(location, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LikePattern right() {
|
||||
return (LikePattern) super.right();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Like swapLeftAndRight() {
|
||||
return new Like(location(), right(), left());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.expression.regex;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.LeafExpression;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
import org.elasticsearch.xpack.sql.type.DataTypes;
|
||||
import org.elasticsearch.xpack.sql.util.StringUtils;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A SQL 'like' pattern.
|
||||
* Similar to basic regex, supporting '_' instead of '?' and '%' instead of '*'.
|
||||
* <p>
|
||||
* Allows escaping based on a regular char.
|
||||
*
|
||||
* To prevent conflicts with ES, the string and char must be validated to not contain '*'.
|
||||
*/
|
||||
public class LikePattern extends LeafExpression {
|
||||
|
||||
private final String pattern;
|
||||
private final char escape;
|
||||
|
||||
private final String regex;
|
||||
private final String wildcard;
|
||||
private final String indexNameWildcard;
|
||||
|
||||
public LikePattern(Location location, String pattern, char escape) {
|
||||
super(location);
|
||||
this.pattern = pattern;
|
||||
this.escape = escape;
|
||||
// early initialization to force string validation
|
||||
this.regex = StringUtils.likeToJavaPattern(pattern, escape);
|
||||
this.wildcard = StringUtils.likeToLuceneWildcard(pattern, escape);
|
||||
this.indexNameWildcard = StringUtils.likeToIndexWildcard(pattern, escape);
|
||||
}
|
||||
|
||||
public String pattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public char escape() {
|
||||
return escape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pattern in (Java) regex format.
|
||||
*/
|
||||
public String asJavaRegex() {
|
||||
return regex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pattern in (Lucene) wildcard format.
|
||||
*/
|
||||
public String asLuceneWildcard() {
|
||||
return wildcard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pattern in (IndexNameExpressionResolver) wildcard format.
|
||||
*/
|
||||
public String asIndexNameWildcard() {
|
||||
return indexNameWildcard;
|
||||
}
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataTypes.KEYWORD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(pattern, escape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LikePattern other = (LikePattern) obj;
|
||||
return Objects.equals(pattern, other.pattern)
|
||||
&& Objects.equals(escape, other.escape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return pattern;
|
||||
}
|
||||
}
|
@ -7,19 +7,25 @@ package org.elasticsearch.xpack.sql.expression.regex;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.BinaryExpression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Literal;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
import org.elasticsearch.xpack.sql.type.DataTypes;
|
||||
|
||||
public class RLike extends BinaryExpression {
|
||||
|
||||
public RLike(Location location, Expression left, Expression right) {
|
||||
public RLike(Location location, Expression left, Literal right) {
|
||||
super(location, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Literal right() {
|
||||
return (Literal) super.right();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RLike swapLeftAndRight() {
|
||||
return new RLike(location(), right(), left());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -102,12 +102,12 @@ abstract class CommandBuilder extends LogicalPlanBuilder {
|
||||
|
||||
@Override
|
||||
public Object visitShowFunctions(ShowFunctionsContext ctx) {
|
||||
return new ShowFunctions(source(ctx), string(ctx.pattern));
|
||||
return new ShowFunctions(source(ctx), visitPattern(ctx.pattern()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitShowTables(ShowTablesContext ctx) {
|
||||
return new ShowTables(source(ctx), string(ctx.pattern));
|
||||
return new ShowTables(source(ctx), visitPattern(ctx.pattern()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -120,4 +120,4 @@ abstract class CommandBuilder extends LogicalPlanBuilder {
|
||||
TableIdentifier identifier = visitTableIdentifier(ctx.tableIdentifier());
|
||||
return new ShowColumns(source(ctx), identifier.index());
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.elasticsearch.common.Booleans;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.xpack.sql.expression.Alias;
|
||||
import org.elasticsearch.xpack.sql.expression.Exists;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
@ -41,6 +42,7 @@ import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MatchQueryPredi
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MultiMatchQueryPredicate;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.StringQueryPredicate;
|
||||
import org.elasticsearch.xpack.sql.expression.regex.Like;
|
||||
import org.elasticsearch.xpack.sql.expression.regex.LikePattern;
|
||||
import org.elasticsearch.xpack.sql.expression.regex.RLike;
|
||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ArithmeticBinaryContext;
|
||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ArithmeticUnaryContext;
|
||||
@ -61,6 +63,7 @@ import org.elasticsearch.xpack.sql.parser.SqlBaseParser.MultiMatchQueryContext;
|
||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.NullLiteralContext;
|
||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.OrderByContext;
|
||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ParenthesizedExpressionContext;
|
||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.PatternContext;
|
||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.PredicateContext;
|
||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.PredicatedContext;
|
||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.PrimitiveDataTypeContext;
|
||||
@ -186,10 +189,10 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
|
||||
e = new In(loc, exp, expressions(pCtx.expression()));
|
||||
break;
|
||||
case SqlBaseParser.LIKE:
|
||||
e = new Like(loc, exp, expression(pCtx.pattern));
|
||||
e = new Like(loc, exp, visitPattern(pCtx.pattern()));
|
||||
break;
|
||||
case SqlBaseParser.RLIKE:
|
||||
e = new RLike(loc, exp, expression(pCtx.pattern));
|
||||
e = new RLike(loc, exp, new Literal(source(pCtx.regex), string(pCtx.regex), DataTypes.KEYWORD));
|
||||
break;
|
||||
case SqlBaseParser.NULL:
|
||||
// shortcut to avoid double negation later on (since there's no IsNull (missing in ES is a negated exists))
|
||||
@ -202,10 +205,60 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
|
||||
return pCtx.NOT() != null ? new Not(loc, e) : e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LikePattern visitPattern(PatternContext ctx) {
|
||||
if (ctx == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String pattern = string(ctx.value);
|
||||
int pos = pattern.indexOf('*');
|
||||
if (pos >= 0) {
|
||||
throw new ParsingException(source(ctx.value),
|
||||
"Invalid char [*] found in pattern [%s] at position %d; use [%%] or [_] instead",
|
||||
pattern, pos);
|
||||
}
|
||||
|
||||
char escape = 0;
|
||||
String escapeString = string(ctx.escape);
|
||||
|
||||
if (Strings.hasText(escapeString)) {
|
||||
// shouldn't happen but adding validation in case the string parsing gets wonky
|
||||
if (escapeString.length() > 1) {
|
||||
throw new ParsingException(source(ctx.escape), "A character not a string required for escaping; found [%s]", escapeString);
|
||||
} else if (escapeString.length() == 1) {
|
||||
escape = escapeString.charAt(0);
|
||||
// these chars already have a meaning
|
||||
if (escape == '*' || escape == '%' || escape == '_') {
|
||||
throw new ParsingException(source(ctx.escape), "Char [%c] cannot be used for escaping", escape);
|
||||
}
|
||||
// lastly validate that escape chars (if present) are followed by special chars
|
||||
for (int i = 0; i < pattern.length(); i++) {
|
||||
char current = pattern.charAt(i);
|
||||
if (current == escape) {
|
||||
if (i + 1 == pattern.length()) {
|
||||
throw new ParsingException(source(ctx.value),
|
||||
"Pattern [%s] is invalid as escape char [%c] at position %d does not escape anything", pattern, escape,
|
||||
i);
|
||||
}
|
||||
char next = pattern.charAt(i + 1);
|
||||
if (next != '%' && next != '_') {
|
||||
throw new ParsingException(source(ctx.value),
|
||||
"Pattern [%s] is invalid as escape char [%c] at position %d can only escape wildcard chars; found [%c]",
|
||||
pattern, escape, i, next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new LikePattern(source(ctx), pattern, escape);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Arithmetic
|
||||
//
|
||||
|
||||
@Override
|
||||
public Object visitArithmeticUnary(ArithmeticUnaryContext ctx) {
|
||||
Expression value = expression(ctx.valueExpression());
|
||||
@ -317,7 +370,7 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
|
||||
String name = visitIdentifier(ctx.identifier());
|
||||
boolean isDistinct = false;
|
||||
if (ctx.setQuantifier() != null) {
|
||||
isDistinct = (ctx.setQuantifier().DISTINCT() != null);
|
||||
isDistinct = ctx.setQuantifier().DISTINCT() != null;
|
||||
}
|
||||
|
||||
return new UnresolvedFunction(source(ctx), name, isDistinct, expressions(ctx.expression()));
|
||||
@ -408,4 +461,4 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
|
||||
// TODO: this can be improved to use the smallest type available
|
||||
return new Literal(source(ctx), bigD.longValueExact(), DataTypes.INTEGER);
|
||||
}
|
||||
}
|
||||
}
|
@ -12,19 +12,14 @@ import org.elasticsearch.xpack.sql.parser.SqlBaseParser.TableIdentifierContext;
|
||||
import org.elasticsearch.xpack.sql.plan.TableIdentifier;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
abstract class IdentifierBuilder extends AbstractBuilder {
|
||||
|
||||
@Override
|
||||
public TableIdentifier visitTableIdentifier(TableIdentifierContext ctx) {
|
||||
String index = text(ctx.index);
|
||||
|
||||
String index = ctx.getText();
|
||||
Location source = source(ctx);
|
||||
validateIndex(index, source);
|
||||
|
||||
validateIndex(ctx.getText(), source);
|
||||
return new TableIdentifier(source, index);
|
||||
}
|
||||
|
||||
@ -33,10 +28,10 @@ abstract class IdentifierBuilder extends AbstractBuilder {
|
||||
for (int i = 0; i < index.length(); i++) {
|
||||
char c = index.charAt(i);
|
||||
if (Character.isUpperCase(c)) {
|
||||
throw new ParsingException(source, format(Locale.ROOT, "Invalid index name (needs to be lowercase) %s", index));
|
||||
throw new ParsingException(source, "Invalid index name (needs to be lowercase) %s", index);
|
||||
}
|
||||
if (c == '\\' || c == '/' || c == '<' || c == '>' || c == '|' || c == ',' || c == ' ') {
|
||||
throw new ParsingException(source, format(Locale.ROOT, "Invalid index name (illegal character %c) %s", c, index));
|
||||
throw new ParsingException(source, "Invalid index name (illegal character %c) %s", c, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -51,7 +46,7 @@ abstract class IdentifierBuilder extends AbstractBuilder {
|
||||
if (ctx == null) {
|
||||
return null;
|
||||
}
|
||||
// TODO: maybe it makes sense to introduce a dedicated object?
|
||||
|
||||
return Strings.collectionToDelimitedString(visitList(ctx.identifier(), String.class), ".");
|
||||
}
|
||||
}
|
||||
}
|
@ -484,6 +484,18 @@ class SqlBaseBaseListener implements SqlBaseListener {
|
||||
* <p>The default implementation does nothing.</p>
|
||||
*/
|
||||
@Override public void exitPredicate(SqlBaseParser.PredicateContext ctx) { }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation does nothing.</p>
|
||||
*/
|
||||
@Override public void enterPattern(SqlBaseParser.PatternContext ctx) { }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation does nothing.</p>
|
||||
*/
|
||||
@Override public void exitPattern(SqlBaseParser.PatternContext ctx) { }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
@ -652,18 +664,6 @@ class SqlBaseBaseListener implements SqlBaseListener {
|
||||
* <p>The default implementation does nothing.</p>
|
||||
*/
|
||||
@Override public void exitNullLiteral(SqlBaseParser.NullLiteralContext ctx) { }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation does nothing.</p>
|
||||
*/
|
||||
@Override public void enterTypeConstructor(SqlBaseParser.TypeConstructorContext ctx) { }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation does nothing.</p>
|
||||
*/
|
||||
@Override public void exitTypeConstructor(SqlBaseParser.TypeConstructorContext ctx) { }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
@ -289,6 +289,13 @@ class SqlBaseBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements SqlBa
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitPredicate(SqlBaseParser.PredicateContext ctx) { return visitChildren(ctx); }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitPattern(SqlBaseParser.PatternContext ctx) { return visitChildren(ctx); }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
@ -387,13 +394,6 @@ class SqlBaseBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements SqlBa
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitNullLiteral(SqlBaseParser.NullLiteralContext ctx) { return visitChildren(ctx); }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation returns the result of calling
|
||||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@Override public T visitTypeConstructor(SqlBaseParser.TypeConstructorContext ctx) { return visitChildren(ctx); }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
@ -22,9 +22,9 @@ class SqlBaseLexer extends Lexer {
|
||||
protected static final PredictionContextCache _sharedContextCache =
|
||||
new PredictionContextCache();
|
||||
public static final int
|
||||
T__0=1, T__1=2, T__2=3, T__3=4, ALL=5, ANALYZE=6, ANALYZED=7, AND=8, ANY=9,
|
||||
AS=10, ASC=11, BETWEEN=12, BY=13, CAST=14, COLUMNS=15, DEBUG=16, DESC=17,
|
||||
DESCRIBE=18, DISTINCT=19, EXECUTABLE=20, EXISTS=21, EXPLAIN=22, EXTRACT=23,
|
||||
T__0=1, T__1=2, T__2=3, ALL=4, ANALYZE=5, ANALYZED=6, AND=7, ANY=8, AS=9,
|
||||
ASC=10, BETWEEN=11, BY=12, CAST=13, COLUMNS=14, DEBUG=15, DESC=16, DESCRIBE=17,
|
||||
DISTINCT=18, ESCAPE=19, EXECUTABLE=20, EXISTS=21, EXPLAIN=22, EXTRACT=23,
|
||||
FALSE=24, FORMAT=25, FROM=26, FULL=27, FUNCTIONS=28, GRAPHVIZ=29, GROUP=30,
|
||||
HAVING=31, IN=32, INNER=33, IS=34, JOIN=35, LEFT=36, LIKE=37, LIMIT=38,
|
||||
MAPPED=39, MATCH=40, NATURAL=41, NOT=42, NULL=43, ON=44, OPTIMIZED=45,
|
||||
@ -32,54 +32,54 @@ class SqlBaseLexer extends Lexer {
|
||||
RIGHT=53, RLIKE=54, SCHEMAS=55, SELECT=56, SHOW=57, TABLES=58, TEXT=59,
|
||||
TRUE=60, USING=61, VERIFY=62, WHERE=63, WITH=64, EQ=65, NEQ=66, LT=67,
|
||||
LTE=68, GT=69, GTE=70, PLUS=71, MINUS=72, ASTERISK=73, SLASH=74, PERCENT=75,
|
||||
CONCAT=76, STRING=77, INTEGER_VALUE=78, DECIMAL_VALUE=79, IDENTIFIER=80,
|
||||
DIGIT_IDENTIFIER=81, QUOTED_IDENTIFIER=82, BACKQUOTED_IDENTIFIER=83, SIMPLE_COMMENT=84,
|
||||
BRACKETED_COMMENT=85, WS=86, UNRECOGNIZED=87;
|
||||
CONCAT=76, DOT=77, STRING=78, INTEGER_VALUE=79, DECIMAL_VALUE=80, IDENTIFIER=81,
|
||||
DIGIT_IDENTIFIER=82, QUOTED_IDENTIFIER=83, BACKQUOTED_IDENTIFIER=84, SIMPLE_COMMENT=85,
|
||||
BRACKETED_COMMENT=86, WS=87, UNRECOGNIZED=88;
|
||||
public static String[] modeNames = {
|
||||
"DEFAULT_MODE"
|
||||
};
|
||||
|
||||
public static final String[] ruleNames = {
|
||||
"T__0", "T__1", "T__2", "T__3", "ALL", "ANALYZE", "ANALYZED", "AND", "ANY",
|
||||
"AS", "ASC", "BETWEEN", "BY", "CAST", "COLUMNS", "DEBUG", "DESC", "DESCRIBE",
|
||||
"DISTINCT", "EXECUTABLE", "EXISTS", "EXPLAIN", "EXTRACT", "FALSE", "FORMAT",
|
||||
"FROM", "FULL", "FUNCTIONS", "GRAPHVIZ", "GROUP", "HAVING", "IN", "INNER",
|
||||
"IS", "JOIN", "LEFT", "LIKE", "LIMIT", "MAPPED", "MATCH", "NATURAL", "NOT",
|
||||
"NULL", "ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED", "PHYSICAL",
|
||||
"PLAN", "QUERY", "RIGHT", "RLIKE", "SCHEMAS", "SELECT", "SHOW", "TABLES",
|
||||
"TEXT", "TRUE", "USING", "VERIFY", "WHERE", "WITH", "EQ", "NEQ", "LT",
|
||||
"LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "CONCAT",
|
||||
"STRING", "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER", "DIGIT_IDENTIFIER",
|
||||
"QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", "EXPONENT", "DIGIT", "LETTER",
|
||||
"SIMPLE_COMMENT", "BRACKETED_COMMENT", "WS", "UNRECOGNIZED"
|
||||
"T__0", "T__1", "T__2", "ALL", "ANALYZE", "ANALYZED", "AND", "ANY", "AS",
|
||||
"ASC", "BETWEEN", "BY", "CAST", "COLUMNS", "DEBUG", "DESC", "DESCRIBE",
|
||||
"DISTINCT", "ESCAPE", "EXECUTABLE", "EXISTS", "EXPLAIN", "EXTRACT", "FALSE",
|
||||
"FORMAT", "FROM", "FULL", "FUNCTIONS", "GRAPHVIZ", "GROUP", "HAVING",
|
||||
"IN", "INNER", "IS", "JOIN", "LEFT", "LIKE", "LIMIT", "MAPPED", "MATCH",
|
||||
"NATURAL", "NOT", "NULL", "ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED",
|
||||
"PHYSICAL", "PLAN", "QUERY", "RIGHT", "RLIKE", "SCHEMAS", "SELECT", "SHOW",
|
||||
"TABLES", "TEXT", "TRUE", "USING", "VERIFY", "WHERE", "WITH", "EQ", "NEQ",
|
||||
"LT", "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT",
|
||||
"CONCAT", "DOT", "STRING", "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER",
|
||||
"DIGIT_IDENTIFIER", "QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", "EXPONENT",
|
||||
"DIGIT", "LETTER", "SIMPLE_COMMENT", "BRACKETED_COMMENT", "WS", "UNRECOGNIZED"
|
||||
};
|
||||
|
||||
private static final String[] _LITERAL_NAMES = {
|
||||
null, "'('", "')'", "','", "'.'", "'ALL'", "'ANALYZE'", "'ANALYZED'",
|
||||
"'AND'", "'ANY'", "'AS'", "'ASC'", "'BETWEEN'", "'BY'", "'CAST'", "'COLUMNS'",
|
||||
"'DEBUG'", "'DESC'", "'DESCRIBE'", "'DISTINCT'", "'EXECUTABLE'", "'EXISTS'",
|
||||
"'EXPLAIN'", "'EXTRACT'", "'FALSE'", "'FORMAT'", "'FROM'", "'FULL'", "'FUNCTIONS'",
|
||||
"'GRAPHVIZ'", "'GROUP'", "'HAVING'", "'IN'", "'INNER'", "'IS'", "'JOIN'",
|
||||
"'LEFT'", "'LIKE'", "'LIMIT'", "'MAPPED'", "'MATCH'", "'NATURAL'", "'NOT'",
|
||||
"'NULL'", "'ON'", "'OPTIMIZED'", "'OR'", "'ORDER'", "'OUTER'", "'PARSED'",
|
||||
"'PHYSICAL'", "'PLAN'", "'QUERY'", "'RIGHT'", "'RLIKE'", "'SCHEMAS'",
|
||||
"'SELECT'", "'SHOW'", "'TABLES'", "'TEXT'", "'TRUE'", "'USING'", "'VERIFY'",
|
||||
"'WHERE'", "'WITH'", "'='", null, "'<'", "'<='", "'>'", "'>='", "'+'",
|
||||
"'-'", "'*'", "'/'", "'%'", "'||'"
|
||||
null, "'('", "')'", "','", "'ALL'", "'ANALYZE'", "'ANALYZED'", "'AND'",
|
||||
"'ANY'", "'AS'", "'ASC'", "'BETWEEN'", "'BY'", "'CAST'", "'COLUMNS'",
|
||||
"'DEBUG'", "'DESC'", "'DESCRIBE'", "'DISTINCT'", "'ESCAPE'", "'EXECUTABLE'",
|
||||
"'EXISTS'", "'EXPLAIN'", "'EXTRACT'", "'FALSE'", "'FORMAT'", "'FROM'",
|
||||
"'FULL'", "'FUNCTIONS'", "'GRAPHVIZ'", "'GROUP'", "'HAVING'", "'IN'",
|
||||
"'INNER'", "'IS'", "'JOIN'", "'LEFT'", "'LIKE'", "'LIMIT'", "'MAPPED'",
|
||||
"'MATCH'", "'NATURAL'", "'NOT'", "'NULL'", "'ON'", "'OPTIMIZED'", "'OR'",
|
||||
"'ORDER'", "'OUTER'", "'PARSED'", "'PHYSICAL'", "'PLAN'", "'QUERY'", "'RIGHT'",
|
||||
"'RLIKE'", "'SCHEMAS'", "'SELECT'", "'SHOW'", "'TABLES'", "'TEXT'", "'TRUE'",
|
||||
"'USING'", "'VERIFY'", "'WHERE'", "'WITH'", "'='", null, "'<'", "'<='",
|
||||
"'>'", "'>='", "'+'", "'-'", "'*'", "'/'", "'%'", "'||'", "'.'"
|
||||
};
|
||||
private static final String[] _SYMBOLIC_NAMES = {
|
||||
null, null, null, null, null, "ALL", "ANALYZE", "ANALYZED", "AND", "ANY",
|
||||
"AS", "ASC", "BETWEEN", "BY", "CAST", "COLUMNS", "DEBUG", "DESC", "DESCRIBE",
|
||||
"DISTINCT", "EXECUTABLE", "EXISTS", "EXPLAIN", "EXTRACT", "FALSE", "FORMAT",
|
||||
"FROM", "FULL", "FUNCTIONS", "GRAPHVIZ", "GROUP", "HAVING", "IN", "INNER",
|
||||
"IS", "JOIN", "LEFT", "LIKE", "LIMIT", "MAPPED", "MATCH", "NATURAL", "NOT",
|
||||
"NULL", "ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED", "PHYSICAL",
|
||||
"PLAN", "QUERY", "RIGHT", "RLIKE", "SCHEMAS", "SELECT", "SHOW", "TABLES",
|
||||
"TEXT", "TRUE", "USING", "VERIFY", "WHERE", "WITH", "EQ", "NEQ", "LT",
|
||||
"LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "CONCAT",
|
||||
"STRING", "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER", "DIGIT_IDENTIFIER",
|
||||
"QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", "SIMPLE_COMMENT", "BRACKETED_COMMENT",
|
||||
"WS", "UNRECOGNIZED"
|
||||
null, null, null, null, "ALL", "ANALYZE", "ANALYZED", "AND", "ANY", "AS",
|
||||
"ASC", "BETWEEN", "BY", "CAST", "COLUMNS", "DEBUG", "DESC", "DESCRIBE",
|
||||
"DISTINCT", "ESCAPE", "EXECUTABLE", "EXISTS", "EXPLAIN", "EXTRACT", "FALSE",
|
||||
"FORMAT", "FROM", "FULL", "FUNCTIONS", "GRAPHVIZ", "GROUP", "HAVING",
|
||||
"IN", "INNER", "IS", "JOIN", "LEFT", "LIKE", "LIMIT", "MAPPED", "MATCH",
|
||||
"NATURAL", "NOT", "NULL", "ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED",
|
||||
"PHYSICAL", "PLAN", "QUERY", "RIGHT", "RLIKE", "SCHEMAS", "SELECT", "SHOW",
|
||||
"TABLES", "TEXT", "TRUE", "USING", "VERIFY", "WHERE", "WITH", "EQ", "NEQ",
|
||||
"LT", "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT",
|
||||
"CONCAT", "DOT", "STRING", "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER",
|
||||
"DIGIT_IDENTIFIER", "QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", "SIMPLE_COMMENT",
|
||||
"BRACKETED_COMMENT", "WS", "UNRECOGNIZED"
|
||||
};
|
||||
public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
|
||||
|
||||
@ -136,7 +136,7 @@ class SqlBaseLexer extends Lexer {
|
||||
public ATN getATN() { return _ATN; }
|
||||
|
||||
public static final String _serializedATN =
|
||||
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2Y\u02ee\b\1\4\2\t"+
|
||||
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2Z\u02f7\b\1\4\2\t"+
|
||||
"\2\4\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\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
|
||||
"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+
|
||||
@ -146,250 +146,253 @@ class SqlBaseLexer extends Lexer {
|
||||
"\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\4;\t;\4<\t<\4=\t="+
|
||||
"\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\tC\4D\tD\4E\tE\4F\tF\4G\tG\4H\tH\4I"+
|
||||
"\tI\4J\tJ\4K\tK\4L\tL\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\tT"+
|
||||
"\4U\tU\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\3\2\3\2\3\3\3\3\3\4\3\4\3\5"+
|
||||
"\3\5\3\6\3\6\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\b\3\b\3\b\3\b\3"+
|
||||
"\b\3\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t\3\n\3\n\3\n\3\n\3\13\3\13\3\13\3\f"+
|
||||
"\3\f\3\f\3\f\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\16\3\16\3\16\3\17\3\17"+
|
||||
"\3\17\3\17\3\17\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\21\3\21\3\21"+
|
||||
"\3\21\3\21\3\21\3\22\3\22\3\22\3\22\3\22\3\23\3\23\3\23\3\23\3\23\3\23"+
|
||||
"\3\23\3\23\3\23\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\25\3\25"+
|
||||
"\3\25\3\25\3\25\3\25\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\26"+
|
||||
"\3\26\3\26\3\27\3\27\3\27\3\27\3\27\3\27\3\27\3\27\3\30\3\30\3\30\3\30"+
|
||||
"\3\30\3\30\3\30\3\30\3\31\3\31\3\31\3\31\3\31\3\31\3\32\3\32\3\32\3\32"+
|
||||
"\3\32\3\32\3\32\3\33\3\33\3\33\3\33\3\33\3\34\3\34\3\34\3\34\3\34\3\35"+
|
||||
"\3\35\3\35\3\35\3\35\3\35\3\35\3\35\3\35\3\35\3\36\3\36\3\36\3\36\3\36"+
|
||||
"\3\36\3\36\3\36\3\36\3\37\3\37\3\37\3\37\3\37\3\37\3 \3 \3 \3 \3 \3 \3"+
|
||||
" \3!\3!\3!\3\"\3\"\3\"\3\"\3\"\3\"\3#\3#\3#\3$\3$\3$\3$\3$\3%\3%\3%\3"+
|
||||
"%\3%\3&\3&\3&\3&\3&\3\'\3\'\3\'\3\'\3\'\3\'\3(\3(\3(\3(\3(\3(\3(\3)\3"+
|
||||
")\3)\3)\3)\3)\3*\3*\3*\3*\3*\3*\3*\3*\3+\3+\3+\3+\3,\3,\3,\3,\3,\3-\3"+
|
||||
"-\3-\3.\3.\3.\3.\3.\3.\3.\3.\3.\3.\3/\3/\3/\3\60\3\60\3\60\3\60\3\60\3"+
|
||||
"\60\3\61\3\61\3\61\3\61\3\61\3\61\3\62\3\62\3\62\3\62\3\62\3\62\3\62\3"+
|
||||
"\63\3\63\3\63\3\63\3\63\3\63\3\63\3\63\3\63\3\64\3\64\3\64\3\64\3\64\3"+
|
||||
"\65\3\65\3\65\3\65\3\65\3\65\3\66\3\66\3\66\3\66\3\66\3\66\3\67\3\67\3"+
|
||||
"\67\3\67\3\67\3\67\38\38\38\38\38\38\38\38\39\39\39\39\39\39\39\3:\3:"+
|
||||
"\3:\3:\3:\3;\3;\3;\3;\3;\3;\3;\3<\3<\3<\3<\3<\3=\3=\3=\3=\3=\3>\3>\3>"+
|
||||
"\3>\3>\3>\3?\3?\3?\3?\3?\3?\3?\3@\3@\3@\3@\3@\3@\3A\3A\3A\3A\3A\3B\3B"+
|
||||
"\3C\3C\3C\3C\3C\3C\3C\5C\u023a\nC\3D\3D\3E\3E\3E\3F\3F\3G\3G\3G\3H\3H"+
|
||||
"\3I\3I\3J\3J\3K\3K\3L\3L\3M\3M\3M\3N\3N\3N\3N\7N\u0257\nN\fN\16N\u025a"+
|
||||
"\13N\3N\3N\3O\6O\u025f\nO\rO\16O\u0260\3P\6P\u0264\nP\rP\16P\u0265\3P"+
|
||||
"\3P\7P\u026a\nP\fP\16P\u026d\13P\3P\3P\6P\u0271\nP\rP\16P\u0272\3P\6P"+
|
||||
"\u0276\nP\rP\16P\u0277\3P\3P\7P\u027c\nP\fP\16P\u027f\13P\5P\u0281\nP"+
|
||||
"\3P\3P\3P\3P\6P\u0287\nP\rP\16P\u0288\3P\3P\5P\u028d\nP\3Q\3Q\5Q\u0291"+
|
||||
"\nQ\3Q\3Q\3Q\7Q\u0296\nQ\fQ\16Q\u0299\13Q\3R\3R\3R\3R\6R\u029f\nR\rR\16"+
|
||||
"R\u02a0\3S\3S\3S\3S\7S\u02a7\nS\fS\16S\u02aa\13S\3S\3S\3T\3T\3T\3T\7T"+
|
||||
"\u02b2\nT\fT\16T\u02b5\13T\3T\3T\3U\3U\5U\u02bb\nU\3U\6U\u02be\nU\rU\16"+
|
||||
"U\u02bf\3V\3V\3W\3W\3X\3X\3X\3X\7X\u02ca\nX\fX\16X\u02cd\13X\3X\5X\u02d0"+
|
||||
"\nX\3X\5X\u02d3\nX\3X\3X\3Y\3Y\3Y\3Y\3Y\7Y\u02dc\nY\fY\16Y\u02df\13Y\3"+
|
||||
"Y\3Y\3Y\3Y\3Y\3Z\6Z\u02e7\nZ\rZ\16Z\u02e8\3Z\3Z\3[\3[\3\u02dd\2\\\3\3"+
|
||||
"\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17\35\20\37\21"+
|
||||
"!\22#\23%\24\'\25)\26+\27-\30/\31\61\32\63\33\65\34\67\359\36;\37= ?!"+
|
||||
"A\"C#E$G%I&K\'M(O)Q*S+U,W-Y.[/]\60_\61a\62c\63e\64g\65i\66k\67m8o9q:s"+
|
||||
";u<w=y>{?}@\177A\u0081B\u0083C\u0085D\u0087E\u0089F\u008bG\u008dH\u008f"+
|
||||
"I\u0091J\u0093K\u0095L\u0097M\u0099N\u009bO\u009dP\u009fQ\u00a1R\u00a3"+
|
||||
"S\u00a5T\u00a7U\u00a9\2\u00ab\2\u00ad\2\u00afV\u00b1W\u00b3X\u00b5Y\3"+
|
||||
"\2\13\3\2))\5\2<<BBaa\3\2$$\3\2bb\4\2--//\3\2\62;\3\2C\\\4\2\f\f\17\17"+
|
||||
"\5\2\13\f\17\17\"\"\u030c\2\3\3\2\2\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\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\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3"+
|
||||
"\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2"+
|
||||
"\2\2\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2\2\2\2\63\3\2\2\2\2\65\3\2\2\2\2\67"+
|
||||
"\3\2\2\2\29\3\2\2\2\2;\3\2\2\2\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2"+
|
||||
"\2\2\2E\3\2\2\2\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2"+
|
||||
"\2Q\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2[\3\2\2\2\2]"+
|
||||
"\3\2\2\2\2_\3\2\2\2\2a\3\2\2\2\2c\3\2\2\2\2e\3\2\2\2\2g\3\2\2\2\2i\3\2"+
|
||||
"\2\2\2k\3\2\2\2\2m\3\2\2\2\2o\3\2\2\2\2q\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2"+
|
||||
"\2w\3\2\2\2\2y\3\2\2\2\2{\3\2\2\2\2}\3\2\2\2\2\177\3\2\2\2\2\u0081\3\2"+
|
||||
"\2\2\2\u0083\3\2\2\2\2\u0085\3\2\2\2\2\u0087\3\2\2\2\2\u0089\3\2\2\2\2"+
|
||||
"\u008b\3\2\2\2\2\u008d\3\2\2\2\2\u008f\3\2\2\2\2\u0091\3\2\2\2\2\u0093"+
|
||||
"\3\2\2\2\2\u0095\3\2\2\2\2\u0097\3\2\2\2\2\u0099\3\2\2\2\2\u009b\3\2\2"+
|
||||
"\2\2\u009d\3\2\2\2\2\u009f\3\2\2\2\2\u00a1\3\2\2\2\2\u00a3\3\2\2\2\2\u00a5"+
|
||||
"\3\2\2\2\2\u00a7\3\2\2\2\2\u00af\3\2\2\2\2\u00b1\3\2\2\2\2\u00b3\3\2\2"+
|
||||
"\2\2\u00b5\3\2\2\2\3\u00b7\3\2\2\2\5\u00b9\3\2\2\2\7\u00bb\3\2\2\2\t\u00bd"+
|
||||
"\3\2\2\2\13\u00bf\3\2\2\2\r\u00c3\3\2\2\2\17\u00cb\3\2\2\2\21\u00d4\3"+
|
||||
"\2\2\2\23\u00d8\3\2\2\2\25\u00dc\3\2\2\2\27\u00df\3\2\2\2\31\u00e3\3\2"+
|
||||
"\2\2\33\u00eb\3\2\2\2\35\u00ee\3\2\2\2\37\u00f3\3\2\2\2!\u00fb\3\2\2\2"+
|
||||
"#\u0101\3\2\2\2%\u0106\3\2\2\2\'\u010f\3\2\2\2)\u0118\3\2\2\2+\u0123\3"+
|
||||
"\2\2\2-\u012a\3\2\2\2/\u0132\3\2\2\2\61\u013a\3\2\2\2\63\u0140\3\2\2\2"+
|
||||
"\65\u0147\3\2\2\2\67\u014c\3\2\2\29\u0151\3\2\2\2;\u015b\3\2\2\2=\u0164"+
|
||||
"\3\2\2\2?\u016a\3\2\2\2A\u0171\3\2\2\2C\u0174\3\2\2\2E\u017a\3\2\2\2G"+
|
||||
"\u017d\3\2\2\2I\u0182\3\2\2\2K\u0187\3\2\2\2M\u018c\3\2\2\2O\u0192\3\2"+
|
||||
"\2\2Q\u0199\3\2\2\2S\u019f\3\2\2\2U\u01a7\3\2\2\2W\u01ab\3\2\2\2Y\u01b0"+
|
||||
"\3\2\2\2[\u01b3\3\2\2\2]\u01bd\3\2\2\2_\u01c0\3\2\2\2a\u01c6\3\2\2\2c"+
|
||||
"\u01cc\3\2\2\2e\u01d3\3\2\2\2g\u01dc\3\2\2\2i\u01e1\3\2\2\2k\u01e7\3\2"+
|
||||
"\2\2m\u01ed\3\2\2\2o\u01f3\3\2\2\2q\u01fb\3\2\2\2s\u0202\3\2\2\2u\u0207"+
|
||||
"\3\2\2\2w\u020e\3\2\2\2y\u0213\3\2\2\2{\u0218\3\2\2\2}\u021e\3\2\2\2\177"+
|
||||
"\u0225\3\2\2\2\u0081\u022b\3\2\2\2\u0083\u0230\3\2\2\2\u0085\u0239\3\2"+
|
||||
"\2\2\u0087\u023b\3\2\2\2\u0089\u023d\3\2\2\2\u008b\u0240\3\2\2\2\u008d"+
|
||||
"\u0242\3\2\2\2\u008f\u0245\3\2\2\2\u0091\u0247\3\2\2\2\u0093\u0249\3\2"+
|
||||
"\2\2\u0095\u024b\3\2\2\2\u0097\u024d\3\2\2\2\u0099\u024f\3\2\2\2\u009b"+
|
||||
"\u0252\3\2\2\2\u009d\u025e\3\2\2\2\u009f\u028c\3\2\2\2\u00a1\u0290\3\2"+
|
||||
"\2\2\u00a3\u029a\3\2\2\2\u00a5\u02a2\3\2\2\2\u00a7\u02ad\3\2\2\2\u00a9"+
|
||||
"\u02b8\3\2\2\2\u00ab\u02c1\3\2\2\2\u00ad\u02c3\3\2\2\2\u00af\u02c5\3\2"+
|
||||
"\2\2\u00b1\u02d6\3\2\2\2\u00b3\u02e6\3\2\2\2\u00b5\u02ec\3\2\2\2\u00b7"+
|
||||
"\u00b8\7*\2\2\u00b8\4\3\2\2\2\u00b9\u00ba\7+\2\2\u00ba\6\3\2\2\2\u00bb"+
|
||||
"\u00bc\7.\2\2\u00bc\b\3\2\2\2\u00bd\u00be\7\60\2\2\u00be\n\3\2\2\2\u00bf"+
|
||||
"\u00c0\7C\2\2\u00c0\u00c1\7N\2\2\u00c1\u00c2\7N\2\2\u00c2\f\3\2\2\2\u00c3"+
|
||||
"\u00c4\7C\2\2\u00c4\u00c5\7P\2\2\u00c5\u00c6\7C\2\2\u00c6\u00c7\7N\2\2"+
|
||||
"\u00c7\u00c8\7[\2\2\u00c8\u00c9\7\\\2\2\u00c9\u00ca\7G\2\2\u00ca\16\3"+
|
||||
"\2\2\2\u00cb\u00cc\7C\2\2\u00cc\u00cd\7P\2\2\u00cd\u00ce\7C\2\2\u00ce"+
|
||||
"\u00cf\7N\2\2\u00cf\u00d0\7[\2\2\u00d0\u00d1\7\\\2\2\u00d1\u00d2\7G\2"+
|
||||
"\2\u00d2\u00d3\7F\2\2\u00d3\20\3\2\2\2\u00d4\u00d5\7C\2\2\u00d5\u00d6"+
|
||||
"\7P\2\2\u00d6\u00d7\7F\2\2\u00d7\22\3\2\2\2\u00d8\u00d9\7C\2\2\u00d9\u00da"+
|
||||
"\7P\2\2\u00da\u00db\7[\2\2\u00db\24\3\2\2\2\u00dc\u00dd\7C\2\2\u00dd\u00de"+
|
||||
"\7U\2\2\u00de\26\3\2\2\2\u00df\u00e0\7C\2\2\u00e0\u00e1\7U\2\2\u00e1\u00e2"+
|
||||
"\7E\2\2\u00e2\30\3\2\2\2\u00e3\u00e4\7D\2\2\u00e4\u00e5\7G\2\2\u00e5\u00e6"+
|
||||
"\7V\2\2\u00e6\u00e7\7Y\2\2\u00e7\u00e8\7G\2\2\u00e8\u00e9\7G\2\2\u00e9"+
|
||||
"\u00ea\7P\2\2\u00ea\32\3\2\2\2\u00eb\u00ec\7D\2\2\u00ec\u00ed\7[\2\2\u00ed"+
|
||||
"\34\3\2\2\2\u00ee\u00ef\7E\2\2\u00ef\u00f0\7C\2\2\u00f0\u00f1\7U\2\2\u00f1"+
|
||||
"\u00f2\7V\2\2\u00f2\36\3\2\2\2\u00f3\u00f4\7E\2\2\u00f4\u00f5\7Q\2\2\u00f5"+
|
||||
"\u00f6\7N\2\2\u00f6\u00f7\7W\2\2\u00f7\u00f8\7O\2\2\u00f8\u00f9\7P\2\2"+
|
||||
"\u00f9\u00fa\7U\2\2\u00fa \3\2\2\2\u00fb\u00fc\7F\2\2\u00fc\u00fd\7G\2"+
|
||||
"\2\u00fd\u00fe\7D\2\2\u00fe\u00ff\7W\2\2\u00ff\u0100\7I\2\2\u0100\"\3"+
|
||||
"\2\2\2\u0101\u0102\7F\2\2\u0102\u0103\7G\2\2\u0103\u0104\7U\2\2\u0104"+
|
||||
"\u0105\7E\2\2\u0105$\3\2\2\2\u0106\u0107\7F\2\2\u0107\u0108\7G\2\2\u0108"+
|
||||
"\u0109\7U\2\2\u0109\u010a\7E\2\2\u010a\u010b\7T\2\2\u010b\u010c\7K\2\2"+
|
||||
"\u010c\u010d\7D\2\2\u010d\u010e\7G\2\2\u010e&\3\2\2\2\u010f\u0110\7F\2"+
|
||||
"\2\u0110\u0111\7K\2\2\u0111\u0112\7U\2\2\u0112\u0113\7V\2\2\u0113\u0114"+
|
||||
"\7K\2\2\u0114\u0115\7P\2\2\u0115\u0116\7E\2\2\u0116\u0117\7V\2\2\u0117"+
|
||||
"(\3\2\2\2\u0118\u0119\7G\2\2\u0119\u011a\7Z\2\2\u011a\u011b\7G\2\2\u011b"+
|
||||
"\u011c\7E\2\2\u011c\u011d\7W\2\2\u011d\u011e\7V\2\2\u011e\u011f\7C\2\2"+
|
||||
"\u011f\u0120\7D\2\2\u0120\u0121\7N\2\2\u0121\u0122\7G\2\2\u0122*\3\2\2"+
|
||||
"\2\u0123\u0124\7G\2\2\u0124\u0125\7Z\2\2\u0125\u0126\7K\2\2\u0126\u0127"+
|
||||
"\7U\2\2\u0127\u0128\7V\2\2\u0128\u0129\7U\2\2\u0129,\3\2\2\2\u012a\u012b"+
|
||||
"\7G\2\2\u012b\u012c\7Z\2\2\u012c\u012d\7R\2\2\u012d\u012e\7N\2\2\u012e"+
|
||||
"\u012f\7C\2\2\u012f\u0130\7K\2\2\u0130\u0131\7P\2\2\u0131.\3\2\2\2\u0132"+
|
||||
"\u0133\7G\2\2\u0133\u0134\7Z\2\2\u0134\u0135\7V\2\2\u0135\u0136\7T\2\2"+
|
||||
"\u0136\u0137\7C\2\2\u0137\u0138\7E\2\2\u0138\u0139\7V\2\2\u0139\60\3\2"+
|
||||
"\2\2\u013a\u013b\7H\2\2\u013b\u013c\7C\2\2\u013c\u013d\7N\2\2\u013d\u013e"+
|
||||
"\7U\2\2\u013e\u013f\7G\2\2\u013f\62\3\2\2\2\u0140\u0141\7H\2\2\u0141\u0142"+
|
||||
"\7Q\2\2\u0142\u0143\7T\2\2\u0143\u0144\7O\2\2\u0144\u0145\7C\2\2\u0145"+
|
||||
"\u0146\7V\2\2\u0146\64\3\2\2\2\u0147\u0148\7H\2\2\u0148\u0149\7T\2\2\u0149"+
|
||||
"\u014a\7Q\2\2\u014a\u014b\7O\2\2\u014b\66\3\2\2\2\u014c\u014d\7H\2\2\u014d"+
|
||||
"\u014e\7W\2\2\u014e\u014f\7N\2\2\u014f\u0150\7N\2\2\u01508\3\2\2\2\u0151"+
|
||||
"\u0152\7H\2\2\u0152\u0153\7W\2\2\u0153\u0154\7P\2\2\u0154\u0155\7E\2\2"+
|
||||
"\u0155\u0156\7V\2\2\u0156\u0157\7K\2\2\u0157\u0158\7Q\2\2\u0158\u0159"+
|
||||
"\7P\2\2\u0159\u015a\7U\2\2\u015a:\3\2\2\2\u015b\u015c\7I\2\2\u015c\u015d"+
|
||||
"\7T\2\2\u015d\u015e\7C\2\2\u015e\u015f\7R\2\2\u015f\u0160\7J\2\2\u0160"+
|
||||
"\u0161\7X\2\2\u0161\u0162\7K\2\2\u0162\u0163\7\\\2\2\u0163<\3\2\2\2\u0164"+
|
||||
"\u0165\7I\2\2\u0165\u0166\7T\2\2\u0166\u0167\7Q\2\2\u0167\u0168\7W\2\2"+
|
||||
"\u0168\u0169\7R\2\2\u0169>\3\2\2\2\u016a\u016b\7J\2\2\u016b\u016c\7C\2"+
|
||||
"\2\u016c\u016d\7X\2\2\u016d\u016e\7K\2\2\u016e\u016f\7P\2\2\u016f\u0170"+
|
||||
"\7I\2\2\u0170@\3\2\2\2\u0171\u0172\7K\2\2\u0172\u0173\7P\2\2\u0173B\3"+
|
||||
"\2\2\2\u0174\u0175\7K\2\2\u0175\u0176\7P\2\2\u0176\u0177\7P\2\2\u0177"+
|
||||
"\u0178\7G\2\2\u0178\u0179\7T\2\2\u0179D\3\2\2\2\u017a\u017b\7K\2\2\u017b"+
|
||||
"\u017c\7U\2\2\u017cF\3\2\2\2\u017d\u017e\7L\2\2\u017e\u017f\7Q\2\2\u017f"+
|
||||
"\u0180\7K\2\2\u0180\u0181\7P\2\2\u0181H\3\2\2\2\u0182\u0183\7N\2\2\u0183"+
|
||||
"\u0184\7G\2\2\u0184\u0185\7H\2\2\u0185\u0186\7V\2\2\u0186J\3\2\2\2\u0187"+
|
||||
"\u0188\7N\2\2\u0188\u0189\7K\2\2\u0189\u018a\7M\2\2\u018a\u018b\7G\2\2"+
|
||||
"\u018bL\3\2\2\2\u018c\u018d\7N\2\2\u018d\u018e\7K\2\2\u018e\u018f\7O\2"+
|
||||
"\2\u018f\u0190\7K\2\2\u0190\u0191\7V\2\2\u0191N\3\2\2\2\u0192\u0193\7"+
|
||||
"O\2\2\u0193\u0194\7C\2\2\u0194\u0195\7R\2\2\u0195\u0196\7R\2\2\u0196\u0197"+
|
||||
"\7G\2\2\u0197\u0198\7F\2\2\u0198P\3\2\2\2\u0199\u019a\7O\2\2\u019a\u019b"+
|
||||
"\7C\2\2\u019b\u019c\7V\2\2\u019c\u019d\7E\2\2\u019d\u019e\7J\2\2\u019e"+
|
||||
"R\3\2\2\2\u019f\u01a0\7P\2\2\u01a0\u01a1\7C\2\2\u01a1\u01a2\7V\2\2\u01a2"+
|
||||
"\u01a3\7W\2\2\u01a3\u01a4\7T\2\2\u01a4\u01a5\7C\2\2\u01a5\u01a6\7N\2\2"+
|
||||
"\u01a6T\3\2\2\2\u01a7\u01a8\7P\2\2\u01a8\u01a9\7Q\2\2\u01a9\u01aa\7V\2"+
|
||||
"\2\u01aaV\3\2\2\2\u01ab\u01ac\7P\2\2\u01ac\u01ad\7W\2\2\u01ad\u01ae\7"+
|
||||
"N\2\2\u01ae\u01af\7N\2\2\u01afX\3\2\2\2\u01b0\u01b1\7Q\2\2\u01b1\u01b2"+
|
||||
"\7P\2\2\u01b2Z\3\2\2\2\u01b3\u01b4\7Q\2\2\u01b4\u01b5\7R\2\2\u01b5\u01b6"+
|
||||
"\7V\2\2\u01b6\u01b7\7K\2\2\u01b7\u01b8\7O\2\2\u01b8\u01b9\7K\2\2\u01b9"+
|
||||
"\u01ba\7\\\2\2\u01ba\u01bb\7G\2\2\u01bb\u01bc\7F\2\2\u01bc\\\3\2\2\2\u01bd"+
|
||||
"\u01be\7Q\2\2\u01be\u01bf\7T\2\2\u01bf^\3\2\2\2\u01c0\u01c1\7Q\2\2\u01c1"+
|
||||
"\u01c2\7T\2\2\u01c2\u01c3\7F\2\2\u01c3\u01c4\7G\2\2\u01c4\u01c5\7T\2\2"+
|
||||
"\u01c5`\3\2\2\2\u01c6\u01c7\7Q\2\2\u01c7\u01c8\7W\2\2\u01c8\u01c9\7V\2"+
|
||||
"\2\u01c9\u01ca\7G\2\2\u01ca\u01cb\7T\2\2\u01cbb\3\2\2\2\u01cc\u01cd\7"+
|
||||
"R\2\2\u01cd\u01ce\7C\2\2\u01ce\u01cf\7T\2\2\u01cf\u01d0\7U\2\2\u01d0\u01d1"+
|
||||
"\7G\2\2\u01d1\u01d2\7F\2\2\u01d2d\3\2\2\2\u01d3\u01d4\7R\2\2\u01d4\u01d5"+
|
||||
"\7J\2\2\u01d5\u01d6\7[\2\2\u01d6\u01d7\7U\2\2\u01d7\u01d8\7K\2\2\u01d8"+
|
||||
"\u01d9\7E\2\2\u01d9\u01da\7C\2\2\u01da\u01db\7N\2\2\u01dbf\3\2\2\2\u01dc"+
|
||||
"\u01dd\7R\2\2\u01dd\u01de\7N\2\2\u01de\u01df\7C\2\2\u01df\u01e0\7P\2\2"+
|
||||
"\u01e0h\3\2\2\2\u01e1\u01e2\7S\2\2\u01e2\u01e3\7W\2\2\u01e3\u01e4\7G\2"+
|
||||
"\2\u01e4\u01e5\7T\2\2\u01e5\u01e6\7[\2\2\u01e6j\3\2\2\2\u01e7\u01e8\7"+
|
||||
"T\2\2\u01e8\u01e9\7K\2\2\u01e9\u01ea\7I\2\2\u01ea\u01eb\7J\2\2\u01eb\u01ec"+
|
||||
"\7V\2\2\u01ecl\3\2\2\2\u01ed\u01ee\7T\2\2\u01ee\u01ef\7N\2\2\u01ef\u01f0"+
|
||||
"\7K\2\2\u01f0\u01f1\7M\2\2\u01f1\u01f2\7G\2\2\u01f2n\3\2\2\2\u01f3\u01f4"+
|
||||
"\7U\2\2\u01f4\u01f5\7E\2\2\u01f5\u01f6\7J\2\2\u01f6\u01f7\7G\2\2\u01f7"+
|
||||
"\u01f8\7O\2\2\u01f8\u01f9\7C\2\2\u01f9\u01fa\7U\2\2\u01fap\3\2\2\2\u01fb"+
|
||||
"\u01fc\7U\2\2\u01fc\u01fd\7G\2\2\u01fd\u01fe\7N\2\2\u01fe\u01ff\7G\2\2"+
|
||||
"\u01ff\u0200\7E\2\2\u0200\u0201\7V\2\2\u0201r\3\2\2\2\u0202\u0203\7U\2"+
|
||||
"\2\u0203\u0204\7J\2\2\u0204\u0205\7Q\2\2\u0205\u0206\7Y\2\2\u0206t\3\2"+
|
||||
"\2\2\u0207\u0208\7V\2\2\u0208\u0209\7C\2\2\u0209\u020a\7D\2\2\u020a\u020b"+
|
||||
"\7N\2\2\u020b\u020c\7G\2\2\u020c\u020d\7U\2\2\u020dv\3\2\2\2\u020e\u020f"+
|
||||
"\7V\2\2\u020f\u0210\7G\2\2\u0210\u0211\7Z\2\2\u0211\u0212\7V\2\2\u0212"+
|
||||
"x\3\2\2\2\u0213\u0214\7V\2\2\u0214\u0215\7T\2\2\u0215\u0216\7W\2\2\u0216"+
|
||||
"\u0217\7G\2\2\u0217z\3\2\2\2\u0218\u0219\7W\2\2\u0219\u021a\7U\2\2\u021a"+
|
||||
"\u021b\7K\2\2\u021b\u021c\7P\2\2\u021c\u021d\7I\2\2\u021d|\3\2\2\2\u021e"+
|
||||
"\u021f\7X\2\2\u021f\u0220\7G\2\2\u0220\u0221\7T\2\2\u0221\u0222\7K\2\2"+
|
||||
"\u0222\u0223\7H\2\2\u0223\u0224\7[\2\2\u0224~\3\2\2\2\u0225\u0226\7Y\2"+
|
||||
"\2\u0226\u0227\7J\2\2\u0227\u0228\7G\2\2\u0228\u0229\7T\2\2\u0229\u022a"+
|
||||
"\7G\2\2\u022a\u0080\3\2\2\2\u022b\u022c\7Y\2\2\u022c\u022d\7K\2\2\u022d"+
|
||||
"\u022e\7V\2\2\u022e\u022f\7J\2\2\u022f\u0082\3\2\2\2\u0230\u0231\7?\2"+
|
||||
"\2\u0231\u0084\3\2\2\2\u0232\u0233\7>\2\2\u0233\u023a\7@\2\2\u0234\u0235"+
|
||||
"\7#\2\2\u0235\u023a\7?\2\2\u0236\u0237\7>\2\2\u0237\u0238\7?\2\2\u0238"+
|
||||
"\u023a\7@\2\2\u0239\u0232\3\2\2\2\u0239\u0234\3\2\2\2\u0239\u0236\3\2"+
|
||||
"\2\2\u023a\u0086\3\2\2\2\u023b\u023c\7>\2\2\u023c\u0088\3\2\2\2\u023d"+
|
||||
"\u023e\7>\2\2\u023e\u023f\7?\2\2\u023f\u008a\3\2\2\2\u0240\u0241\7@\2"+
|
||||
"\2\u0241\u008c\3\2\2\2\u0242\u0243\7@\2\2\u0243\u0244\7?\2\2\u0244\u008e"+
|
||||
"\3\2\2\2\u0245\u0246\7-\2\2\u0246\u0090\3\2\2\2\u0247\u0248\7/\2\2\u0248"+
|
||||
"\u0092\3\2\2\2\u0249\u024a\7,\2\2\u024a\u0094\3\2\2\2\u024b\u024c\7\61"+
|
||||
"\2\2\u024c\u0096\3\2\2\2\u024d\u024e\7\'\2\2\u024e\u0098\3\2\2\2\u024f"+
|
||||
"\u0250\7~\2\2\u0250\u0251\7~\2\2\u0251\u009a\3\2\2\2\u0252\u0258\7)\2"+
|
||||
"\2\u0253\u0257\n\2\2\2\u0254\u0255\7)\2\2\u0255\u0257\7)\2\2\u0256\u0253"+
|
||||
"\3\2\2\2\u0256\u0254\3\2\2\2\u0257\u025a\3\2\2\2\u0258\u0256\3\2\2\2\u0258"+
|
||||
"\u0259\3\2\2\2\u0259\u025b\3\2\2\2\u025a\u0258\3\2\2\2\u025b\u025c\7)"+
|
||||
"\2\2\u025c\u009c\3\2\2\2\u025d\u025f\5\u00abV\2\u025e\u025d\3\2\2\2\u025f"+
|
||||
"\u0260\3\2\2\2\u0260\u025e\3\2\2\2\u0260\u0261\3\2\2\2\u0261\u009e\3\2"+
|
||||
"\2\2\u0262\u0264\5\u00abV\2\u0263\u0262\3\2\2\2\u0264\u0265\3\2\2\2\u0265"+
|
||||
"\u0263\3\2\2\2\u0265\u0266\3\2\2\2\u0266\u0267\3\2\2\2\u0267\u026b\7\60"+
|
||||
"\2\2\u0268\u026a\5\u00abV\2\u0269\u0268\3\2\2\2\u026a\u026d\3\2\2\2\u026b"+
|
||||
"\u0269\3\2\2\2\u026b\u026c\3\2\2\2\u026c\u028d\3\2\2\2\u026d\u026b\3\2"+
|
||||
"\2\2\u026e\u0270\7\60\2\2\u026f\u0271\5\u00abV\2\u0270\u026f\3\2\2\2\u0271"+
|
||||
"\u0272\3\2\2\2\u0272\u0270\3\2\2\2\u0272\u0273\3\2\2\2\u0273\u028d\3\2"+
|
||||
"\2\2\u0274\u0276\5\u00abV\2\u0275\u0274\3\2\2\2\u0276\u0277\3\2\2\2\u0277"+
|
||||
"\u0275\3\2\2\2\u0277\u0278\3\2\2\2\u0278\u0280\3\2\2\2\u0279\u027d\7\60"+
|
||||
"\2\2\u027a\u027c\5\u00abV\2\u027b\u027a\3\2\2\2\u027c\u027f\3\2\2\2\u027d"+
|
||||
"\u027b\3\2\2\2\u027d\u027e\3\2\2\2\u027e\u0281\3\2\2\2\u027f\u027d\3\2"+
|
||||
"\2\2\u0280\u0279\3\2\2\2\u0280\u0281\3\2\2\2\u0281\u0282\3\2\2\2\u0282"+
|
||||
"\u0283\5\u00a9U\2\u0283\u028d\3\2\2\2\u0284\u0286\7\60\2\2\u0285\u0287"+
|
||||
"\5\u00abV\2\u0286\u0285\3\2\2\2\u0287\u0288\3\2\2\2\u0288\u0286\3\2\2"+
|
||||
"\2\u0288\u0289\3\2\2\2\u0289\u028a\3\2\2\2\u028a\u028b\5\u00a9U\2\u028b"+
|
||||
"\u028d\3\2\2\2\u028c\u0263\3\2\2\2\u028c\u026e\3\2\2\2\u028c\u0275\3\2"+
|
||||
"\2\2\u028c\u0284\3\2\2\2\u028d\u00a0\3\2\2\2\u028e\u0291\5\u00adW\2\u028f"+
|
||||
"\u0291\7a\2\2\u0290\u028e\3\2\2\2\u0290\u028f\3\2\2\2\u0291\u0297\3\2"+
|
||||
"\2\2\u0292\u0296\5\u00adW\2\u0293\u0296\5\u00abV\2\u0294\u0296\t\3\2\2"+
|
||||
"\u0295\u0292\3\2\2\2\u0295\u0293\3\2\2\2\u0295\u0294\3\2\2\2\u0296\u0299"+
|
||||
"\3\2\2\2\u0297\u0295\3\2\2\2\u0297\u0298\3\2\2\2\u0298\u00a2\3\2\2\2\u0299"+
|
||||
"\u0297\3\2\2\2\u029a\u029e\5\u00abV\2\u029b\u029f\5\u00adW\2\u029c\u029f"+
|
||||
"\5\u00abV\2\u029d\u029f\t\3\2\2\u029e\u029b\3\2\2\2\u029e\u029c\3\2\2"+
|
||||
"\2\u029e\u029d\3\2\2\2\u029f\u02a0\3\2\2\2\u02a0\u029e\3\2\2\2\u02a0\u02a1"+
|
||||
"\3\2\2\2\u02a1\u00a4\3\2\2\2\u02a2\u02a8\7$\2\2\u02a3\u02a7\n\4\2\2\u02a4"+
|
||||
"\u02a5\7$\2\2\u02a5\u02a7\7$\2\2\u02a6\u02a3\3\2\2\2\u02a6\u02a4\3\2\2"+
|
||||
"\2\u02a7\u02aa\3\2\2\2\u02a8\u02a6\3\2\2\2\u02a8\u02a9\3\2\2\2\u02a9\u02ab"+
|
||||
"\3\2\2\2\u02aa\u02a8\3\2\2\2\u02ab\u02ac\7$\2\2\u02ac\u00a6\3\2\2\2\u02ad"+
|
||||
"\u02b3\7b\2\2\u02ae\u02b2\n\5\2\2\u02af\u02b0\7b\2\2\u02b0\u02b2\7b\2"+
|
||||
"\2\u02b1\u02ae\3\2\2\2\u02b1\u02af\3\2\2\2\u02b2\u02b5\3\2\2\2\u02b3\u02b1"+
|
||||
"\3\2\2\2\u02b3\u02b4\3\2\2\2\u02b4\u02b6\3\2\2\2\u02b5\u02b3\3\2\2\2\u02b6"+
|
||||
"\u02b7\7b\2\2\u02b7\u00a8\3\2\2\2\u02b8\u02ba\7G\2\2\u02b9\u02bb\t\6\2"+
|
||||
"\2\u02ba\u02b9\3\2\2\2\u02ba\u02bb\3\2\2\2\u02bb\u02bd\3\2\2\2\u02bc\u02be"+
|
||||
"\5\u00abV\2\u02bd\u02bc\3\2\2\2\u02be\u02bf\3\2\2\2\u02bf\u02bd\3\2\2"+
|
||||
"\2\u02bf\u02c0\3\2\2\2\u02c0\u00aa\3\2\2\2\u02c1\u02c2\t\7\2\2\u02c2\u00ac"+
|
||||
"\3\2\2\2\u02c3\u02c4\t\b\2\2\u02c4\u00ae\3\2\2\2\u02c5\u02c6\7/\2\2\u02c6"+
|
||||
"\u02c7\7/\2\2\u02c7\u02cb\3\2\2\2\u02c8\u02ca\n\t\2\2\u02c9\u02c8\3\2"+
|
||||
"\2\2\u02ca\u02cd\3\2\2\2\u02cb\u02c9\3\2\2\2\u02cb\u02cc\3\2\2\2\u02cc"+
|
||||
"\u02cf\3\2\2\2\u02cd\u02cb\3\2\2\2\u02ce\u02d0\7\17\2\2\u02cf\u02ce\3"+
|
||||
"\2\2\2\u02cf\u02d0\3\2\2\2\u02d0\u02d2\3\2\2\2\u02d1\u02d3\7\f\2\2\u02d2"+
|
||||
"\u02d1\3\2\2\2\u02d2\u02d3\3\2\2\2\u02d3\u02d4\3\2\2\2\u02d4\u02d5\bX"+
|
||||
"\2\2\u02d5\u00b0\3\2\2\2\u02d6\u02d7\7\61\2\2\u02d7\u02d8\7,\2\2\u02d8"+
|
||||
"\u02dd\3\2\2\2\u02d9\u02dc\5\u00b1Y\2\u02da\u02dc\13\2\2\2\u02db\u02d9"+
|
||||
"\3\2\2\2\u02db\u02da\3\2\2\2\u02dc\u02df\3\2\2\2\u02dd\u02de\3\2\2\2\u02dd"+
|
||||
"\u02db\3\2\2\2\u02de\u02e0\3\2\2\2\u02df\u02dd\3\2\2\2\u02e0\u02e1\7,"+
|
||||
"\2\2\u02e1\u02e2\7\61\2\2\u02e2\u02e3\3\2\2\2\u02e3\u02e4\bY\2\2\u02e4"+
|
||||
"\u00b2\3\2\2\2\u02e5\u02e7\t\n\2\2\u02e6\u02e5\3\2\2\2\u02e7\u02e8\3\2"+
|
||||
"\2\2\u02e8\u02e6\3\2\2\2\u02e8\u02e9\3\2\2\2\u02e9\u02ea\3\2\2\2\u02ea"+
|
||||
"\u02eb\bZ\2\2\u02eb\u00b4\3\2\2\2\u02ec\u02ed\13\2\2\2\u02ed\u00b6\3\2"+
|
||||
"\2\2 \2\u0239\u0256\u0258\u0260\u0265\u026b\u0272\u0277\u027d\u0280\u0288"+
|
||||
"\u028c\u0290\u0295\u0297\u029e\u02a0\u02a6\u02a8\u02b1\u02b3\u02ba\u02bf"+
|
||||
"\u02cb\u02cf\u02d2\u02db\u02dd\u02e8\3\2\3\2";
|
||||
"\4U\tU\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\3\2\3\2\3\3\3\3\3\4"+
|
||||
"\3\4\3\5\3\5\3\5\3\5\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\7\3\7\3\7\3\7\3"+
|
||||
"\7\3\7\3\7\3\7\3\7\3\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t\3\n\3\n\3\n\3\13\3"+
|
||||
"\13\3\13\3\13\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\r\3\r\3\r\3\16\3\16\3"+
|
||||
"\16\3\16\3\16\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\20\3\20\3\20\3"+
|
||||
"\20\3\20\3\20\3\21\3\21\3\21\3\21\3\21\3\22\3\22\3\22\3\22\3\22\3\22\3"+
|
||||
"\22\3\22\3\22\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\24\3\24\3"+
|
||||
"\24\3\24\3\24\3\24\3\24\3\25\3\25\3\25\3\25\3\25\3\25\3\25\3\25\3\25\3"+
|
||||
"\25\3\25\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3\27\3\27\3\27\3\27\3\27\3"+
|
||||
"\27\3\27\3\27\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\31\3\31\3\31\3"+
|
||||
"\31\3\31\3\31\3\32\3\32\3\32\3\32\3\32\3\32\3\32\3\33\3\33\3\33\3\33\3"+
|
||||
"\33\3\34\3\34\3\34\3\34\3\34\3\35\3\35\3\35\3\35\3\35\3\35\3\35\3\35\3"+
|
||||
"\35\3\35\3\36\3\36\3\36\3\36\3\36\3\36\3\36\3\36\3\36\3\37\3\37\3\37\3"+
|
||||
"\37\3\37\3\37\3 \3 \3 \3 \3 \3 \3 \3!\3!\3!\3\"\3\"\3\"\3\"\3\"\3\"\3"+
|
||||
"#\3#\3#\3$\3$\3$\3$\3$\3%\3%\3%\3%\3%\3&\3&\3&\3&\3&\3\'\3\'\3\'\3\'\3"+
|
||||
"\'\3\'\3(\3(\3(\3(\3(\3(\3(\3)\3)\3)\3)\3)\3)\3*\3*\3*\3*\3*\3*\3*\3*"+
|
||||
"\3+\3+\3+\3+\3,\3,\3,\3,\3,\3-\3-\3-\3.\3.\3.\3.\3.\3.\3.\3.\3.\3.\3/"+
|
||||
"\3/\3/\3\60\3\60\3\60\3\60\3\60\3\60\3\61\3\61\3\61\3\61\3\61\3\61\3\62"+
|
||||
"\3\62\3\62\3\62\3\62\3\62\3\62\3\63\3\63\3\63\3\63\3\63\3\63\3\63\3\63"+
|
||||
"\3\63\3\64\3\64\3\64\3\64\3\64\3\65\3\65\3\65\3\65\3\65\3\65\3\66\3\66"+
|
||||
"\3\66\3\66\3\66\3\66\3\67\3\67\3\67\3\67\3\67\3\67\38\38\38\38\38\38\3"+
|
||||
"8\38\39\39\39\39\39\39\39\3:\3:\3:\3:\3:\3;\3;\3;\3;\3;\3;\3;\3<\3<\3"+
|
||||
"<\3<\3<\3=\3=\3=\3=\3=\3>\3>\3>\3>\3>\3>\3?\3?\3?\3?\3?\3?\3?\3@\3@\3"+
|
||||
"@\3@\3@\3@\3A\3A\3A\3A\3A\3B\3B\3C\3C\3C\3C\3C\3C\3C\5C\u0241\nC\3D\3"+
|
||||
"D\3E\3E\3E\3F\3F\3G\3G\3G\3H\3H\3I\3I\3J\3J\3K\3K\3L\3L\3M\3M\3M\3N\3"+
|
||||
"N\3O\3O\3O\3O\7O\u0260\nO\fO\16O\u0263\13O\3O\3O\3P\6P\u0268\nP\rP\16"+
|
||||
"P\u0269\3Q\6Q\u026d\nQ\rQ\16Q\u026e\3Q\3Q\7Q\u0273\nQ\fQ\16Q\u0276\13"+
|
||||
"Q\3Q\3Q\6Q\u027a\nQ\rQ\16Q\u027b\3Q\6Q\u027f\nQ\rQ\16Q\u0280\3Q\3Q\7Q"+
|
||||
"\u0285\nQ\fQ\16Q\u0288\13Q\5Q\u028a\nQ\3Q\3Q\3Q\3Q\6Q\u0290\nQ\rQ\16Q"+
|
||||
"\u0291\3Q\3Q\5Q\u0296\nQ\3R\3R\5R\u029a\nR\3R\3R\3R\7R\u029f\nR\fR\16"+
|
||||
"R\u02a2\13R\3S\3S\3S\3S\6S\u02a8\nS\rS\16S\u02a9\3T\3T\3T\3T\7T\u02b0"+
|
||||
"\nT\fT\16T\u02b3\13T\3T\3T\3U\3U\3U\3U\7U\u02bb\nU\fU\16U\u02be\13U\3"+
|
||||
"U\3U\3V\3V\5V\u02c4\nV\3V\6V\u02c7\nV\rV\16V\u02c8\3W\3W\3X\3X\3Y\3Y\3"+
|
||||
"Y\3Y\7Y\u02d3\nY\fY\16Y\u02d6\13Y\3Y\5Y\u02d9\nY\3Y\5Y\u02dc\nY\3Y\3Y"+
|
||||
"\3Z\3Z\3Z\3Z\3Z\7Z\u02e5\nZ\fZ\16Z\u02e8\13Z\3Z\3Z\3Z\3Z\3Z\3[\6[\u02f0"+
|
||||
"\n[\r[\16[\u02f1\3[\3[\3\\\3\\\3\u02e6\2]\3\3\5\4\7\5\t\6\13\7\r\b\17"+
|
||||
"\t\21\n\23\13\25\f\27\r\31\16\33\17\35\20\37\21!\22#\23%\24\'\25)\26+"+
|
||||
"\27-\30/\31\61\32\63\33\65\34\67\359\36;\37= ?!A\"C#E$G%I&K\'M(O)Q*S+"+
|
||||
"U,W-Y.[/]\60_\61a\62c\63e\64g\65i\66k\67m8o9q:s;u<w=y>{?}@\177A\u0081"+
|
||||
"B\u0083C\u0085D\u0087E\u0089F\u008bG\u008dH\u008fI\u0091J\u0093K\u0095"+
|
||||
"L\u0097M\u0099N\u009bO\u009dP\u009fQ\u00a1R\u00a3S\u00a5T\u00a7U\u00a9"+
|
||||
"V\u00ab\2\u00ad\2\u00af\2\u00b1W\u00b3X\u00b5Y\u00b7Z\3\2\13\3\2))\5\2"+
|
||||
"<<BBaa\3\2$$\3\2bb\4\2--//\3\2\62;\3\2C\\\4\2\f\f\17\17\5\2\13\f\17\17"+
|
||||
"\"\"\u0315\2\3\3\2\2\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\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\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2"+
|
||||
"\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2"+
|
||||
"\2/\3\2\2\2\2\61\3\2\2\2\2\63\3\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2"+
|
||||
"\2\2\2;\3\2\2\2\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2"+
|
||||
"\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S"+
|
||||
"\3\2\2\2\2U\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2[\3\2\2\2\2]\3\2\2\2\2_\3\2"+
|
||||
"\2\2\2a\3\2\2\2\2c\3\2\2\2\2e\3\2\2\2\2g\3\2\2\2\2i\3\2\2\2\2k\3\2\2\2"+
|
||||
"\2m\3\2\2\2\2o\3\2\2\2\2q\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2\2w\3\2\2\2\2y"+
|
||||
"\3\2\2\2\2{\3\2\2\2\2}\3\2\2\2\2\177\3\2\2\2\2\u0081\3\2\2\2\2\u0083\3"+
|
||||
"\2\2\2\2\u0085\3\2\2\2\2\u0087\3\2\2\2\2\u0089\3\2\2\2\2\u008b\3\2\2\2"+
|
||||
"\2\u008d\3\2\2\2\2\u008f\3\2\2\2\2\u0091\3\2\2\2\2\u0093\3\2\2\2\2\u0095"+
|
||||
"\3\2\2\2\2\u0097\3\2\2\2\2\u0099\3\2\2\2\2\u009b\3\2\2\2\2\u009d\3\2\2"+
|
||||
"\2\2\u009f\3\2\2\2\2\u00a1\3\2\2\2\2\u00a3\3\2\2\2\2\u00a5\3\2\2\2\2\u00a7"+
|
||||
"\3\2\2\2\2\u00a9\3\2\2\2\2\u00b1\3\2\2\2\2\u00b3\3\2\2\2\2\u00b5\3\2\2"+
|
||||
"\2\2\u00b7\3\2\2\2\3\u00b9\3\2\2\2\5\u00bb\3\2\2\2\7\u00bd\3\2\2\2\t\u00bf"+
|
||||
"\3\2\2\2\13\u00c3\3\2\2\2\r\u00cb\3\2\2\2\17\u00d4\3\2\2\2\21\u00d8\3"+
|
||||
"\2\2\2\23\u00dc\3\2\2\2\25\u00df\3\2\2\2\27\u00e3\3\2\2\2\31\u00eb\3\2"+
|
||||
"\2\2\33\u00ee\3\2\2\2\35\u00f3\3\2\2\2\37\u00fb\3\2\2\2!\u0101\3\2\2\2"+
|
||||
"#\u0106\3\2\2\2%\u010f\3\2\2\2\'\u0118\3\2\2\2)\u011f\3\2\2\2+\u012a\3"+
|
||||
"\2\2\2-\u0131\3\2\2\2/\u0139\3\2\2\2\61\u0141\3\2\2\2\63\u0147\3\2\2\2"+
|
||||
"\65\u014e\3\2\2\2\67\u0153\3\2\2\29\u0158\3\2\2\2;\u0162\3\2\2\2=\u016b"+
|
||||
"\3\2\2\2?\u0171\3\2\2\2A\u0178\3\2\2\2C\u017b\3\2\2\2E\u0181\3\2\2\2G"+
|
||||
"\u0184\3\2\2\2I\u0189\3\2\2\2K\u018e\3\2\2\2M\u0193\3\2\2\2O\u0199\3\2"+
|
||||
"\2\2Q\u01a0\3\2\2\2S\u01a6\3\2\2\2U\u01ae\3\2\2\2W\u01b2\3\2\2\2Y\u01b7"+
|
||||
"\3\2\2\2[\u01ba\3\2\2\2]\u01c4\3\2\2\2_\u01c7\3\2\2\2a\u01cd\3\2\2\2c"+
|
||||
"\u01d3\3\2\2\2e\u01da\3\2\2\2g\u01e3\3\2\2\2i\u01e8\3\2\2\2k\u01ee\3\2"+
|
||||
"\2\2m\u01f4\3\2\2\2o\u01fa\3\2\2\2q\u0202\3\2\2\2s\u0209\3\2\2\2u\u020e"+
|
||||
"\3\2\2\2w\u0215\3\2\2\2y\u021a\3\2\2\2{\u021f\3\2\2\2}\u0225\3\2\2\2\177"+
|
||||
"\u022c\3\2\2\2\u0081\u0232\3\2\2\2\u0083\u0237\3\2\2\2\u0085\u0240\3\2"+
|
||||
"\2\2\u0087\u0242\3\2\2\2\u0089\u0244\3\2\2\2\u008b\u0247\3\2\2\2\u008d"+
|
||||
"\u0249\3\2\2\2\u008f\u024c\3\2\2\2\u0091\u024e\3\2\2\2\u0093\u0250\3\2"+
|
||||
"\2\2\u0095\u0252\3\2\2\2\u0097\u0254\3\2\2\2\u0099\u0256\3\2\2\2\u009b"+
|
||||
"\u0259\3\2\2\2\u009d\u025b\3\2\2\2\u009f\u0267\3\2\2\2\u00a1\u0295\3\2"+
|
||||
"\2\2\u00a3\u0299\3\2\2\2\u00a5\u02a3\3\2\2\2\u00a7\u02ab\3\2\2\2\u00a9"+
|
||||
"\u02b6\3\2\2\2\u00ab\u02c1\3\2\2\2\u00ad\u02ca\3\2\2\2\u00af\u02cc\3\2"+
|
||||
"\2\2\u00b1\u02ce\3\2\2\2\u00b3\u02df\3\2\2\2\u00b5\u02ef\3\2\2\2\u00b7"+
|
||||
"\u02f5\3\2\2\2\u00b9\u00ba\7*\2\2\u00ba\4\3\2\2\2\u00bb\u00bc\7+\2\2\u00bc"+
|
||||
"\6\3\2\2\2\u00bd\u00be\7.\2\2\u00be\b\3\2\2\2\u00bf\u00c0\7C\2\2\u00c0"+
|
||||
"\u00c1\7N\2\2\u00c1\u00c2\7N\2\2\u00c2\n\3\2\2\2\u00c3\u00c4\7C\2\2\u00c4"+
|
||||
"\u00c5\7P\2\2\u00c5\u00c6\7C\2\2\u00c6\u00c7\7N\2\2\u00c7\u00c8\7[\2\2"+
|
||||
"\u00c8\u00c9\7\\\2\2\u00c9\u00ca\7G\2\2\u00ca\f\3\2\2\2\u00cb\u00cc\7"+
|
||||
"C\2\2\u00cc\u00cd\7P\2\2\u00cd\u00ce\7C\2\2\u00ce\u00cf\7N\2\2\u00cf\u00d0"+
|
||||
"\7[\2\2\u00d0\u00d1\7\\\2\2\u00d1\u00d2\7G\2\2\u00d2\u00d3\7F\2\2\u00d3"+
|
||||
"\16\3\2\2\2\u00d4\u00d5\7C\2\2\u00d5\u00d6\7P\2\2\u00d6\u00d7\7F\2\2\u00d7"+
|
||||
"\20\3\2\2\2\u00d8\u00d9\7C\2\2\u00d9\u00da\7P\2\2\u00da\u00db\7[\2\2\u00db"+
|
||||
"\22\3\2\2\2\u00dc\u00dd\7C\2\2\u00dd\u00de\7U\2\2\u00de\24\3\2\2\2\u00df"+
|
||||
"\u00e0\7C\2\2\u00e0\u00e1\7U\2\2\u00e1\u00e2\7E\2\2\u00e2\26\3\2\2\2\u00e3"+
|
||||
"\u00e4\7D\2\2\u00e4\u00e5\7G\2\2\u00e5\u00e6\7V\2\2\u00e6\u00e7\7Y\2\2"+
|
||||
"\u00e7\u00e8\7G\2\2\u00e8\u00e9\7G\2\2\u00e9\u00ea\7P\2\2\u00ea\30\3\2"+
|
||||
"\2\2\u00eb\u00ec\7D\2\2\u00ec\u00ed\7[\2\2\u00ed\32\3\2\2\2\u00ee\u00ef"+
|
||||
"\7E\2\2\u00ef\u00f0\7C\2\2\u00f0\u00f1\7U\2\2\u00f1\u00f2\7V\2\2\u00f2"+
|
||||
"\34\3\2\2\2\u00f3\u00f4\7E\2\2\u00f4\u00f5\7Q\2\2\u00f5\u00f6\7N\2\2\u00f6"+
|
||||
"\u00f7\7W\2\2\u00f7\u00f8\7O\2\2\u00f8\u00f9\7P\2\2\u00f9\u00fa\7U\2\2"+
|
||||
"\u00fa\36\3\2\2\2\u00fb\u00fc\7F\2\2\u00fc\u00fd\7G\2\2\u00fd\u00fe\7"+
|
||||
"D\2\2\u00fe\u00ff\7W\2\2\u00ff\u0100\7I\2\2\u0100 \3\2\2\2\u0101\u0102"+
|
||||
"\7F\2\2\u0102\u0103\7G\2\2\u0103\u0104\7U\2\2\u0104\u0105\7E\2\2\u0105"+
|
||||
"\"\3\2\2\2\u0106\u0107\7F\2\2\u0107\u0108\7G\2\2\u0108\u0109\7U\2\2\u0109"+
|
||||
"\u010a\7E\2\2\u010a\u010b\7T\2\2\u010b\u010c\7K\2\2\u010c\u010d\7D\2\2"+
|
||||
"\u010d\u010e\7G\2\2\u010e$\3\2\2\2\u010f\u0110\7F\2\2\u0110\u0111\7K\2"+
|
||||
"\2\u0111\u0112\7U\2\2\u0112\u0113\7V\2\2\u0113\u0114\7K\2\2\u0114\u0115"+
|
||||
"\7P\2\2\u0115\u0116\7E\2\2\u0116\u0117\7V\2\2\u0117&\3\2\2\2\u0118\u0119"+
|
||||
"\7G\2\2\u0119\u011a\7U\2\2\u011a\u011b\7E\2\2\u011b\u011c\7C\2\2\u011c"+
|
||||
"\u011d\7R\2\2\u011d\u011e\7G\2\2\u011e(\3\2\2\2\u011f\u0120\7G\2\2\u0120"+
|
||||
"\u0121\7Z\2\2\u0121\u0122\7G\2\2\u0122\u0123\7E\2\2\u0123\u0124\7W\2\2"+
|
||||
"\u0124\u0125\7V\2\2\u0125\u0126\7C\2\2\u0126\u0127\7D\2\2\u0127\u0128"+
|
||||
"\7N\2\2\u0128\u0129\7G\2\2\u0129*\3\2\2\2\u012a\u012b\7G\2\2\u012b\u012c"+
|
||||
"\7Z\2\2\u012c\u012d\7K\2\2\u012d\u012e\7U\2\2\u012e\u012f\7V\2\2\u012f"+
|
||||
"\u0130\7U\2\2\u0130,\3\2\2\2\u0131\u0132\7G\2\2\u0132\u0133\7Z\2\2\u0133"+
|
||||
"\u0134\7R\2\2\u0134\u0135\7N\2\2\u0135\u0136\7C\2\2\u0136\u0137\7K\2\2"+
|
||||
"\u0137\u0138\7P\2\2\u0138.\3\2\2\2\u0139\u013a\7G\2\2\u013a\u013b\7Z\2"+
|
||||
"\2\u013b\u013c\7V\2\2\u013c\u013d\7T\2\2\u013d\u013e\7C\2\2\u013e\u013f"+
|
||||
"\7E\2\2\u013f\u0140\7V\2\2\u0140\60\3\2\2\2\u0141\u0142\7H\2\2\u0142\u0143"+
|
||||
"\7C\2\2\u0143\u0144\7N\2\2\u0144\u0145\7U\2\2\u0145\u0146\7G\2\2\u0146"+
|
||||
"\62\3\2\2\2\u0147\u0148\7H\2\2\u0148\u0149\7Q\2\2\u0149\u014a\7T\2\2\u014a"+
|
||||
"\u014b\7O\2\2\u014b\u014c\7C\2\2\u014c\u014d\7V\2\2\u014d\64\3\2\2\2\u014e"+
|
||||
"\u014f\7H\2\2\u014f\u0150\7T\2\2\u0150\u0151\7Q\2\2\u0151\u0152\7O\2\2"+
|
||||
"\u0152\66\3\2\2\2\u0153\u0154\7H\2\2\u0154\u0155\7W\2\2\u0155\u0156\7"+
|
||||
"N\2\2\u0156\u0157\7N\2\2\u01578\3\2\2\2\u0158\u0159\7H\2\2\u0159\u015a"+
|
||||
"\7W\2\2\u015a\u015b\7P\2\2\u015b\u015c\7E\2\2\u015c\u015d\7V\2\2\u015d"+
|
||||
"\u015e\7K\2\2\u015e\u015f\7Q\2\2\u015f\u0160\7P\2\2\u0160\u0161\7U\2\2"+
|
||||
"\u0161:\3\2\2\2\u0162\u0163\7I\2\2\u0163\u0164\7T\2\2\u0164\u0165\7C\2"+
|
||||
"\2\u0165\u0166\7R\2\2\u0166\u0167\7J\2\2\u0167\u0168\7X\2\2\u0168\u0169"+
|
||||
"\7K\2\2\u0169\u016a\7\\\2\2\u016a<\3\2\2\2\u016b\u016c\7I\2\2\u016c\u016d"+
|
||||
"\7T\2\2\u016d\u016e\7Q\2\2\u016e\u016f\7W\2\2\u016f\u0170\7R\2\2\u0170"+
|
||||
">\3\2\2\2\u0171\u0172\7J\2\2\u0172\u0173\7C\2\2\u0173\u0174\7X\2\2\u0174"+
|
||||
"\u0175\7K\2\2\u0175\u0176\7P\2\2\u0176\u0177\7I\2\2\u0177@\3\2\2\2\u0178"+
|
||||
"\u0179\7K\2\2\u0179\u017a\7P\2\2\u017aB\3\2\2\2\u017b\u017c\7K\2\2\u017c"+
|
||||
"\u017d\7P\2\2\u017d\u017e\7P\2\2\u017e\u017f\7G\2\2\u017f\u0180\7T\2\2"+
|
||||
"\u0180D\3\2\2\2\u0181\u0182\7K\2\2\u0182\u0183\7U\2\2\u0183F\3\2\2\2\u0184"+
|
||||
"\u0185\7L\2\2\u0185\u0186\7Q\2\2\u0186\u0187\7K\2\2\u0187\u0188\7P\2\2"+
|
||||
"\u0188H\3\2\2\2\u0189\u018a\7N\2\2\u018a\u018b\7G\2\2\u018b\u018c\7H\2"+
|
||||
"\2\u018c\u018d\7V\2\2\u018dJ\3\2\2\2\u018e\u018f\7N\2\2\u018f\u0190\7"+
|
||||
"K\2\2\u0190\u0191\7M\2\2\u0191\u0192\7G\2\2\u0192L\3\2\2\2\u0193\u0194"+
|
||||
"\7N\2\2\u0194\u0195\7K\2\2\u0195\u0196\7O\2\2\u0196\u0197\7K\2\2\u0197"+
|
||||
"\u0198\7V\2\2\u0198N\3\2\2\2\u0199\u019a\7O\2\2\u019a\u019b\7C\2\2\u019b"+
|
||||
"\u019c\7R\2\2\u019c\u019d\7R\2\2\u019d\u019e\7G\2\2\u019e\u019f\7F\2\2"+
|
||||
"\u019fP\3\2\2\2\u01a0\u01a1\7O\2\2\u01a1\u01a2\7C\2\2\u01a2\u01a3\7V\2"+
|
||||
"\2\u01a3\u01a4\7E\2\2\u01a4\u01a5\7J\2\2\u01a5R\3\2\2\2\u01a6\u01a7\7"+
|
||||
"P\2\2\u01a7\u01a8\7C\2\2\u01a8\u01a9\7V\2\2\u01a9\u01aa\7W\2\2\u01aa\u01ab"+
|
||||
"\7T\2\2\u01ab\u01ac\7C\2\2\u01ac\u01ad\7N\2\2\u01adT\3\2\2\2\u01ae\u01af"+
|
||||
"\7P\2\2\u01af\u01b0\7Q\2\2\u01b0\u01b1\7V\2\2\u01b1V\3\2\2\2\u01b2\u01b3"+
|
||||
"\7P\2\2\u01b3\u01b4\7W\2\2\u01b4\u01b5\7N\2\2\u01b5\u01b6\7N\2\2\u01b6"+
|
||||
"X\3\2\2\2\u01b7\u01b8\7Q\2\2\u01b8\u01b9\7P\2\2\u01b9Z\3\2\2\2\u01ba\u01bb"+
|
||||
"\7Q\2\2\u01bb\u01bc\7R\2\2\u01bc\u01bd\7V\2\2\u01bd\u01be\7K\2\2\u01be"+
|
||||
"\u01bf\7O\2\2\u01bf\u01c0\7K\2\2\u01c0\u01c1\7\\\2\2\u01c1\u01c2\7G\2"+
|
||||
"\2\u01c2\u01c3\7F\2\2\u01c3\\\3\2\2\2\u01c4\u01c5\7Q\2\2\u01c5\u01c6\7"+
|
||||
"T\2\2\u01c6^\3\2\2\2\u01c7\u01c8\7Q\2\2\u01c8\u01c9\7T\2\2\u01c9\u01ca"+
|
||||
"\7F\2\2\u01ca\u01cb\7G\2\2\u01cb\u01cc\7T\2\2\u01cc`\3\2\2\2\u01cd\u01ce"+
|
||||
"\7Q\2\2\u01ce\u01cf\7W\2\2\u01cf\u01d0\7V\2\2\u01d0\u01d1\7G\2\2\u01d1"+
|
||||
"\u01d2\7T\2\2\u01d2b\3\2\2\2\u01d3\u01d4\7R\2\2\u01d4\u01d5\7C\2\2\u01d5"+
|
||||
"\u01d6\7T\2\2\u01d6\u01d7\7U\2\2\u01d7\u01d8\7G\2\2\u01d8\u01d9\7F\2\2"+
|
||||
"\u01d9d\3\2\2\2\u01da\u01db\7R\2\2\u01db\u01dc\7J\2\2\u01dc\u01dd\7[\2"+
|
||||
"\2\u01dd\u01de\7U\2\2\u01de\u01df\7K\2\2\u01df\u01e0\7E\2\2\u01e0\u01e1"+
|
||||
"\7C\2\2\u01e1\u01e2\7N\2\2\u01e2f\3\2\2\2\u01e3\u01e4\7R\2\2\u01e4\u01e5"+
|
||||
"\7N\2\2\u01e5\u01e6\7C\2\2\u01e6\u01e7\7P\2\2\u01e7h\3\2\2\2\u01e8\u01e9"+
|
||||
"\7S\2\2\u01e9\u01ea\7W\2\2\u01ea\u01eb\7G\2\2\u01eb\u01ec\7T\2\2\u01ec"+
|
||||
"\u01ed\7[\2\2\u01edj\3\2\2\2\u01ee\u01ef\7T\2\2\u01ef\u01f0\7K\2\2\u01f0"+
|
||||
"\u01f1\7I\2\2\u01f1\u01f2\7J\2\2\u01f2\u01f3\7V\2\2\u01f3l\3\2\2\2\u01f4"+
|
||||
"\u01f5\7T\2\2\u01f5\u01f6\7N\2\2\u01f6\u01f7\7K\2\2\u01f7\u01f8\7M\2\2"+
|
||||
"\u01f8\u01f9\7G\2\2\u01f9n\3\2\2\2\u01fa\u01fb\7U\2\2\u01fb\u01fc\7E\2"+
|
||||
"\2\u01fc\u01fd\7J\2\2\u01fd\u01fe\7G\2\2\u01fe\u01ff\7O\2\2\u01ff\u0200"+
|
||||
"\7C\2\2\u0200\u0201\7U\2\2\u0201p\3\2\2\2\u0202\u0203\7U\2\2\u0203\u0204"+
|
||||
"\7G\2\2\u0204\u0205\7N\2\2\u0205\u0206\7G\2\2\u0206\u0207\7E\2\2\u0207"+
|
||||
"\u0208\7V\2\2\u0208r\3\2\2\2\u0209\u020a\7U\2\2\u020a\u020b\7J\2\2\u020b"+
|
||||
"\u020c\7Q\2\2\u020c\u020d\7Y\2\2\u020dt\3\2\2\2\u020e\u020f\7V\2\2\u020f"+
|
||||
"\u0210\7C\2\2\u0210\u0211\7D\2\2\u0211\u0212\7N\2\2\u0212\u0213\7G\2\2"+
|
||||
"\u0213\u0214\7U\2\2\u0214v\3\2\2\2\u0215\u0216\7V\2\2\u0216\u0217\7G\2"+
|
||||
"\2\u0217\u0218\7Z\2\2\u0218\u0219\7V\2\2\u0219x\3\2\2\2\u021a\u021b\7"+
|
||||
"V\2\2\u021b\u021c\7T\2\2\u021c\u021d\7W\2\2\u021d\u021e\7G\2\2\u021ez"+
|
||||
"\3\2\2\2\u021f\u0220\7W\2\2\u0220\u0221\7U\2\2\u0221\u0222\7K\2\2\u0222"+
|
||||
"\u0223\7P\2\2\u0223\u0224\7I\2\2\u0224|\3\2\2\2\u0225\u0226\7X\2\2\u0226"+
|
||||
"\u0227\7G\2\2\u0227\u0228\7T\2\2\u0228\u0229\7K\2\2\u0229\u022a\7H\2\2"+
|
||||
"\u022a\u022b\7[\2\2\u022b~\3\2\2\2\u022c\u022d\7Y\2\2\u022d\u022e\7J\2"+
|
||||
"\2\u022e\u022f\7G\2\2\u022f\u0230\7T\2\2\u0230\u0231\7G\2\2\u0231\u0080"+
|
||||
"\3\2\2\2\u0232\u0233\7Y\2\2\u0233\u0234\7K\2\2\u0234\u0235\7V\2\2\u0235"+
|
||||
"\u0236\7J\2\2\u0236\u0082\3\2\2\2\u0237\u0238\7?\2\2\u0238\u0084\3\2\2"+
|
||||
"\2\u0239\u023a\7>\2\2\u023a\u0241\7@\2\2\u023b\u023c\7#\2\2\u023c\u0241"+
|
||||
"\7?\2\2\u023d\u023e\7>\2\2\u023e\u023f\7?\2\2\u023f\u0241\7@\2\2\u0240"+
|
||||
"\u0239\3\2\2\2\u0240\u023b\3\2\2\2\u0240\u023d\3\2\2\2\u0241\u0086\3\2"+
|
||||
"\2\2\u0242\u0243\7>\2\2\u0243\u0088\3\2\2\2\u0244\u0245\7>\2\2\u0245\u0246"+
|
||||
"\7?\2\2\u0246\u008a\3\2\2\2\u0247\u0248\7@\2\2\u0248\u008c\3\2\2\2\u0249"+
|
||||
"\u024a\7@\2\2\u024a\u024b\7?\2\2\u024b\u008e\3\2\2\2\u024c\u024d\7-\2"+
|
||||
"\2\u024d\u0090\3\2\2\2\u024e\u024f\7/\2\2\u024f\u0092\3\2\2\2\u0250\u0251"+
|
||||
"\7,\2\2\u0251\u0094\3\2\2\2\u0252\u0253\7\61\2\2\u0253\u0096\3\2\2\2\u0254"+
|
||||
"\u0255\7\'\2\2\u0255\u0098\3\2\2\2\u0256\u0257\7~\2\2\u0257\u0258\7~\2"+
|
||||
"\2\u0258\u009a\3\2\2\2\u0259\u025a\7\60\2\2\u025a\u009c\3\2\2\2\u025b"+
|
||||
"\u0261\7)\2\2\u025c\u0260\n\2\2\2\u025d\u025e\7)\2\2\u025e\u0260\7)\2"+
|
||||
"\2\u025f\u025c\3\2\2\2\u025f\u025d\3\2\2\2\u0260\u0263\3\2\2\2\u0261\u025f"+
|
||||
"\3\2\2\2\u0261\u0262\3\2\2\2\u0262\u0264\3\2\2\2\u0263\u0261\3\2\2\2\u0264"+
|
||||
"\u0265\7)\2\2\u0265\u009e\3\2\2\2\u0266\u0268\5\u00adW\2\u0267\u0266\3"+
|
||||
"\2\2\2\u0268\u0269\3\2\2\2\u0269\u0267\3\2\2\2\u0269\u026a\3\2\2\2\u026a"+
|
||||
"\u00a0\3\2\2\2\u026b\u026d\5\u00adW\2\u026c\u026b\3\2\2\2\u026d\u026e"+
|
||||
"\3\2\2\2\u026e\u026c\3\2\2\2\u026e\u026f\3\2\2\2\u026f\u0270\3\2\2\2\u0270"+
|
||||
"\u0274\5\u009bN\2\u0271\u0273\5\u00adW\2\u0272\u0271\3\2\2\2\u0273\u0276"+
|
||||
"\3\2\2\2\u0274\u0272\3\2\2\2\u0274\u0275\3\2\2\2\u0275\u0296\3\2\2\2\u0276"+
|
||||
"\u0274\3\2\2\2\u0277\u0279\5\u009bN\2\u0278\u027a\5\u00adW\2\u0279\u0278"+
|
||||
"\3\2\2\2\u027a\u027b\3\2\2\2\u027b\u0279\3\2\2\2\u027b\u027c\3\2\2\2\u027c"+
|
||||
"\u0296\3\2\2\2\u027d\u027f\5\u00adW\2\u027e\u027d\3\2\2\2\u027f\u0280"+
|
||||
"\3\2\2\2\u0280\u027e\3\2\2\2\u0280\u0281\3\2\2\2\u0281\u0289\3\2\2\2\u0282"+
|
||||
"\u0286\5\u009bN\2\u0283\u0285\5\u00adW\2\u0284\u0283\3\2\2\2\u0285\u0288"+
|
||||
"\3\2\2\2\u0286\u0284\3\2\2\2\u0286\u0287\3\2\2\2\u0287\u028a\3\2\2\2\u0288"+
|
||||
"\u0286\3\2\2\2\u0289\u0282\3\2\2\2\u0289\u028a\3\2\2\2\u028a\u028b\3\2"+
|
||||
"\2\2\u028b\u028c\5\u00abV\2\u028c\u0296\3\2\2\2\u028d\u028f\5\u009bN\2"+
|
||||
"\u028e\u0290\5\u00adW\2\u028f\u028e\3\2\2\2\u0290\u0291\3\2\2\2\u0291"+
|
||||
"\u028f\3\2\2\2\u0291\u0292\3\2\2\2\u0292\u0293\3\2\2\2\u0293\u0294\5\u00ab"+
|
||||
"V\2\u0294\u0296\3\2\2\2\u0295\u026c\3\2\2\2\u0295\u0277\3\2\2\2\u0295"+
|
||||
"\u027e\3\2\2\2\u0295\u028d\3\2\2\2\u0296\u00a2\3\2\2\2\u0297\u029a\5\u00af"+
|
||||
"X\2\u0298\u029a\7a\2\2\u0299\u0297\3\2\2\2\u0299\u0298\3\2\2\2\u029a\u02a0"+
|
||||
"\3\2\2\2\u029b\u029f\5\u00afX\2\u029c\u029f\5\u00adW\2\u029d\u029f\t\3"+
|
||||
"\2\2\u029e\u029b\3\2\2\2\u029e\u029c\3\2\2\2\u029e\u029d\3\2\2\2\u029f"+
|
||||
"\u02a2\3\2\2\2\u02a0\u029e\3\2\2\2\u02a0\u02a1\3\2\2\2\u02a1\u00a4\3\2"+
|
||||
"\2\2\u02a2\u02a0\3\2\2\2\u02a3\u02a7\5\u00adW\2\u02a4\u02a8\5\u00afX\2"+
|
||||
"\u02a5\u02a8\5\u00adW\2\u02a6\u02a8\t\3\2\2\u02a7\u02a4\3\2\2\2\u02a7"+
|
||||
"\u02a5\3\2\2\2\u02a7\u02a6\3\2\2\2\u02a8\u02a9\3\2\2\2\u02a9\u02a7\3\2"+
|
||||
"\2\2\u02a9\u02aa\3\2\2\2\u02aa\u00a6\3\2\2\2\u02ab\u02b1\7$\2\2\u02ac"+
|
||||
"\u02b0\n\4\2\2\u02ad\u02ae\7$\2\2\u02ae\u02b0\7$\2\2\u02af\u02ac\3\2\2"+
|
||||
"\2\u02af\u02ad\3\2\2\2\u02b0\u02b3\3\2\2\2\u02b1\u02af\3\2\2\2\u02b1\u02b2"+
|
||||
"\3\2\2\2\u02b2\u02b4\3\2\2\2\u02b3\u02b1\3\2\2\2\u02b4\u02b5\7$\2\2\u02b5"+
|
||||
"\u00a8\3\2\2\2\u02b6\u02bc\7b\2\2\u02b7\u02bb\n\5\2\2\u02b8\u02b9\7b\2"+
|
||||
"\2\u02b9\u02bb\7b\2\2\u02ba\u02b7\3\2\2\2\u02ba\u02b8\3\2\2\2\u02bb\u02be"+
|
||||
"\3\2\2\2\u02bc\u02ba\3\2\2\2\u02bc\u02bd\3\2\2\2\u02bd\u02bf\3\2\2\2\u02be"+
|
||||
"\u02bc\3\2\2\2\u02bf\u02c0\7b\2\2\u02c0\u00aa\3\2\2\2\u02c1\u02c3\7G\2"+
|
||||
"\2\u02c2\u02c4\t\6\2\2\u02c3\u02c2\3\2\2\2\u02c3\u02c4\3\2\2\2\u02c4\u02c6"+
|
||||
"\3\2\2\2\u02c5\u02c7\5\u00adW\2\u02c6\u02c5\3\2\2\2\u02c7\u02c8\3\2\2"+
|
||||
"\2\u02c8\u02c6\3\2\2\2\u02c8\u02c9\3\2\2\2\u02c9\u00ac\3\2\2\2\u02ca\u02cb"+
|
||||
"\t\7\2\2\u02cb\u00ae\3\2\2\2\u02cc\u02cd\t\b\2\2\u02cd\u00b0\3\2\2\2\u02ce"+
|
||||
"\u02cf\7/\2\2\u02cf\u02d0\7/\2\2\u02d0\u02d4\3\2\2\2\u02d1\u02d3\n\t\2"+
|
||||
"\2\u02d2\u02d1\3\2\2\2\u02d3\u02d6\3\2\2\2\u02d4\u02d2\3\2\2\2\u02d4\u02d5"+
|
||||
"\3\2\2\2\u02d5\u02d8\3\2\2\2\u02d6\u02d4\3\2\2\2\u02d7\u02d9\7\17\2\2"+
|
||||
"\u02d8\u02d7\3\2\2\2\u02d8\u02d9\3\2\2\2\u02d9\u02db\3\2\2\2\u02da\u02dc"+
|
||||
"\7\f\2\2\u02db\u02da\3\2\2\2\u02db\u02dc\3\2\2\2\u02dc\u02dd\3\2\2\2\u02dd"+
|
||||
"\u02de\bY\2\2\u02de\u00b2\3\2\2\2\u02df\u02e0\7\61\2\2\u02e0\u02e1\7,"+
|
||||
"\2\2\u02e1\u02e6\3\2\2\2\u02e2\u02e5\5\u00b3Z\2\u02e3\u02e5\13\2\2\2\u02e4"+
|
||||
"\u02e2\3\2\2\2\u02e4\u02e3\3\2\2\2\u02e5\u02e8\3\2\2\2\u02e6\u02e7\3\2"+
|
||||
"\2\2\u02e6\u02e4\3\2\2\2\u02e7\u02e9\3\2\2\2\u02e8\u02e6\3\2\2\2\u02e9"+
|
||||
"\u02ea\7,\2\2\u02ea\u02eb\7\61\2\2\u02eb\u02ec\3\2\2\2\u02ec\u02ed\bZ"+
|
||||
"\2\2\u02ed\u00b4\3\2\2\2\u02ee\u02f0\t\n\2\2\u02ef\u02ee\3\2\2\2\u02f0"+
|
||||
"\u02f1\3\2\2\2\u02f1\u02ef\3\2\2\2\u02f1\u02f2\3\2\2\2\u02f2\u02f3\3\2"+
|
||||
"\2\2\u02f3\u02f4\b[\2\2\u02f4\u00b6\3\2\2\2\u02f5\u02f6\13\2\2\2\u02f6"+
|
||||
"\u00b8\3\2\2\2 \2\u0240\u025f\u0261\u0269\u026e\u0274\u027b\u0280\u0286"+
|
||||
"\u0289\u0291\u0295\u0299\u029e\u02a0\u02a7\u02a9\u02af\u02b1\u02ba\u02bc"+
|
||||
"\u02c3\u02c8\u02d4\u02d8\u02db\u02e4\u02e6\u02f1\3\2\3\2";
|
||||
public static final ATN _ATN =
|
||||
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
|
||||
static {
|
||||
|
@ -444,6 +444,16 @@ interface SqlBaseListener extends ParseTreeListener {
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
void exitPredicate(SqlBaseParser.PredicateContext ctx);
|
||||
/**
|
||||
* Enter a parse tree produced by {@link SqlBaseParser#pattern}.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
void enterPattern(SqlBaseParser.PatternContext ctx);
|
||||
/**
|
||||
* Exit a parse tree produced by {@link SqlBaseParser#pattern}.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
void exitPattern(SqlBaseParser.PatternContext ctx);
|
||||
/**
|
||||
* Enter a parse tree produced by the {@code valueExpressionDefault}
|
||||
* labeled alternative in {@link SqlBaseParser#valueExpression}.
|
||||
@ -612,18 +622,6 @@ interface SqlBaseListener extends ParseTreeListener {
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
void exitNullLiteral(SqlBaseParser.NullLiteralContext ctx);
|
||||
/**
|
||||
* Enter a parse tree produced by the {@code typeConstructor}
|
||||
* labeled alternative in {@link SqlBaseParser#constant}.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
void enterTypeConstructor(SqlBaseParser.TypeConstructorContext ctx);
|
||||
/**
|
||||
* Exit a parse tree produced by the {@code typeConstructor}
|
||||
* labeled alternative in {@link SqlBaseParser#constant}.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
void exitTypeConstructor(SqlBaseParser.TypeConstructorContext ctx);
|
||||
/**
|
||||
* Enter a parse tree produced by the {@code numericLiteral}
|
||||
* labeled alternative in {@link SqlBaseParser#constant}.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -270,6 +270,12 @@ interface SqlBaseVisitor<T> extends ParseTreeVisitor<T> {
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitPredicate(SqlBaseParser.PredicateContext ctx);
|
||||
/**
|
||||
* Visit a parse tree produced by {@link SqlBaseParser#pattern}.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitPattern(SqlBaseParser.PatternContext ctx);
|
||||
/**
|
||||
* Visit a parse tree produced by the {@code valueExpressionDefault}
|
||||
* labeled alternative in {@link SqlBaseParser#valueExpression}.
|
||||
@ -368,13 +374,6 @@ interface SqlBaseVisitor<T> extends ParseTreeVisitor<T> {
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitNullLiteral(SqlBaseParser.NullLiteralContext ctx);
|
||||
/**
|
||||
* Visit a parse tree produced by the {@code typeConstructor}
|
||||
* labeled alternative in {@link SqlBaseParser#constant}.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitTypeConstructor(SqlBaseParser.TypeConstructorContext ctx);
|
||||
/**
|
||||
* Visit a parse tree produced by the {@code numericLiteral}
|
||||
* labeled alternative in {@link SqlBaseParser#constant}.
|
||||
|
@ -8,11 +8,15 @@ package org.elasticsearch.xpack.sql.parser;
|
||||
import org.antlr.v4.runtime.BaseErrorListener;
|
||||
import org.antlr.v4.runtime.CommonToken;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.DiagnosticErrorListener;
|
||||
import org.antlr.v4.runtime.Parser;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.atn.ATNConfigSet;
|
||||
import org.antlr.v4.runtime.atn.PredictionMode;
|
||||
import org.antlr.v4.runtime.dfa.DFA;
|
||||
import org.antlr.v4.runtime.misc.Pair;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@ -22,6 +26,7 @@ import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
@ -65,9 +70,11 @@ public class SqlParser {
|
||||
SqlBaseParser parser = new SqlBaseParser(tokenStream);
|
||||
|
||||
parser.addParseListener(new PostProcessor(Arrays.asList(parser.getRuleNames())));
|
||||
|
||||
parser.removeErrorListeners();
|
||||
parser.addErrorListener(ERROR_LISTENER);
|
||||
|
||||
//debug(parser);
|
||||
ParserRuleContext tree;
|
||||
try {
|
||||
// first, try parsing with potentially faster SLL mode
|
||||
@ -87,6 +94,20 @@ public class SqlParser {
|
||||
return visitor.apply(new AstBuilder(timeZone), tree);
|
||||
}
|
||||
|
||||
private void debug(SqlBaseParser parser) {
|
||||
parser.addParseListener(parser.new TraceListener());
|
||||
|
||||
parser.addErrorListener(new DiagnosticErrorListener() {
|
||||
@Override
|
||||
public void reportAttemptingFullContext(Parser recognizer, DFA dfa,
|
||||
int startIndex, int stopIndex, BitSet conflictingAlts, ATNConfigSet configs) {}
|
||||
|
||||
@Override
|
||||
public void reportContextSensitivity(Parser recognizer, DFA dfa,
|
||||
int startIndex, int stopIndex, int prediction, ATNConfigSet configs) {}
|
||||
});
|
||||
}
|
||||
|
||||
protected void postProcess(SqlBaseLexer lexer, SqlBaseParser parser, ParserRuleContext tree) {
|
||||
// no-op
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ public class ShowColumns extends Command {
|
||||
|
||||
@Override
|
||||
public void execute(SqlSession session, ActionListener<SchemaRowSet> listener) {
|
||||
session.indexResolver().asIndex(index, ActionListener.wrap(
|
||||
session.indexResolver().resolveWithSameMapping(index, null, ActionListener.wrap(
|
||||
indexResult -> {
|
||||
List<List<?>> rows = emptyList();
|
||||
if (indexResult.isValid()) {
|
||||
|
@ -10,6 +10,7 @@ import org.elasticsearch.xpack.sql.expression.Attribute;
|
||||
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
|
||||
import org.elasticsearch.xpack.sql.expression.function.FunctionDefinition;
|
||||
import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry;
|
||||
import org.elasticsearch.xpack.sql.expression.regex.LikePattern;
|
||||
import org.elasticsearch.xpack.sql.session.Rows;
|
||||
import org.elasticsearch.xpack.sql.session.SchemaRowSet;
|
||||
import org.elasticsearch.xpack.sql.session.SqlSession;
|
||||
@ -25,27 +26,27 @@ import static java.util.stream.Collectors.toList;
|
||||
|
||||
public class ShowFunctions extends Command {
|
||||
|
||||
private final String pattern;
|
||||
private final LikePattern pattern;
|
||||
|
||||
public ShowFunctions(Location location, String pattern) {
|
||||
public ShowFunctions(Location location, LikePattern pattern) {
|
||||
super(location);
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
public String pattern() {
|
||||
public LikePattern pattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Attribute> output() {
|
||||
return asList(new FieldAttribute(location(), "name", DataTypes.KEYWORD),
|
||||
return asList(new FieldAttribute(location(), "name", DataTypes.KEYWORD),
|
||||
new FieldAttribute(location(), "type", DataTypes.KEYWORD));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(SqlSession session, ActionListener<SchemaRowSet> listener) {
|
||||
FunctionRegistry registry = session.functionRegistry();
|
||||
Collection<FunctionDefinition> functions = registry.listFunctions(pattern);
|
||||
Collection<FunctionDefinition> functions = registry.listFunctions(pattern != null ? pattern.pattern() : null);
|
||||
|
||||
listener.onResponse(Rows.of(output(), functions.stream()
|
||||
.map(f -> asList(f.name(), f.type().name()))
|
||||
|
@ -6,49 +6,47 @@
|
||||
package org.elasticsearch.xpack.sql.plan.logical.command;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.xpack.sql.expression.Attribute;
|
||||
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
|
||||
import org.elasticsearch.xpack.sql.expression.regex.LikePattern;
|
||||
import org.elasticsearch.xpack.sql.session.Rows;
|
||||
import org.elasticsearch.xpack.sql.session.SchemaRowSet;
|
||||
import org.elasticsearch.xpack.sql.session.SqlSession;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.type.DataTypes;
|
||||
import org.elasticsearch.xpack.sql.util.StringUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
public class ShowTables extends Command {
|
||||
|
||||
@Nullable
|
||||
private final String pattern;
|
||||
private final LikePattern pattern;
|
||||
|
||||
public ShowTables(Location location, @Nullable String pattern) {
|
||||
public ShowTables(Location location, LikePattern pattern) {
|
||||
super(location);
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
public String pattern() {
|
||||
public LikePattern pattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Attribute> output() {
|
||||
return Collections.singletonList(new FieldAttribute(location(), "table", DataTypes.KEYWORD));
|
||||
return asList(new FieldAttribute(location(), "name", DataTypes.KEYWORD),
|
||||
new FieldAttribute(location(), "type", DataTypes.KEYWORD));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void execute(SqlSession session, ActionListener<SchemaRowSet> listener) {
|
||||
String pattern = Strings.hasText(this.pattern) ? StringUtils.jdbcToEsPattern(this.pattern) : "*";
|
||||
session.indexResolver().asList(pattern, ActionListener.wrap(result -> {
|
||||
String index = pattern != null ? pattern.asIndexNameWildcard() : "*";
|
||||
String regex = pattern != null ? pattern.asJavaRegex() : null;
|
||||
session.indexResolver().resolveNames(index, regex, ActionListener.wrap(result -> {
|
||||
listener.onResponse(Rows.of(output(), result.stream()
|
||||
.map(t -> singletonList(t.name()))
|
||||
.map(t -> asList(t.name(), t.type()))
|
||||
.collect(toList())));
|
||||
}, listener::onFailure));
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MatchQueryPredi
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MultiMatchQueryPredicate;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.StringQueryPredicate;
|
||||
import org.elasticsearch.xpack.sql.expression.regex.Like;
|
||||
import org.elasticsearch.xpack.sql.expression.regex.LikePattern;
|
||||
import org.elasticsearch.xpack.sql.expression.regex.RLike;
|
||||
import org.elasticsearch.xpack.sql.querydsl.agg.AggFilter;
|
||||
import org.elasticsearch.xpack.sql.querydsl.agg.AggPath;
|
||||
@ -439,31 +440,28 @@ abstract class QueryTranslator {
|
||||
target = nameOf(inexact ? fa : fa.exactAttribute());
|
||||
}
|
||||
|
||||
String pattern = sqlToEsPatternMatching(stringValueOf(e.right()));
|
||||
if (e instanceof Like) {
|
||||
LikePattern p = ((Like) e).right();
|
||||
if (inexact) {
|
||||
q = new QueryStringQuery(e.location(), pattern, target);
|
||||
q = new QueryStringQuery(e.location(), p.asLuceneWildcard(), target);
|
||||
}
|
||||
else {
|
||||
q = new WildcardQuery(e.location(), nameOf(e.left()), pattern);
|
||||
q = new WildcardQuery(e.location(), nameOf(e.left()), p.asLuceneWildcard());
|
||||
}
|
||||
}
|
||||
|
||||
if (e instanceof RLike) {
|
||||
String pattern = stringValueOf(e.right());
|
||||
if (inexact) {
|
||||
q = new QueryStringQuery(e.location(), "/" + pattern + "/", target);
|
||||
}
|
||||
else {
|
||||
q = new RegexQuery(e.location(), nameOf(e.left()), sqlToEsPatternMatching(stringValueOf(e.right())));
|
||||
q = new RegexQuery(e.location(), nameOf(e.left()), pattern);
|
||||
}
|
||||
}
|
||||
|
||||
return q != null ? new QueryTranslation(wrapIfNested(q, e.left())) : null;
|
||||
}
|
||||
|
||||
private static String sqlToEsPatternMatching(String pattern) {
|
||||
return pattern.replace("%", "*").replace("_", "?");
|
||||
}
|
||||
}
|
||||
|
||||
static class StringQueries extends ExpressionTranslator<StringQueryPredicate> {
|
||||
|
@ -9,13 +9,13 @@ import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.main.MainAction;
|
||||
import org.elasticsearch.action.main.MainRequest;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.rest.RestChannel;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.EsIndex;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.IndexResolver;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.IndexResolver.IndexInfo;
|
||||
import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo;
|
||||
import org.elasticsearch.xpack.sql.jdbc.net.protocol.InfoResponse;
|
||||
import org.elasticsearch.xpack.sql.jdbc.net.protocol.MetaColumnInfo;
|
||||
@ -102,17 +102,22 @@ public class RestSqlJdbcAction extends AbstractSqlProtocolRestAction {
|
||||
}
|
||||
|
||||
private Consumer<RestChannel> metaTable(MetaTableRequest request) {
|
||||
String indexPattern = hasText(request.pattern()) ? StringUtils.jdbcToEsPattern(request.pattern()) : "*";
|
||||
return channel -> indexResolver.asList(indexPattern, toActionListener(channel, list ->
|
||||
String indexPattern = hasText(request.pattern()) ? StringUtils.likeToIndexWildcard(request.pattern(), (char) 0) : "*";
|
||||
String regexPattern = hasText(request.pattern()) ? StringUtils.likeToJavaPattern(request.pattern(), (char) 0) : "*";
|
||||
|
||||
return channel -> indexResolver.resolveNames(indexPattern, regexPattern, toActionListener(channel, list ->
|
||||
new MetaTableResponse(list.stream()
|
||||
.map(EsIndex::name)
|
||||
.map(IndexInfo::name)
|
||||
.collect(toList()))));
|
||||
}
|
||||
|
||||
private Consumer<RestChannel> metaColumn(MetaColumnRequest request) {
|
||||
String indexPattern = Strings.hasText(request.tablePattern()) ? StringUtils.jdbcToEsPattern(request.tablePattern()) : "*";
|
||||
Pattern columnMatcher = hasText(request.columnPattern()) ? StringUtils.likeRegex(request.columnPattern()) : null;
|
||||
return channel -> indexResolver.asList(indexPattern, toActionListener(channel, esIndices -> {
|
||||
String indexPattern = hasText(request.tablePattern()) ? StringUtils.likeToIndexWildcard(request.tablePattern(), (char) 0) : "*";
|
||||
String regexPattern = hasText(request.tablePattern()) ? StringUtils.likeToJavaPattern(request.tablePattern(), (char) 0) : "*";
|
||||
|
||||
Pattern columnMatcher = hasText(request.columnPattern()) ? Pattern.compile(
|
||||
StringUtils.likeToJavaPattern(request.columnPattern(), (char) 0)) : null;
|
||||
return channel -> indexResolver.resolveAsSeparateMappings(indexPattern, regexPattern, toActionListener(channel, esIndices -> {
|
||||
List<MetaColumnInfo> columns = new ArrayList<>();
|
||||
for (EsIndex esIndex : esIndices) {
|
||||
int pos = 0;
|
||||
|
@ -9,7 +9,6 @@ import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.HandledTransportAction;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
@ -47,9 +46,14 @@ public class TransportSqlListColumnsAction extends HandledTransportAction<SqlLis
|
||||
// TODO: This is wrong
|
||||
// See https://github.com/elastic/x-pack-elasticsearch/pull/3438/commits/61b7c26fe08db2721f0431579f215fe493744af3
|
||||
// and https://github.com/elastic/x-pack-elasticsearch/issues/3460
|
||||
String indexPattern = Strings.hasText(request.getTablePattern()) ? StringUtils.jdbcToEsPattern(request.getTablePattern()) : "*";
|
||||
Pattern columnMatcher = hasText(request.getColumnPattern()) ? StringUtils.likeRegex(request.getColumnPattern()) : null;
|
||||
indexResolver.asList(indexPattern, ActionListener.wrap(esIndices -> {
|
||||
String indexPattern = hasText(request.getTablePattern()) ?
|
||||
StringUtils.likeToIndexWildcard(request.getTablePattern(), (char) 0) : "*";
|
||||
String regexPattern = hasText(request.getTablePattern()) ?
|
||||
StringUtils.likeToJavaPattern(request.getTablePattern(), (char) 0) : "*";
|
||||
Pattern columnMatcher = hasText(request.getColumnPattern()) ?
|
||||
Pattern.compile(StringUtils.likeToJavaPattern(request.getColumnPattern(), (char) 0)) : null;
|
||||
|
||||
indexResolver.resolveAsSeparateMappings(indexPattern, regexPattern, ActionListener.wrap(esIndices -> {
|
||||
List<ColumnInfo> columns = new ArrayList<>();
|
||||
for (EsIndex esIndex : esIndices) {
|
||||
for (Map.Entry<String, DataType> entry : esIndex.mapping().entrySet()) {
|
||||
|
@ -13,8 +13,8 @@ import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.EsIndex;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.IndexResolver;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.IndexResolver.IndexInfo;
|
||||
import org.elasticsearch.xpack.sql.util.StringUtils;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
@ -41,10 +41,12 @@ public class TransportSqlListTablesAction extends HandledTransportAction<SqlList
|
||||
// TODO: This is wrong
|
||||
// See https://github.com/elastic/x-pack-elasticsearch/pull/3438/commits/61b7c26fe08db2721f0431579f215fe493744af3
|
||||
// and https://github.com/elastic/x-pack-elasticsearch/issues/3460
|
||||
String indexPattern = hasText(request.getPattern()) ? StringUtils.jdbcToEsPattern(request.getPattern()) : "*";
|
||||
indexResolver.asList(indexPattern, ActionListener.wrap(list -> listener.onResponse(
|
||||
new SqlListTablesResponse(list.stream()
|
||||
.map(EsIndex::name)
|
||||
String indexPattern = hasText(request.getPattern()) ? StringUtils.likeToIndexWildcard(request.getPattern(), (char) 0) : "*";
|
||||
String regexPattern = hasText(request.getPattern()) ? StringUtils.likeToJavaPattern(request.getPattern(), (char) 0) : "*";
|
||||
|
||||
indexResolver.resolveNames(indexPattern, regexPattern, ActionListener.wrap(set -> listener.onResponse(
|
||||
new SqlListTablesResponse(set.stream()
|
||||
.map(IndexInfo::name)
|
||||
.collect(toList()))), listener::onFailure));
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.xpack.sql.analysis.analyzer.Analyzer;
|
||||
import org.elasticsearch.xpack.sql.analysis.analyzer.PreAnalyzer;
|
||||
import org.elasticsearch.xpack.sql.analysis.analyzer.PreAnalyzer.PreAnalysis;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.GetIndexResult;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.IndexResolution;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.IndexResolver;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.MappingException;
|
||||
import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry;
|
||||
@ -36,7 +36,7 @@ public class SqlSession {
|
||||
private final Planner planner;
|
||||
|
||||
// TODO rename to `configuration`
|
||||
private Configuration settings;
|
||||
private final Configuration settings;
|
||||
|
||||
public SqlSession(SqlSession other) {
|
||||
this(other.settings, other.client, other.functionRegistry, other.indexResolver,
|
||||
@ -102,25 +102,25 @@ public class SqlSession {
|
||||
return;
|
||||
}
|
||||
|
||||
preAnalyze(parsed, getIndexResult -> {
|
||||
Analyzer analyzer = new Analyzer(functionRegistry, getIndexResult, settings.timeZone());
|
||||
preAnalyze(parsed, r -> {
|
||||
Analyzer analyzer = new Analyzer(functionRegistry, r, settings.timeZone());
|
||||
return analyzer.debugAnalyze(parsed);
|
||||
}, listener);
|
||||
}
|
||||
|
||||
private <T> void preAnalyze(LogicalPlan parsed, Function<GetIndexResult, T> action, ActionListener<T> listener) {
|
||||
private <T> void preAnalyze(LogicalPlan parsed, Function<IndexResolution, T> action, ActionListener<T> listener) {
|
||||
PreAnalysis preAnalysis = preAnalyzer.preAnalyze(parsed);
|
||||
// TODO we plan to support joins in the future when possible, but for now we'll just fail early if we see one
|
||||
if (preAnalysis.indices.size() > 1) {
|
||||
// Note: JOINs are not supported but we detect them when
|
||||
listener.onFailure(new MappingException("Queries with multiple indices are not supported"));
|
||||
} else if (preAnalysis.indices.size() == 1) {
|
||||
indexResolver.asIndex(preAnalysis.indices.get(0),
|
||||
indexResolver.resolveWithSameMapping(preAnalysis.indices.get(0), null,
|
||||
wrap(indexResult -> listener.onResponse(action.apply(indexResult)), listener::onFailure));
|
||||
} else {
|
||||
try {
|
||||
//TODO when can this ever happen? shouldn't it be an exception instead?
|
||||
listener.onResponse(action.apply(GetIndexResult.invalid("_na_")));
|
||||
// occurs when dealing with local relations (SELECT 5+2)
|
||||
listener.onResponse(action.apply(IndexResolution.invalid("[none specified]")));
|
||||
} catch (Exception ex) {
|
||||
listener.onFailure(ex);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@ -38,8 +39,9 @@ public abstract class StringUtils {
|
||||
|
||||
public static <E> String limitedToString(Collection<E> c) {
|
||||
Iterator<E> it = c.iterator();
|
||||
if (!it.hasNext())
|
||||
if (!it.hasNext()) {
|
||||
return "[]";
|
||||
}
|
||||
|
||||
// ..]
|
||||
StringBuilder sb = new StringBuilder(TO_STRING_LIMIT + 4);
|
||||
@ -55,8 +57,9 @@ public abstract class StringUtils {
|
||||
else {
|
||||
sb.append(next);
|
||||
}
|
||||
if (!it.hasNext())
|
||||
if (!it.hasNext()) {
|
||||
return sb.append(']').toString();
|
||||
}
|
||||
sb.append(',').append(' ');
|
||||
}
|
||||
}
|
||||
@ -95,31 +98,37 @@ public abstract class StringUtils {
|
||||
return string == null ? EMPTY : string;
|
||||
}
|
||||
|
||||
// % -> *
|
||||
// % -> .*
|
||||
// _ -> .
|
||||
// consider \ as an escaping char
|
||||
public static String sqlToJavaPattern(CharSequence sqlPattern, char escapeChar, boolean shouldEscape) {
|
||||
StringBuilder regex = new StringBuilder(sqlPattern.length() + 4);
|
||||
// escape character - can be 0 (in which case every regex gets escaped) or
|
||||
// should be followed by % or _ (otherwise an exception is thrown)
|
||||
public static String likeToJavaPattern(String pattern, char escape) {
|
||||
StringBuilder regex = new StringBuilder(pattern.length() + 4);
|
||||
|
||||
boolean escaped = false;
|
||||
regex.append('^');
|
||||
for (int i = 0; i < sqlPattern.length(); i++) {
|
||||
char curr = sqlPattern.charAt(i);
|
||||
if (shouldEscape && !escaped && (curr == escapeChar)) {
|
||||
for (int i = 0; i < pattern.length(); i++) {
|
||||
char curr = pattern.charAt(i);
|
||||
if (!escaped && (curr == escape) && escape != 0) {
|
||||
escaped = true;
|
||||
regex.append(curr);
|
||||
if (i + 1 == pattern.length()) {
|
||||
throw new SqlIllegalArgumentException(
|
||||
"Invalid sequence - escape character is not followed by special wildcard char");
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (curr) {
|
||||
case '%':
|
||||
regex.append(escaped ? "%" : ".*");
|
||||
escaped = false;
|
||||
break;
|
||||
case '_':
|
||||
regex.append(escaped ? "_" : ".");
|
||||
escaped = false;
|
||||
break;
|
||||
default:
|
||||
if (escaped) {
|
||||
throw new SqlIllegalArgumentException(
|
||||
"Invalid sequence - escape character is not followed by special wildcard char");
|
||||
}
|
||||
// escape special regex characters
|
||||
switch (curr) {
|
||||
case '\\':
|
||||
@ -136,14 +145,11 @@ public abstract class StringUtils {
|
||||
case ']':
|
||||
case '{':
|
||||
case '}':
|
||||
if (!escaped) {
|
||||
regex.append('\\');
|
||||
}
|
||||
regex.append('\\');
|
||||
}
|
||||
|
||||
regex.append(curr);
|
||||
escaped = false;
|
||||
}
|
||||
escaped = false;
|
||||
}
|
||||
}
|
||||
regex.append('$');
|
||||
@ -151,25 +157,98 @@ public abstract class StringUtils {
|
||||
return regex.toString();
|
||||
}
|
||||
|
||||
//TODO: likely this needs to be changed to probably its own indexNameResolver
|
||||
public static String jdbcToEsPattern(String sqlPattern) {
|
||||
if (Strings.hasText(sqlPattern)) {
|
||||
// the index might include a type - since we only support only support one type per index, remove the type
|
||||
int dotIndex = sqlPattern.indexOf(".");
|
||||
if (dotIndex >= 0) {
|
||||
sqlPattern = sqlPattern.substring(0, dotIndex);
|
||||
/**
|
||||
* Translates a like pattern to a Lucene wildcard.
|
||||
* This methods pays attention to the custom escape char which gets converted into \ (used by Lucene).
|
||||
* <pre>
|
||||
* % -> *
|
||||
* _ -> ?
|
||||
* escape character - can be 0 (in which case every regex gets escaped) or should be followed by
|
||||
* % or _ (otherwise an exception is thrown)
|
||||
* </pre>
|
||||
*/
|
||||
public static String likeToLuceneWildcard(String pattern, char escape) {
|
||||
StringBuilder wildcard = new StringBuilder(pattern.length() + 4);
|
||||
|
||||
boolean escaped = false;
|
||||
for (int i = 0; i < pattern.length(); i++) {
|
||||
char curr = pattern.charAt(i);
|
||||
|
||||
if (!escaped && (curr == escape) && escape != 0) {
|
||||
if (i + 1 == pattern.length()) {
|
||||
throw new SqlIllegalArgumentException("Invalid sequence - escape character is not followed by special wildcard char");
|
||||
}
|
||||
escaped = true;
|
||||
} else {
|
||||
switch (curr) {
|
||||
case '%':
|
||||
wildcard.append(escaped ? "%" : "*");
|
||||
break;
|
||||
case '_':
|
||||
wildcard.append(escaped ? "_" : "?");
|
||||
break;
|
||||
default:
|
||||
if (escaped) {
|
||||
throw new SqlIllegalArgumentException(
|
||||
"Invalid sequence - escape character is not followed by special wildcard char");
|
||||
}
|
||||
// escape special regex characters
|
||||
switch (curr) {
|
||||
case '\\':
|
||||
case '*':
|
||||
case '?':
|
||||
wildcard.append('\\');
|
||||
}
|
||||
wildcard.append(curr);
|
||||
}
|
||||
escaped = false;
|
||||
}
|
||||
return sqlPattern.replace('%', '*').replace('_', '*');
|
||||
}
|
||||
return EMPTY;
|
||||
return wildcard.toString();
|
||||
}
|
||||
|
||||
public static String sqlToJavaPattern(CharSequence sqlPattern) {
|
||||
return sqlToJavaPattern(sqlPattern, '\\', true);
|
||||
/**
|
||||
* Translates a like pattern to pattern for ES index name expression resolver.
|
||||
*
|
||||
* Note the resolver only supports * (not ?) and has no notion of escaping. This is not really an issue since we don't allow *
|
||||
* anyway in the pattern.
|
||||
*/
|
||||
public static String likeToIndexWildcard(String pattern, char escape) {
|
||||
StringBuilder wildcard = new StringBuilder(pattern.length() + 4);
|
||||
|
||||
boolean escaped = false;
|
||||
for (int i = 0; i < pattern.length(); i++) {
|
||||
char curr = pattern.charAt(i);
|
||||
|
||||
if (!escaped && (curr == escape) && escape != 0) {
|
||||
if (i + 1 == pattern.length()) {
|
||||
throw new SqlIllegalArgumentException("Invalid sequence - escape character is not followed by special wildcard char");
|
||||
}
|
||||
escaped = true;
|
||||
} else {
|
||||
switch (curr) {
|
||||
case '%':
|
||||
wildcard.append(escaped ? "%" : "*");
|
||||
break;
|
||||
case '_':
|
||||
wildcard.append(escaped ? "_" : "*");
|
||||
break;
|
||||
default:
|
||||
if (escaped) {
|
||||
throw new SqlIllegalArgumentException(
|
||||
"Invalid sequence - escape character is not followed by special wildcard char");
|
||||
}
|
||||
// the resolver doesn't support escaping...
|
||||
wildcard.append(curr);
|
||||
}
|
||||
escaped = false;
|
||||
}
|
||||
}
|
||||
return wildcard.toString();
|
||||
}
|
||||
|
||||
public static Pattern likeRegex(String likePattern) {
|
||||
return Pattern.compile(sqlToJavaPattern(likePattern));
|
||||
return Pattern.compile(likeToJavaPattern(likePattern, '\\'));
|
||||
}
|
||||
|
||||
public static String toString(SearchSourceBuilder source) {
|
||||
|
@ -7,7 +7,7 @@ package org.elasticsearch.xpack.sql.analysis.analyzer;
|
||||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.EsIndex;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.GetIndexResult;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.IndexResolution;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.MappingException;
|
||||
import org.elasticsearch.xpack.sql.expression.Attribute;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
@ -38,7 +38,7 @@ import static org.hamcrest.Matchers.not;
|
||||
public class FieldAttributeTests extends ESTestCase {
|
||||
|
||||
private SqlParser parser;
|
||||
private GetIndexResult getIndexResult;
|
||||
private IndexResolution getIndexResult;
|
||||
private FunctionRegistry functionRegistry;
|
||||
private Analyzer analyzer;
|
||||
|
||||
@ -49,7 +49,7 @@ public class FieldAttributeTests extends ESTestCase {
|
||||
Map<String, DataType> mapping = TypesTests.loadMapping("mapping-multi-field-variation.json");
|
||||
|
||||
EsIndex test = new EsIndex("test", mapping);
|
||||
getIndexResult = GetIndexResult.valid(test);
|
||||
getIndexResult = IndexResolution.valid(test);
|
||||
analyzer = new Analyzer(functionRegistry, getIndexResult, DateTimeZone.UTC);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ package org.elasticsearch.xpack.sql.analysis.analyzer;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.sql.analysis.AnalysisException;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.EsIndex;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.GetIndexResult;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.IndexResolution;
|
||||
import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry;
|
||||
import org.elasticsearch.xpack.sql.parser.SqlParser;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
@ -23,10 +23,10 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
||||
private String verify(String sql) {
|
||||
Map<String, DataType> mapping = TypesTests.loadMapping("mapping-multi-field-variation.json");
|
||||
EsIndex test = new EsIndex("test", mapping);
|
||||
return verify(GetIndexResult.valid(test), sql);
|
||||
return verify(IndexResolution.valid(test), sql);
|
||||
}
|
||||
|
||||
private String verify(GetIndexResult getIndexResult, String sql) {
|
||||
private String verify(IndexResolution getIndexResult, String sql) {
|
||||
Analyzer analyzer = new Analyzer(new FunctionRegistry(), getIndexResult, DateTimeZone.UTC);
|
||||
AnalysisException e = expectThrows(AnalysisException.class, () -> analyzer.analyze(parser.createStatement(sql), true));
|
||||
assertTrue(e.getMessage().startsWith("Found "));
|
||||
@ -35,7 +35,7 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
||||
}
|
||||
|
||||
public void testMissingIndex() {
|
||||
assertEquals("1:17: Unknown index [missing]", verify(GetIndexResult.notFound("missing"), "SELECT foo FROM missing"));
|
||||
assertEquals("1:17: Unknown index [missing]", verify(IndexResolution.notFound("missing"), "SELECT foo FROM missing"));
|
||||
}
|
||||
|
||||
public void testMissingColumn() {
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.parser;
|
||||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.regex.Like;
|
||||
import org.elasticsearch.xpack.sql.expression.regex.LikePattern;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class LikeEscapingParsingTests extends ESTestCase {
|
||||
|
||||
private final SqlParser parser = new SqlParser(DateTimeZone.UTC);
|
||||
|
||||
private String error(String pattern) {
|
||||
ParsingException ex = expectThrows(ParsingException.class,
|
||||
() -> parser.createExpression(String.format(Locale.ROOT, "exp LIKE %s", pattern)));
|
||||
|
||||
return ex.getMessage();
|
||||
}
|
||||
|
||||
private LikePattern like(String pattern) {
|
||||
Expression exp = parser.createExpression(String.format(Locale.ROOT, "exp LIKE %s", pattern));
|
||||
assertThat(exp, instanceOf(Like.class));
|
||||
Like l = (Like) exp;
|
||||
return l.right();
|
||||
}
|
||||
|
||||
public void testNoEscaping() {
|
||||
LikePattern like = like("'string'");
|
||||
assertThat(like.pattern(), is("string"));
|
||||
assertThat(like.asJavaRegex(), is("^string$"));
|
||||
assertThat(like.asLuceneWildcard(), is("string"));
|
||||
}
|
||||
|
||||
public void testEscapingLastChar() {
|
||||
assertThat(error("'string|' ESCAPE '|'"),
|
||||
is("line 1:11: Pattern [string|] is invalid as escape char [|] at position 6 does not escape anything"));
|
||||
}
|
||||
|
||||
public void testEscapingWrongChar() {
|
||||
assertThat(error("'|string' ESCAPE '|'"),
|
||||
is("line 1:11: Pattern [|string] is invalid as escape char [|] at position 0 can only escape wildcard chars; found [s]"));
|
||||
}
|
||||
|
||||
public void testInvalidChar() {
|
||||
assertThat(error("'%string' ESCAPE '%'"),
|
||||
is("line 1:28: Char [%] cannot be used for escaping"));
|
||||
}
|
||||
|
||||
public void testCannotUseStar() {
|
||||
assertThat(error("'|*string' ESCAPE '|'"),
|
||||
is("line 1:11: Invalid char [*] found in pattern [|*string] at position 1; use [%] or [_] instead"));
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ import org.elasticsearch.xpack.sql.expression.Order;
|
||||
import org.elasticsearch.xpack.sql.expression.UnresolvedAttribute;
|
||||
import org.elasticsearch.xpack.sql.expression.UnresolvedStar;
|
||||
import org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction;
|
||||
import org.elasticsearch.xpack.sql.parser.SqlParser;
|
||||
import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan;
|
||||
import org.elasticsearch.xpack.sql.plan.logical.OrderBy;
|
||||
import org.elasticsearch.xpack.sql.plan.logical.Project;
|
||||
@ -20,11 +19,10 @@ import org.joda.time.DateTimeZone;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
public class SqlParserTests extends ESTestCase {
|
||||
public void testSelectStar() {
|
||||
singleProjection(project(parseStatement("SELECT * FROM foo")), UnresolvedStar.class);
|
||||
@ -94,7 +92,7 @@ public class SqlParserTests extends ESTestCase {
|
||||
}
|
||||
|
||||
private Project project(LogicalPlan plan) {
|
||||
List<Project> sync = new ArrayList<Project>(1);
|
||||
List<Project> sync = new ArrayList<>(1);
|
||||
projectRecur(plan, sync);
|
||||
assertThat("expected only one SELECT", sync, hasSize(1));
|
||||
return sync.get(0);
|
||||
@ -129,4 +127,4 @@ public class SqlParserTests extends ESTestCase {
|
||||
String dirStr = dir.toString();
|
||||
return randomBoolean() && dirStr.equals("ASC") ? "" : " " + dirStr;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ package org.elasticsearch.xpack.sql.planner;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.sql.analysis.analyzer.Analyzer;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.EsIndex;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.GetIndexResult;
|
||||
import org.elasticsearch.xpack.sql.analysis.index.IndexResolution;
|
||||
import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry;
|
||||
import org.elasticsearch.xpack.sql.optimizer.Optimizer;
|
||||
import org.elasticsearch.xpack.sql.parser.SqlParser;
|
||||
@ -33,7 +33,7 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
||||
mapping.put("text", DataTypes.TEXT);
|
||||
mapping.put("keyword", DataTypes.KEYWORD);
|
||||
EsIndex test = new EsIndex("test", mapping);
|
||||
GetIndexResult getIndexResult = GetIndexResult.valid(test);
|
||||
IndexResolution getIndexResult = IndexResolution.valid(test);
|
||||
Analyzer analyzer = new Analyzer(new FunctionRegistry(), getIndexResult, DateTimeZone.UTC);
|
||||
LogicalPlan plan = optimizer.optimize(analyzer.analyze(parser.createStatement(sql), true));
|
||||
PlanningException e = expectThrows(PlanningException.class, () -> planner.mapPlan(plan, true));
|
||||
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.util;
|
||||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.util.StringUtils.likeToJavaPattern;
|
||||
import static org.elasticsearch.xpack.sql.util.StringUtils.likeToLuceneWildcard;;
|
||||
|
||||
public class LikeConversionTests extends ESTestCase {
|
||||
|
||||
private static String regex(String pattern) {
|
||||
return likeToJavaPattern(pattern, '|');
|
||||
}
|
||||
|
||||
private static String wildcard(String pattern) {
|
||||
return likeToLuceneWildcard(pattern, '|');
|
||||
}
|
||||
|
||||
public void testNoRegex() {
|
||||
assertEquals("^fooBar$", regex("fooBar"));
|
||||
}
|
||||
|
||||
public void testEscapedSqlWildcard() {
|
||||
assertEquals("^foo\\\\_bar$", regex("foo\\|_bar"));
|
||||
}
|
||||
|
||||
public void testEscapedSqlWildcardGreedy() {
|
||||
assertEquals("^foo.*%bar$", regex("foo%|%bar"));
|
||||
}
|
||||
|
||||
public void testSimpleSqlRegex1() {
|
||||
assertEquals("^foo.bar$", regex("foo_bar"));
|
||||
}
|
||||
|
||||
public void testSimpleSqlRegex2() {
|
||||
assertEquals("^foo.*bar$", regex("foo%bar"));
|
||||
}
|
||||
|
||||
public void testMultipleSqlRegexes() {
|
||||
assertEquals("^foo.*bar.$", regex("foo%bar_"));
|
||||
}
|
||||
|
||||
public void testJavaRegexNoSqlRegex() {
|
||||
assertEquals("^foo\\.\\*bar$", regex("foo.*bar"));
|
||||
}
|
||||
|
||||
public void testMultipleRegexAndSqlRegex() {
|
||||
assertEquals("^foo\\\\\\.\\*bar\\..*$", regex("foo\\.*bar.%"));
|
||||
}
|
||||
|
||||
public void testEscapedJavaRegex() {
|
||||
assertEquals("^\\[a-zA-Z\\]$", regex("[a-zA-Z]"));
|
||||
}
|
||||
|
||||
public void testComplicatedJavaRegex() {
|
||||
assertEquals("^\\^\\[0\\.\\.9\\]\\.\\*\\$$", regex("^[0..9].*$"));
|
||||
}
|
||||
|
||||
public void testNoWildcard() {
|
||||
assertEquals("foo", wildcard("foo"));
|
||||
}
|
||||
|
||||
public void testQuestionMarkWildcard() {
|
||||
assertEquals("foo?bar", wildcard("foo_bar"));
|
||||
}
|
||||
|
||||
public void testStarWildcard() {
|
||||
assertEquals("foo*", wildcard("foo%"));
|
||||
}
|
||||
|
||||
public void testWildcardEscapeLuceneWildcard() {
|
||||
assertEquals("foo\\*bar*", wildcard("foo*bar%"));
|
||||
}
|
||||
|
||||
public void testWildcardEscapedWildcard() {
|
||||
assertEquals("foo\\*bar%", wildcard("foo*bar|%"));
|
||||
}
|
||||
|
||||
public void testEscapedLuceneEscape() {
|
||||
assertEquals("foo\\\\\\*bar", wildcard("foo\\*bar"));
|
||||
}
|
||||
|
||||
public void testMixOfEscapedLuceneAndSqlEscapes() {
|
||||
assertEquals("foo\\\\?_\\*bar*", wildcard("foo\\_|_*bar%"));
|
||||
}
|
||||
|
||||
public void testWildcardIgnoreEscapedWildcard() {
|
||||
assertEquals("foo\\\\\\*bar*", wildcard("foo\\*bar%"));
|
||||
}
|
||||
|
||||
public void testWildcardDoubleEscaping() {
|
||||
assertEquals("foo\\\\\\\\bar", wildcard("foo\\\\bar"));
|
||||
}
|
||||
|
||||
public void testWildcardTripleEscaping() {
|
||||
assertEquals("foo\\\\\\\\bar\\?\\\\?", wildcard("foo\\\\bar?\\_"));
|
||||
}
|
||||
|
||||
public void testWildcardIgnoreDoubleEscapedButSkipEscapingOfSql() {
|
||||
assertEquals("foo\\\\\\*bar\\\\?\\?", wildcard("foo\\*bar\\_?"));
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.util;
|
||||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.util.StringUtils.sqlToJavaPattern;
|
||||
|
||||
public class StringUtilsTests extends ESTestCase {
|
||||
|
||||
public void testNoRegex() {
|
||||
assertEquals("^fooBar$", sqlToJavaPattern("fooBar"));
|
||||
}
|
||||
|
||||
public void testEscapedJavaRegex() {
|
||||
assertEquals("^\\.\\d$", sqlToJavaPattern("\\.\\d"));
|
||||
}
|
||||
|
||||
public void testSimpleSqlRegex1() {
|
||||
assertEquals("^foo.bar$", sqlToJavaPattern("foo_bar"));
|
||||
}
|
||||
|
||||
public void testSimpleSqlRegex2() {
|
||||
assertEquals("^foo.*bar$", sqlToJavaPattern("foo%bar"));
|
||||
}
|
||||
|
||||
public void testMultipleSqlRegexes() {
|
||||
assertEquals("^foo.*bar.$", sqlToJavaPattern("foo%bar_"));
|
||||
}
|
||||
|
||||
public void testJavaRegexNoSqlRegex() {
|
||||
assertEquals("^foo\\.\\*bar$", sqlToJavaPattern("foo.*bar"));
|
||||
}
|
||||
|
||||
public void testMultipleRegexAndSqlRegex() {
|
||||
assertEquals("^foo\\.\\*bar\\..*$", sqlToJavaPattern("foo.*bar.%"));
|
||||
}
|
||||
|
||||
public void testComplicatedJavaRegex() {
|
||||
assertEquals("^\\^\\[\\d\\]\\.\\*\\$$", sqlToJavaPattern("^[\\d].*$"));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user