SQL: Implement CASE... WHEN... THEN... ELSE... END (#41349)

Implement the ANSI SQL CASE expression which provides the if/else
functionality common to most programming languages.

The CASE expression can have multiple WHEN branches and becomes a
powerful tool for SQL queries as it can be used in SELECT, WHERE,
GROUP BY, HAVING and ORDER BY clauses.

Closes: #36200
(cherry picked from commit 8b2577406f47ae60d15803058921d128390af0b6)
This commit is contained in:
Marios Trivyzas 2019-04-22 19:26:15 +03:00
parent eb2295ac81
commit 67d4e399c2
No known key found for this signature in database
GPG Key ID: 8817B46B0CF36A3F
33 changed files with 3136 additions and 1791 deletions

View File

@ -1,10 +1,103 @@
[role="xpack"]
[testenv="basic"]
[[sql-functions-conditional]]
=== Conditional Functions
=== Conditional Functions And Expressions
Functions that return one of their arguments by evaluating in an if-else manner.
[[sql-functions-conditional-case]]
==== `CASE`
.Synopsis:
[source, sql]
----
CASE WHEN condition THEN result
[WHEN ...]
[ELSE default_result]
END
----
*Input*:
One or multiple _WHEN *condition* THEN *result_* clauses are used and the expression can optionally have
an _ELSE *default_result_* clause. Every *condition* should be a boolean expression.
*Output*: one of the *result* expressions if the corresponding _WHEN *condition_* evaluates to `true` or
the *default_result* if all _WHEN *condition_* clauses evaluate to `false`. If the optional _ELSE *default_result_*
clause is missing and all _WHEN *condition_* clauses evaluate to `false` then `null` is returned.
.Description
The CASE expression is a generic conditional expression which simulates if/else statements of other programming languages
If the conditions result is true, the value of the result expression that follows the condition will be the returned
the subsequent when clauses will be skipped and not processed.
["source","sql",subs="attributes,callouts,macros"]
----
include-tagged::{sql-specs}/docs/docs.csv-spec[case]
----
["source","sql",subs="attributes,callouts,macros"]
----
include-tagged::{sql-specs}/docs/docs.csv-spec[caseReturnNull]
----
["source","sql",subs="attributes,callouts,macros"]
----
include-tagged::{sql-specs}/docs/docs.csv-spec[caseWithElse]
----
As a variant, a case expression can be expressed with a syntax similar to *switch-case* of other programming languages:
[source, sql]
----
CASE expression
WHEN value1 THEN result1
[WHEN value2 THEN result2]
[WHEN ...]
[ELSE default_result]
END
----
In this case it's transformed internally to:
[source, sql]
----
CASE WHEN expression = value1 THEN result1
[WHEN expression = value2 THEN result2]
[WHEN ...]
[ELSE default_result]
END
----
["source","sql",subs="attributes,callouts,macros"]
----
include-tagged::{sql-specs}/docs/docs.csv-spec[caseWithOperand]
----
["source","sql",subs="attributes,callouts,macros"]
----
include-tagged::{sql-specs}/docs/docs.csv-spec[caseWithOperandAndElse]
----
[NOTE]
===============================
All result expressions must be of compatible data types. More specifically all result
expressions should have a compatible data type with the 1st _non-null_ result expression.
E.g.:
for the following query:
[source, sql]
CASE WHEN a = 1 THEN null
WHEN a > 2 THEN 10
WHEN a > 5 THEN 'foo'
END
an error message would be returned, mentioning that *'foo'* is of data type *keyword*,
which does not match the expected data type *integer* (based on result *10*).
===============================
[[sql-functions-conditional-coalesce]]
==== `COALESCE`

View File

@ -127,6 +127,7 @@
** <<sql-functions-type-conversion-cast>>
** <<sql-functions-type-conversion-convert>>
* <<sql-functions-conditional>>
** <<sql-functions-conditional-case>>
** <<sql-functions-conditional-coalesce>>
** <<sql-functions-conditional-greatest>>
** <<sql-functions-conditional-ifnull>>

View File

@ -25,6 +25,7 @@ STDDEV_POP |AGGREGATE
SUM_OF_SQUARES |AGGREGATE
VAR_POP |AGGREGATE
HISTOGRAM |GROUPING
CASE |CONDITIONAL
COALESCE |CONDITIONAL
GREATEST |CONDITIONAL
IFNULL |CONDITIONAL

View File

@ -0,0 +1,181 @@
caseField
SELECT emp_no, CASE WHEN emp_no - 10000 < 10 THEN 'First 10' ELSE 'Second 10' END as "case" FROM test_emp WHERE emp_no >= 10005
ORDER BY emp_no LIMIT 10;
emp_no | case
--------+-----------
10005 | First 10
10006 | First 10
10007 | First 10
10008 | First 10
10009 | First 10
10010 | Second 10
10011 | Second 10
10012 | Second 10
10013 | Second 10
10014 | Second 10
;
caseFieldWithoutAlias
SELECT emp_no, CASE WHEN emp_no - 10000 < 10 THEN emp_no ELSE emp_no % 10 END FROM test_emp WHERE emp_no >= 10005
ORDER BY emp_no LIMIT 10;
emp_no | CASE WHEN emp_no - 10000 < 10 THEN emp_no ELSE emp_no % 10 END
--------+----------------------------------------------------------------
10005 | 10005
10006 | 10006
10007 | 10007
10008 | 10008
10009 | 10009
10010 | 0
10011 | 1
10012 | 2
10013 | 3
10014 | 4
;
caseFieldNoElse
SELECT emp_no, CASE WHEN emp_no - 10000 < 10 THEN 'First 10' END as "case" FROM test_emp WHERE emp_no >= 10005
ORDER BY emp_no LIMIT 10;
emp_no | case
--------+----------
10005 | First 10
10006 | First 10
10007 | First 10
10008 | First 10
10009 | First 10
10010 | null
10011 | null
10012 | null
10013 | null
10014 | null
;
caseWhere
SELECT last_name FROM test_emp WHERE CASE WHEN LENGTH(last_name) < 7 THEN 'ShortName' ELSE 'LongName' END = 'LongName'
ORDER BY emp_no LIMIT 10;
last_name
-----------
Facello
Bamford
Koblick
Maliniak
Preusig
Zielinski
Kalloufi
Piveteau
Bridgland
Nooteboom
;
caseWhereNoElse
SELECT last_name FROM test_emp WHERE CASE WHEN LENGTH(last_name) < 7 THEN 'ShortName' END IS NOT NULL
ORDER BY emp_no LIMIT 10;
last_name
-----------
Simmel
Peac
Sluis
Terkki
Genin
Peha
Erde
Famili
Pettey
Heyers
;
caseOrderBy
schema::last_name:s|languages:byte|emp_no:i
SELECT last_name, languages, emp_no FROM test_emp WHERE emp_no BETWEEN 10005 AND 10015
ORDER BY CASE WHEN languages >= 3 THEN 'first' ELSE 'second' END, emp_no LIMIT 10;
last_name | languages | emp_no
-----------+-----------+--------
Preusig | 3 | 10006
Zielinski | 4 | 10007
Piveteau | 4 | 10010
Sluis | 5 | 10011
Bridgland | 5 | 10012
Genin | 5 | 10014
Nooteboom | 5 | 10015
Maliniak | 1 | 10005
Kalloufi | 2 | 10008
Peac | 1 | 10009
;
caseOrderByNoElse
schema::last_name:s|languages:byte|emp_no:i
SELECT last_name, languages, emp_no FROM test_emp WHERE emp_no BETWEEN 10005 AND 10015
ORDER BY CASE WHEN languages >= 3 THEN 'first' END NULLS FIRST, emp_no LIMIT 10;
last_name | languages | emp_no
-----------+-----------+--------
Maliniak | 1 | 10005
Kalloufi | 2 | 10008
Peac | 1 | 10009
Terkki | 1 | 10013
Preusig | 3 | 10006
Zielinski | 4 | 10007
Piveteau | 4 | 10010
Sluis | 5 | 10011
Bridgland | 5 | 10012
Genin | 5 | 10014
;
caseGroupBy
schema::count:l|lang_skills:s
SELECT count(*) AS count, CASE WHEN NVL(languages, 0) <= 1 THEN 'zero-to-one' ELSE 'multilingual' END as lang_skills
FROM test_emp GROUP BY lang_skills ORDER BY lang_skills;
count | lang_skills
-------+--------------
75 | multilingual
25 | zero-to-one
;
caseGroupByNoElse
schema::count:l|lang_skills:s
SELECT count(*) AS count, CASE WHEN NVL(languages, 0) <= 1 THEN 'zero-to-one' END as lang_skills
FROM test_emp GROUP BY lang_skills ORDER BY lang_skills;
count | lang_skills
-------+-------------
75 | null
25 | zero-to-one
;
caseGroupByComplexNested
schema::count:l|lang_skills:s
SELECT count(*) AS count,
CASE WHEN NVL(languages, 0) = 0 THEN 'zero'
WHEN languages = 1 THEN 'one'
WHEN languages = 2 THEN 'bilingual'
WHEN languages = 3 THEN 'trilingual'
ELSE 'multilingual'
END as lang_skills FROM test_emp GROUP BY lang_skills ORDER BY 2;
count | lang_skills
-------+--------------
19 | bilingual
39 | multilingual
15 | one
17 | trilingual
10 | zero
;
caseGroupByAndHaving
schema::count:l|gender:s|languages:byte
SELECT count(*) AS count, gender, languages FROM test_emp
GROUP BY 2, 3 HAVING CASE WHEN count(*) > 10 THEN 'many' ELSE 'a few' END = 'many'
ORDER BY 2, 3;
count | gender | languages
----------+-------------+---------------
11 | M | 2
11 | M | 3
11 | M | 4
;

View File

@ -202,6 +202,7 @@ STDDEV_POP |AGGREGATE
SUM_OF_SQUARES |AGGREGATE
VAR_POP |AGGREGATE
HISTOGRAM |GROUPING
CASE |CONDITIONAL
COALESCE |CONDITIONAL
GREATEST |CONDITIONAL
IFNULL |CONDITIONAL
@ -1987,10 +1988,81 @@ SELECT TRUNCATE(-345.153, 1) AS trimmed;
///////////////////////////////
//
// Null handling
// Conditional
//
///////////////////////////////
case
schema::case:s
// tag::case
SELECT CASE WHEN 1 > 2 THEN 'elastic'
WHEN 2 <= 3 THEN 'search'
END AS "case";
case
---------------
search
// end::case
;
caseReturnNull
schema::case:s
// tag::caseReturnNull
SELECT CASE WHEN 1 > 2 THEN 'elastic'
WHEN 2 > 10 THEN 'search'
END AS "case";
case
---------------
null
// end::caseReturnNull
;
caseWithElse
schema::case:s
// tag::caseWithElse
SELECT CASE WHEN 1 > 2 THEN 'elastic'
WHEN 2 > 10 THEN 'search'
ELSE 'default'
END AS "case";
case
---------------
default
// end::caseWithElse
;
caseWithOperand
schema::case:s
// tag::caseWithOperand
SELECT CASE 5
WHEN 1 THEN 'elastic'
WHEN 2 THEN 'search'
WHEN 5 THEN 'elasticsearch'
END AS "case";
case
---------------
elasticsearch
// end::caseWithOperand
;
caseWithOperandAndElse
schema::case:s
// tag::caseWithOperandAndElse
SELECT CASE 5
WHEN 1 THEN 'elastic'
WHEN 2 THEN 'search'
WHEN 3 THEN 'elasticsearch'
ELSE 'default'
END AS "case";
case
---------------
default
// end::caseWithOperandAndElse
;
coalesceReturnNonNull
// tag::coalesceReturnNonNull
SELECT COALESCE(null, 'elastic', 'search') AS "coalesce";

View File

@ -38,45 +38,9 @@ c
null
;
coalesceFirstNotNull
SELECT COALESCE(123) AS c;
c
123
;
coalesceWithFirstNullOfString
SELECT COALESCE(null, 'first') AS c;
c:s
first
;
coalesceWithFirstNullOfNumber
SELECT COALESCE(null, 123) AS c;
c:i
123
;
coalesceMixed
SELECT COALESCE(null, 123, null, 321);
COALESCE(null, 123, null, 321):i
123
;
coalesceMixedWithAlias
SELECT COALESCE(null, 123, null, 321) AS c;
c:i
123
;
coalesceScalar
SELECT COALESCE(null, ABS(123) + 1) AS c;
c:i
124
;

View File

@ -1,7 +1,22 @@
//
// Null expressions
// Null conditionals
//
coalesceFirstNotNull
SELECT COALESCE(123) AS c;
coalesceWithFirstNullOfString
SELECT COALESCE(null, 'first') AS c;
coalesceWithFirstNullOfNumber
SELECT COALESCE(null, 123) AS c;
coalesceMixedWithAlias
SELECT COALESCE(null, 123, null, 321) AS c;
coalesceScalar
SELECT COALESCE(null, ABS(123) + 1) AS c;
coalesceField
SELECT COALESCE(null, ABS(emp_no) + 1) AS c FROM test_emp ORDER BY emp_no LIMIT 5;

View File

@ -213,16 +213,17 @@ valueExpression
;
primaryExpression
: castExpression #cast
| primaryExpression CAST_OP dataType #castOperatorExpression
| extractExpression #extract
| builtinDateTimeFunction #currentDateTimeFunction
| constant #constantDefault
| (qualifiedName DOT)? ASTERISK #star
| functionExpression #function
| '(' query ')' #subqueryExpression
| qualifiedName #dereference
| '(' expression ')' #parenthesizedExpression
: castExpression #cast
| primaryExpression CAST_OP dataType #castOperatorExpression
| extractExpression #extract
| builtinDateTimeFunction #currentDateTimeFunction
| constant #constantDefault
| (qualifiedName DOT)? ASTERISK #star
| functionExpression #function
| '(' query ')' #subqueryExpression
| qualifiedName #dereference
| '(' expression ')' #parenthesizedExpression
| CASE (operand=booleanExpression)? whenClause+ (ELSE elseClause=booleanExpression)? END #case
;
builtinDateTimeFunction
@ -337,6 +338,10 @@ string
| STRING
;
whenClause
: WHEN condition=expression THEN result=expression
;
// http://developer.mimer.se/validator/sql-reserved-words.tml
nonReserved
: ANALYZE | ANALYZED
@ -368,6 +373,7 @@ AS: 'AS';
ASC: 'ASC';
BETWEEN: 'BETWEEN';
BY: 'BY';
CASE: 'CASE';
CAST: 'CAST';
CATALOG: 'CATALOG';
CATALOGS: 'CATALOGS';
@ -382,6 +388,8 @@ DEBUG: 'DEBUG';
DESC: 'DESC';
DESCRIBE: 'DESCRIBE';
DISTINCT: 'DISTINCT';
ELSE: 'ELSE';
END: 'END';
ESCAPE: 'ESCAPE';
EXECUTABLE: 'EXECUTABLE';
EXISTS: 'EXISTS';
@ -437,12 +445,14 @@ SYS: 'SYS';
TABLE: 'TABLE';
TABLES: 'TABLES';
TEXT: 'TEXT';
THEN: 'THEN';
TRUE: 'TRUE';
TO: 'TO';
TYPE: 'TYPE';
TYPES: 'TYPES';
USING: 'USING';
VERIFY: 'VERIFY';
WHEN: 'WHEN';
WHERE: 'WHERE';
WITH: 'WITH';
YEAR: 'YEAR';

View File

@ -11,122 +11,127 @@ AS=10
ASC=11
BETWEEN=12
BY=13
CAST=14
CATALOG=15
CATALOGS=16
COLUMNS=17
CONVERT=18
CURRENT_DATE=19
CURRENT_TIME=20
CURRENT_TIMESTAMP=21
DAY=22
DAYS=23
DEBUG=24
DESC=25
DESCRIBE=26
DISTINCT=27
ESCAPE=28
EXECUTABLE=29
EXISTS=30
EXPLAIN=31
EXTRACT=32
FALSE=33
FIRST=34
FORMAT=35
FROM=36
FULL=37
FUNCTIONS=38
GRAPHVIZ=39
GROUP=40
HAVING=41
HOUR=42
HOURS=43
IN=44
INNER=45
INTERVAL=46
IS=47
JOIN=48
LAST=49
LEFT=50
LIKE=51
LIMIT=52
MAPPED=53
MATCH=54
MINUTE=55
MINUTES=56
MONTH=57
MONTHS=58
NATURAL=59
NOT=60
NULL=61
NULLS=62
ON=63
OPTIMIZED=64
OR=65
ORDER=66
OUTER=67
PARSED=68
PHYSICAL=69
PLAN=70
RIGHT=71
RLIKE=72
QUERY=73
SCHEMAS=74
SECOND=75
SECONDS=76
SELECT=77
SHOW=78
SYS=79
TABLE=80
TABLES=81
TEXT=82
TRUE=83
TO=84
TYPE=85
TYPES=86
USING=87
VERIFY=88
WHERE=89
WITH=90
YEAR=91
YEARS=92
ESCAPE_ESC=93
FUNCTION_ESC=94
LIMIT_ESC=95
DATE_ESC=96
TIME_ESC=97
TIMESTAMP_ESC=98
GUID_ESC=99
ESC_END=100
EQ=101
NULLEQ=102
NEQ=103
LT=104
LTE=105
GT=106
GTE=107
PLUS=108
MINUS=109
ASTERISK=110
SLASH=111
PERCENT=112
CAST_OP=113
CONCAT=114
DOT=115
PARAM=116
STRING=117
INTEGER_VALUE=118
DECIMAL_VALUE=119
IDENTIFIER=120
DIGIT_IDENTIFIER=121
TABLE_IDENTIFIER=122
QUOTED_IDENTIFIER=123
BACKQUOTED_IDENTIFIER=124
SIMPLE_COMMENT=125
BRACKETED_COMMENT=126
WS=127
UNRECOGNIZED=128
DELIMITER=129
CASE=14
CAST=15
CATALOG=16
CATALOGS=17
COLUMNS=18
CONVERT=19
CURRENT_DATE=20
CURRENT_TIME=21
CURRENT_TIMESTAMP=22
DAY=23
DAYS=24
DEBUG=25
DESC=26
DESCRIBE=27
DISTINCT=28
ELSE=29
END=30
ESCAPE=31
EXECUTABLE=32
EXISTS=33
EXPLAIN=34
EXTRACT=35
FALSE=36
FIRST=37
FORMAT=38
FROM=39
FULL=40
FUNCTIONS=41
GRAPHVIZ=42
GROUP=43
HAVING=44
HOUR=45
HOURS=46
IN=47
INNER=48
INTERVAL=49
IS=50
JOIN=51
LAST=52
LEFT=53
LIKE=54
LIMIT=55
MAPPED=56
MATCH=57
MINUTE=58
MINUTES=59
MONTH=60
MONTHS=61
NATURAL=62
NOT=63
NULL=64
NULLS=65
ON=66
OPTIMIZED=67
OR=68
ORDER=69
OUTER=70
PARSED=71
PHYSICAL=72
PLAN=73
RIGHT=74
RLIKE=75
QUERY=76
SCHEMAS=77
SECOND=78
SECONDS=79
SELECT=80
SHOW=81
SYS=82
TABLE=83
TABLES=84
TEXT=85
THEN=86
TRUE=87
TO=88
TYPE=89
TYPES=90
USING=91
VERIFY=92
WHEN=93
WHERE=94
WITH=95
YEAR=96
YEARS=97
ESCAPE_ESC=98
FUNCTION_ESC=99
LIMIT_ESC=100
DATE_ESC=101
TIME_ESC=102
TIMESTAMP_ESC=103
GUID_ESC=104
ESC_END=105
EQ=106
NULLEQ=107
NEQ=108
LT=109
LTE=110
GT=111
GTE=112
PLUS=113
MINUS=114
ASTERISK=115
SLASH=116
PERCENT=117
CAST_OP=118
CONCAT=119
DOT=120
PARAM=121
STRING=122
INTEGER_VALUE=123
DECIMAL_VALUE=124
IDENTIFIER=125
DIGIT_IDENTIFIER=126
TABLE_IDENTIFIER=127
QUOTED_IDENTIFIER=128
BACKQUOTED_IDENTIFIER=129
SIMPLE_COMMENT=130
BRACKETED_COMMENT=131
WS=132
UNRECOGNIZED=133
DELIMITER=134
'('=1
')'=2
','=3
@ -140,105 +145,110 @@ DELIMITER=129
'ASC'=11
'BETWEEN'=12
'BY'=13
'CAST'=14
'CATALOG'=15
'CATALOGS'=16
'COLUMNS'=17
'CONVERT'=18
'CURRENT_DATE'=19
'CURRENT_TIME'=20
'CURRENT_TIMESTAMP'=21
'DAY'=22
'DAYS'=23
'DEBUG'=24
'DESC'=25
'DESCRIBE'=26
'DISTINCT'=27
'ESCAPE'=28
'EXECUTABLE'=29
'EXISTS'=30
'EXPLAIN'=31
'EXTRACT'=32
'FALSE'=33
'FIRST'=34
'FORMAT'=35
'FROM'=36
'FULL'=37
'FUNCTIONS'=38
'GRAPHVIZ'=39
'GROUP'=40
'HAVING'=41
'HOUR'=42
'HOURS'=43
'IN'=44
'INNER'=45
'INTERVAL'=46
'IS'=47
'JOIN'=48
'LAST'=49
'LEFT'=50
'LIKE'=51
'LIMIT'=52
'MAPPED'=53
'MATCH'=54
'MINUTE'=55
'MINUTES'=56
'MONTH'=57
'MONTHS'=58
'NATURAL'=59
'NOT'=60
'NULL'=61
'NULLS'=62
'ON'=63
'OPTIMIZED'=64
'OR'=65
'ORDER'=66
'OUTER'=67
'PARSED'=68
'PHYSICAL'=69
'PLAN'=70
'RIGHT'=71
'RLIKE'=72
'QUERY'=73
'SCHEMAS'=74
'SECOND'=75
'SECONDS'=76
'SELECT'=77
'SHOW'=78
'SYS'=79
'TABLE'=80
'TABLES'=81
'TEXT'=82
'TRUE'=83
'TO'=84
'TYPE'=85
'TYPES'=86
'USING'=87
'VERIFY'=88
'WHERE'=89
'WITH'=90
'YEAR'=91
'YEARS'=92
'{ESCAPE'=93
'{FN'=94
'{LIMIT'=95
'{D'=96
'{T'=97
'{TS'=98
'{GUID'=99
'}'=100
'='=101
'<=>'=102
'<'=104
'<='=105
'>'=106
'>='=107
'+'=108
'-'=109
'*'=110
'/'=111
'%'=112
'::'=113
'||'=114
'.'=115
'?'=116
'CASE'=14
'CAST'=15
'CATALOG'=16
'CATALOGS'=17
'COLUMNS'=18
'CONVERT'=19
'CURRENT_DATE'=20
'CURRENT_TIME'=21
'CURRENT_TIMESTAMP'=22
'DAY'=23
'DAYS'=24
'DEBUG'=25
'DESC'=26
'DESCRIBE'=27
'DISTINCT'=28
'ELSE'=29
'END'=30
'ESCAPE'=31
'EXECUTABLE'=32
'EXISTS'=33
'EXPLAIN'=34
'EXTRACT'=35
'FALSE'=36
'FIRST'=37
'FORMAT'=38
'FROM'=39
'FULL'=40
'FUNCTIONS'=41
'GRAPHVIZ'=42
'GROUP'=43
'HAVING'=44
'HOUR'=45
'HOURS'=46
'IN'=47
'INNER'=48
'INTERVAL'=49
'IS'=50
'JOIN'=51
'LAST'=52
'LEFT'=53
'LIKE'=54
'LIMIT'=55
'MAPPED'=56
'MATCH'=57
'MINUTE'=58
'MINUTES'=59
'MONTH'=60
'MONTHS'=61
'NATURAL'=62
'NOT'=63
'NULL'=64
'NULLS'=65
'ON'=66
'OPTIMIZED'=67
'OR'=68
'ORDER'=69
'OUTER'=70
'PARSED'=71
'PHYSICAL'=72
'PLAN'=73
'RIGHT'=74
'RLIKE'=75
'QUERY'=76
'SCHEMAS'=77
'SECOND'=78
'SECONDS'=79
'SELECT'=80
'SHOW'=81
'SYS'=82
'TABLE'=83
'TABLES'=84
'TEXT'=85
'THEN'=86
'TRUE'=87
'TO'=88
'TYPE'=89
'TYPES'=90
'USING'=91
'VERIFY'=92
'WHEN'=93
'WHERE'=94
'WITH'=95
'YEAR'=96
'YEARS'=97
'{ESCAPE'=98
'{FN'=99
'{LIMIT'=100
'{D'=101
'{T'=102
'{TS'=103
'{GUID'=104
'}'=105
'='=106
'<=>'=107
'<'=109
'<='=110
'>'=111
'>='=112
'+'=113
'-'=114
'*'=115
'/'=116
'%'=117
'::'=118
'||'=119
'.'=120
'?'=121

View File

@ -11,121 +11,126 @@ AS=10
ASC=11
BETWEEN=12
BY=13
CAST=14
CATALOG=15
CATALOGS=16
COLUMNS=17
CONVERT=18
CURRENT_DATE=19
CURRENT_TIME=20
CURRENT_TIMESTAMP=21
DAY=22
DAYS=23
DEBUG=24
DESC=25
DESCRIBE=26
DISTINCT=27
ESCAPE=28
EXECUTABLE=29
EXISTS=30
EXPLAIN=31
EXTRACT=32
FALSE=33
FIRST=34
FORMAT=35
FROM=36
FULL=37
FUNCTIONS=38
GRAPHVIZ=39
GROUP=40
HAVING=41
HOUR=42
HOURS=43
IN=44
INNER=45
INTERVAL=46
IS=47
JOIN=48
LAST=49
LEFT=50
LIKE=51
LIMIT=52
MAPPED=53
MATCH=54
MINUTE=55
MINUTES=56
MONTH=57
MONTHS=58
NATURAL=59
NOT=60
NULL=61
NULLS=62
ON=63
OPTIMIZED=64
OR=65
ORDER=66
OUTER=67
PARSED=68
PHYSICAL=69
PLAN=70
RIGHT=71
RLIKE=72
QUERY=73
SCHEMAS=74
SECOND=75
SECONDS=76
SELECT=77
SHOW=78
SYS=79
TABLE=80
TABLES=81
TEXT=82
TRUE=83
TO=84
TYPE=85
TYPES=86
USING=87
VERIFY=88
WHERE=89
WITH=90
YEAR=91
YEARS=92
ESCAPE_ESC=93
FUNCTION_ESC=94
LIMIT_ESC=95
DATE_ESC=96
TIME_ESC=97
TIMESTAMP_ESC=98
GUID_ESC=99
ESC_END=100
EQ=101
NULLEQ=102
NEQ=103
LT=104
LTE=105
GT=106
GTE=107
PLUS=108
MINUS=109
ASTERISK=110
SLASH=111
PERCENT=112
CAST_OP=113
CONCAT=114
DOT=115
PARAM=116
STRING=117
INTEGER_VALUE=118
DECIMAL_VALUE=119
IDENTIFIER=120
DIGIT_IDENTIFIER=121
TABLE_IDENTIFIER=122
QUOTED_IDENTIFIER=123
BACKQUOTED_IDENTIFIER=124
SIMPLE_COMMENT=125
BRACKETED_COMMENT=126
WS=127
UNRECOGNIZED=128
CASE=14
CAST=15
CATALOG=16
CATALOGS=17
COLUMNS=18
CONVERT=19
CURRENT_DATE=20
CURRENT_TIME=21
CURRENT_TIMESTAMP=22
DAY=23
DAYS=24
DEBUG=25
DESC=26
DESCRIBE=27
DISTINCT=28
ELSE=29
END=30
ESCAPE=31
EXECUTABLE=32
EXISTS=33
EXPLAIN=34
EXTRACT=35
FALSE=36
FIRST=37
FORMAT=38
FROM=39
FULL=40
FUNCTIONS=41
GRAPHVIZ=42
GROUP=43
HAVING=44
HOUR=45
HOURS=46
IN=47
INNER=48
INTERVAL=49
IS=50
JOIN=51
LAST=52
LEFT=53
LIKE=54
LIMIT=55
MAPPED=56
MATCH=57
MINUTE=58
MINUTES=59
MONTH=60
MONTHS=61
NATURAL=62
NOT=63
NULL=64
NULLS=65
ON=66
OPTIMIZED=67
OR=68
ORDER=69
OUTER=70
PARSED=71
PHYSICAL=72
PLAN=73
RIGHT=74
RLIKE=75
QUERY=76
SCHEMAS=77
SECOND=78
SECONDS=79
SELECT=80
SHOW=81
SYS=82
TABLE=83
TABLES=84
TEXT=85
THEN=86
TRUE=87
TO=88
TYPE=89
TYPES=90
USING=91
VERIFY=92
WHEN=93
WHERE=94
WITH=95
YEAR=96
YEARS=97
ESCAPE_ESC=98
FUNCTION_ESC=99
LIMIT_ESC=100
DATE_ESC=101
TIME_ESC=102
TIMESTAMP_ESC=103
GUID_ESC=104
ESC_END=105
EQ=106
NULLEQ=107
NEQ=108
LT=109
LTE=110
GT=111
GTE=112
PLUS=113
MINUS=114
ASTERISK=115
SLASH=116
PERCENT=117
CAST_OP=118
CONCAT=119
DOT=120
PARAM=121
STRING=122
INTEGER_VALUE=123
DECIMAL_VALUE=124
IDENTIFIER=125
DIGIT_IDENTIFIER=126
TABLE_IDENTIFIER=127
QUOTED_IDENTIFIER=128
BACKQUOTED_IDENTIFIER=129
SIMPLE_COMMENT=130
BRACKETED_COMMENT=131
WS=132
UNRECOGNIZED=133
'('=1
')'=2
','=3
@ -139,105 +144,110 @@ UNRECOGNIZED=128
'ASC'=11
'BETWEEN'=12
'BY'=13
'CAST'=14
'CATALOG'=15
'CATALOGS'=16
'COLUMNS'=17
'CONVERT'=18
'CURRENT_DATE'=19
'CURRENT_TIME'=20
'CURRENT_TIMESTAMP'=21
'DAY'=22
'DAYS'=23
'DEBUG'=24
'DESC'=25
'DESCRIBE'=26
'DISTINCT'=27
'ESCAPE'=28
'EXECUTABLE'=29
'EXISTS'=30
'EXPLAIN'=31
'EXTRACT'=32
'FALSE'=33
'FIRST'=34
'FORMAT'=35
'FROM'=36
'FULL'=37
'FUNCTIONS'=38
'GRAPHVIZ'=39
'GROUP'=40
'HAVING'=41
'HOUR'=42
'HOURS'=43
'IN'=44
'INNER'=45
'INTERVAL'=46
'IS'=47
'JOIN'=48
'LAST'=49
'LEFT'=50
'LIKE'=51
'LIMIT'=52
'MAPPED'=53
'MATCH'=54
'MINUTE'=55
'MINUTES'=56
'MONTH'=57
'MONTHS'=58
'NATURAL'=59
'NOT'=60
'NULL'=61
'NULLS'=62
'ON'=63
'OPTIMIZED'=64
'OR'=65
'ORDER'=66
'OUTER'=67
'PARSED'=68
'PHYSICAL'=69
'PLAN'=70
'RIGHT'=71
'RLIKE'=72
'QUERY'=73
'SCHEMAS'=74
'SECOND'=75
'SECONDS'=76
'SELECT'=77
'SHOW'=78
'SYS'=79
'TABLE'=80
'TABLES'=81
'TEXT'=82
'TRUE'=83
'TO'=84
'TYPE'=85
'TYPES'=86
'USING'=87
'VERIFY'=88
'WHERE'=89
'WITH'=90
'YEAR'=91
'YEARS'=92
'{ESCAPE'=93
'{FN'=94
'{LIMIT'=95
'{D'=96
'{T'=97
'{TS'=98
'{GUID'=99
'}'=100
'='=101
'<=>'=102
'<'=104
'<='=105
'>'=106
'>='=107
'+'=108
'-'=109
'*'=110
'/'=111
'%'=112
'::'=113
'||'=114
'.'=115
'?'=116
'CASE'=14
'CAST'=15
'CATALOG'=16
'CATALOGS'=17
'COLUMNS'=18
'CONVERT'=19
'CURRENT_DATE'=20
'CURRENT_TIME'=21
'CURRENT_TIMESTAMP'=22
'DAY'=23
'DAYS'=24
'DEBUG'=25
'DESC'=26
'DESCRIBE'=27
'DISTINCT'=28
'ELSE'=29
'END'=30
'ESCAPE'=31
'EXECUTABLE'=32
'EXISTS'=33
'EXPLAIN'=34
'EXTRACT'=35
'FALSE'=36
'FIRST'=37
'FORMAT'=38
'FROM'=39
'FULL'=40
'FUNCTIONS'=41
'GRAPHVIZ'=42
'GROUP'=43
'HAVING'=44
'HOUR'=45
'HOURS'=46
'IN'=47
'INNER'=48
'INTERVAL'=49
'IS'=50
'JOIN'=51
'LAST'=52
'LEFT'=53
'LIKE'=54
'LIMIT'=55
'MAPPED'=56
'MATCH'=57
'MINUTE'=58
'MINUTES'=59
'MONTH'=60
'MONTHS'=61
'NATURAL'=62
'NOT'=63
'NULL'=64
'NULLS'=65
'ON'=66
'OPTIMIZED'=67
'OR'=68
'ORDER'=69
'OUTER'=70
'PARSED'=71
'PHYSICAL'=72
'PLAN'=73
'RIGHT'=74
'RLIKE'=75
'QUERY'=76
'SCHEMAS'=77
'SECOND'=78
'SECONDS'=79
'SELECT'=80
'SHOW'=81
'SYS'=82
'TABLE'=83
'TABLES'=84
'TEXT'=85
'THEN'=86
'TRUE'=87
'TO'=88
'TYPE'=89
'TYPES'=90
'USING'=91
'VERIFY'=92
'WHEN'=93
'WHERE'=94
'WITH'=95
'YEAR'=96
'YEARS'=97
'{ESCAPE'=98
'{FN'=99
'{LIMIT'=100
'{D'=101
'{T'=102
'{TS'=103
'{GUID'=104
'}'=105
'='=106
'<=>'=107
'<'=109
'<='=110
'>'=111
'>='=112
'+'=113
'-'=114
'*'=115
'/'=116
'%'=117
'::'=118
'||'=119
'.'=120
'?'=121

View File

@ -94,6 +94,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.string.Right;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Space;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Substring;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.UCase;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Case;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Coalesce;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Greatest;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IfNull;
@ -169,7 +170,8 @@ public class FunctionRegistry {
addToMap(def(Histogram.class, Histogram::new, "HISTOGRAM"));
// Scalar functions
// Conditional
addToMap(def(Coalesce.class, Coalesce::new, "COALESCE"),
addToMap(def(Case.class, Case::new, "CASE"),
def(Coalesce.class, Coalesce::new, "COALESCE"),
def(IfNull.class, IfNull::new, "IFNULL", "ISNULL", "NVL"),
def(NullIf.class, NullIf::new, "NULLIF"),
def(Greatest.class, Greatest::new, "GREATEST"),

View File

@ -28,6 +28,7 @@ import org.elasticsearch.xpack.sql.expression.gen.processor.ChainingProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.ConstantProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.HitExtractorProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.CaseProcessor;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.ConditionalProcessor;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.NullIfProcessor;
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor;
@ -62,7 +63,8 @@ public final class Processors {
// logical
entries.add(new Entry(Processor.class, BinaryLogicProcessor.NAME, BinaryLogicProcessor::new));
entries.add(new Entry(Processor.class, NotProcessor.NAME, NotProcessor::new));
// null
// conditionals
entries.add(new Entry(Processor.class, CaseProcessor.NAME, CaseProcessor::new));
entries.add(new Entry(Processor.class, CheckNullProcessor.NAME, CheckNullProcessor::new));
entries.add(new Entry(Processor.class, ConditionalProcessor.NAME, ConditionalProcessor::new));
entries.add(new Entry(Processor.class, NullIfProcessor.NAME, NullIfProcessor::new));

View File

@ -26,6 +26,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.string.StringProce
import org.elasticsearch.xpack.sql.expression.function.scalar.string.SubstringFunctionProcessor;
import org.elasticsearch.xpack.sql.expression.literal.IntervalDayTime;
import org.elasticsearch.xpack.sql.expression.literal.IntervalYearMonth;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.CaseProcessor;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.ConditionalProcessor.ConditionalOperation;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.NullIfProcessor;
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
@ -146,8 +147,12 @@ public final class InternalSqlScriptUtils {
}
//
// Null
// Conditional
//
public static Object caseFunction(List<Object> expressions) {
return CaseProcessor.apply(expressions);
}
public static Object coalesce(List<Object> expressions) {
return ConditionalOperation.COALESCE.apply(expressions);
}

View File

@ -0,0 +1,164 @@
/*
* 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.predicate.conditional;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.Expressions;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
import org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.optimizer.Optimizer;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.tree.Source;
import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.type.DataTypeConversion;
import org.elasticsearch.xpack.sql.type.DataTypes;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
/**
* Implements the CASE WHEN ... THEN ... ELSE ... END expression
*/
public class Case extends ConditionalFunction {
private final List<IfConditional> conditions;
private final Expression defaultElse;
@SuppressWarnings("unchecked")
public Case(Source source, List<Expression> expressions) {
super(source, expressions);
this.conditions = (List<IfConditional>) (List<?>) expressions.subList(0, expressions.size() - 1);
this.defaultElse = expressions.get(expressions.size() - 1);
}
public List<IfConditional> conditions() {
return conditions;
}
public Expression defaultElse() {
return defaultElse;
}
@Override
public DataType dataType() {
if (dataType == null) {
if (conditions.isEmpty()) {
dataType = defaultElse().dataType();
} else {
dataType = DataType.NULL;
for (IfConditional conditional : conditions) {
dataType = DataTypeConversion.commonType(dataType, conditional.dataType());
}
}
}
return dataType;
}
@Override
public Expression replaceChildren(List<Expression> newChildren) {
return new Case(source(), newChildren);
}
@Override
protected NodeInfo<? extends Expression> info() {
return NodeInfo.create(this, Case::new, children());
}
@Override
protected TypeResolution resolveType() {
DataType expectedResultDataType = null;
for (IfConditional ifConditional : conditions) {
if (DataTypes.isNull(ifConditional.result().dataType()) == false) {
expectedResultDataType = ifConditional.result().dataType();
break;
}
}
if (expectedResultDataType == null) {
expectedResultDataType = defaultElse().dataType();
}
for (IfConditional conditional : conditions) {
if (conditional.condition().dataType() != DataType.BOOLEAN) {
return new TypeResolution(format(null, "condition of [{}] must be [boolean], found value [{}] type [{}]",
conditional.sourceText(),
Expressions.name(conditional.condition()),
conditional.condition().dataType().typeName));
}
if (DataTypes.areTypesCompatible(expectedResultDataType, conditional.dataType()) == false) {
return new TypeResolution(format(null, "result of [{}] must be [{}], found value [{}] type [{}]",
conditional.sourceText(),
expectedResultDataType.typeName,
Expressions.name(conditional.result()),
conditional.dataType().typeName));
}
}
if (DataTypes.areTypesCompatible(expectedResultDataType, defaultElse.dataType()) == false) {
return new TypeResolution(format(null, "ELSE clause of [{}] must be [{}], found value [{}] type [{}]",
defaultElse.sourceText(),
expectedResultDataType.typeName,
Expressions.name(defaultElse),
defaultElse.dataType().typeName));
}
return TypeResolution.TYPE_RESOLVED;
}
/**
* All foldable conditions that fold to FALSE should have
* been removed by the {@link Optimizer}.
*/
@Override
public boolean foldable() {
return (conditions.isEmpty() && defaultElse.foldable()) ||
(conditions.size() == 1 && conditions.get(0).condition().foldable() && conditions.get(0).result().foldable());
}
@Override
public Object fold() {
if (conditions.isEmpty() == false && conditions.get(0).condition().fold() == Boolean.TRUE) {
return conditions.get(0).result().fold();
}
return defaultElse.fold();
}
@Override
protected Pipe makePipe() {
List<Pipe> pipes = new ArrayList<>(conditions.size() + 1);
for (IfConditional ifConditional : conditions) {
pipes.add(Expressions.pipe(ifConditional.condition()));
pipes.add(Expressions.pipe(ifConditional.result()));
}
pipes.add(Expressions.pipe(defaultElse));
return new CasePipe(source(), this, pipes);
}
@Override
public ScriptTemplate asScript() {
List<ScriptTemplate> templates = new ArrayList<>();
for (IfConditional ifConditional : conditions) {
templates.add(asScript(ifConditional.condition()));
templates.add(asScript(ifConditional.result()));
}
templates.add(asScript(defaultElse));
StringJoiner template = new StringJoiner(",", "{sql}.caseFunction([", "])");
ParamsBuilder params = paramsBuilder();
for (ScriptTemplate scriptTemplate : templates) {
template.add(scriptTemplate.template());
params.script(scriptTemplate.params());
}
return new ScriptTemplate(formatTemplate(template.toString()), params.build(), dataType());
}
}

View File

@ -0,0 +1,38 @@
/*
* 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.predicate.conditional;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.MultiPipe;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.tree.Source;
import java.util.List;
public class CasePipe extends MultiPipe {
public CasePipe(Source source, Expression expression, List<Pipe> children) {
super(source, expression, children);
}
@Override
protected NodeInfo<CasePipe> info() {
return NodeInfo.create(this, CasePipe::new, expression(), children());
}
@Override
public Pipe replaceChildren(List<Pipe> newChildren) {
return new CasePipe(source(), expression(), newChildren);
}
@Override
public Processor asProcessor(List<Processor> procs) {
return new CaseProcessor(procs);
}
}

View File

@ -0,0 +1,77 @@
/*
* 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.predicate.conditional;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class CaseProcessor implements Processor {
public static final String NAME = "case";
private final List<Processor> processors;
public CaseProcessor(List<Processor> processors) {
this.processors = processors;
}
public CaseProcessor(StreamInput in) throws IOException {
processors = in.readNamedWriteableList(Processor.class);
}
@Override
public String getWriteableName() {
return NAME;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeNamedWriteableList(processors);
}
@Override
public Object process(Object input) {
List<Object> objects = new ArrayList<>(processors.size());
for (Processor processor : processors) {
objects.add(processor.process(input));
}
return apply(objects);
}
public static Object apply(List<Object> objects) {
for (int i = 0; i < objects.size() - 2; i += 2) {
if (objects.get(i) == Boolean.TRUE) {
return objects.get(i + 1);
}
}
// resort to default value
return objects.get(objects.size() - 1);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CaseProcessor that = (CaseProcessor) o;
return Objects.equals(processors, that.processors);
}
@Override
public int hashCode() {
return Objects.hash(processors);
}
}

View File

@ -15,6 +15,7 @@ import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.type.DataTypeConversion;
import java.util.List;
import java.util.Objects;
import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
import static org.elasticsearch.xpack.sql.type.DataTypes.areTypesCompatible;
@ -75,4 +76,21 @@ public abstract class ConditionalFunction extends ScalarFunction {
public Nullability nullable() {
return Nullability.UNKNOWN;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ConditionalFunction that = (ConditionalFunction) o;
return children().equals(that.children());
}
@Override
public int hashCode() {
return Objects.hash(children());
}
}

View File

@ -0,0 +1,92 @@
/*
* 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.predicate.conditional;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.Nullability;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.tree.Source;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* Helper expression (cannot be created directly from a query) to model a
* {@code WHEN <condition> ELSE <result>} clause of {@link Case} expression
*/
public class IfConditional extends Expression {
private final Expression condition;
private final Expression result;
public IfConditional(Source source, Expression condition, Expression result) {
super(source, Arrays.asList(condition, result));
this.condition = condition;
this.result = result;
}
public Expression condition() {
return condition;
}
public Expression result() {
return result;
}
@Override
public Nullability nullable() {
return Nullability.UNKNOWN;
}
@Override
public DataType dataType() {
return result.dataType();
}
@Override
public Expression replaceChildren(List<Expression> newChildren) {
if (newChildren.size() < 2) {
throw new IllegalArgumentException("expected at least [2] children but received [" + newChildren.size() + "]");
}
return new IfConditional(source(), newChildren.get(0), newChildren.get(1));
}
@Override
protected NodeInfo<? extends Expression> info() {
return NodeInfo.create(this, IfConditional::new, condition, result);
}
@Override
protected TypeResolution resolveType() {
// Verification takes place is Case function to be
// able to generate more accurate error messages
return TypeResolution.TYPE_RESOLVED;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
IfConditional that = (IfConditional) o;
return Objects.equals(condition, that.condition) &&
Objects.equals(result, that.result);
}
@Override
public int hashCode() {
return Objects.hash(condition, result);
}
}

View File

@ -48,7 +48,9 @@ import org.elasticsearch.xpack.sql.expression.predicate.Negatable;
import org.elasticsearch.xpack.sql.expression.predicate.Predicates;
import org.elasticsearch.xpack.sql.expression.predicate.Range;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.ArbitraryConditionalFunction;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Case;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Coalesce;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IfConditional;
import org.elasticsearch.xpack.sql.expression.predicate.logical.And;
import org.elasticsearch.xpack.sql.expression.predicate.logical.Not;
import org.elasticsearch.xpack.sql.expression.predicate.logical.Or;
@ -127,6 +129,7 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
new FoldNull(),
new ConstantFolding(),
new SimplifyConditional(),
new SimplifyCase(),
// boolean
new BooleanSimplification(),
new BooleanLiteralsOnTheRight(),
@ -1227,6 +1230,40 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
}
}
static class SimplifyCase extends OptimizerExpressionRule {
SimplifyCase() {
super(TransformDirection.DOWN);
}
@Override
protected Expression rule(Expression e) {
if (e instanceof Case) {
Case c = (Case) e;
// Remove or foldable conditions that fold to FALSE
// Stop at the 1st foldable condition that folds to TRUE
List<IfConditional> newConditions = new ArrayList<>();
for (IfConditional conditional : c.conditions()) {
if (conditional.condition().foldable()) {
Boolean res = (Boolean) conditional.condition().fold();
if (res == Boolean.TRUE) {
newConditions.add(conditional);
break;
}
} else {
newConditions.add(conditional);
}
}
if (newConditions.size() < c.children().size()) {
return c.replaceChildren(combine(newConditions, c.defaultElse()));
}
}
return e;
}
}
static class BooleanSimplification extends OptimizerExpressionRule {

View File

@ -32,6 +32,8 @@ import org.elasticsearch.xpack.sql.expression.literal.IntervalYearMonth;
import org.elasticsearch.xpack.sql.expression.literal.Intervals;
import org.elasticsearch.xpack.sql.expression.literal.Intervals.TimeUnit;
import org.elasticsearch.xpack.sql.expression.predicate.Range;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Case;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IfConditional;
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MatchQueryPredicate;
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MultiMatchQueryPredicate;
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.StringQueryPredicate;
@ -116,6 +118,7 @@ import java.time.Duration;
import java.time.Period;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAmount;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
@ -459,6 +462,25 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
return new ScalarSubquery(source(ctx), plan(ctx.query()));
}
@Override
public Object visitCase(SqlBaseParser.CaseContext ctx) {
List<Expression> expressions = new ArrayList<>(ctx.whenClause().size());
for (SqlBaseParser.WhenClauseContext when : ctx.whenClause()) {
if (ctx.operand != null) {
expressions.add(new IfConditional(source(when),
new Equals(source(when), expression(ctx.operand), expression(when.condition)), expression(when.result)));
} else {
expressions.add(new IfConditional(source(when), expression(when.condition), expression(when.result)));
}
}
if (ctx.elseClause != null) {
expressions.add(expression(ctx.elseClause));
} else {
expressions.add(Literal.NULL);
}
return new Case(source(ctx), expressions);
}
@Override
public Expression visitParenthesizedExpression(ParenthesizedExpressionContext ctx) {
return expression(ctx.expression());

View File

@ -743,6 +743,18 @@ class SqlBaseBaseListener implements SqlBaseListener {
* <p>The default implementation does nothing.</p>
*/
@Override public void exitSubqueryExpression(SqlBaseParser.SubqueryExpressionContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterCase(SqlBaseParser.CaseContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitCase(SqlBaseParser.CaseContext ctx) { }
/**
* {@inheritDoc}
*
@ -1151,6 +1163,18 @@ class SqlBaseBaseListener implements SqlBaseListener {
* <p>The default implementation does nothing.</p>
*/
@Override public void exitString(SqlBaseParser.StringContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterWhenClause(SqlBaseParser.WhenClauseContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitWhenClause(SqlBaseParser.WhenClauseContext ctx) { }
/**
* {@inheritDoc}
*

View File

@ -438,6 +438,13 @@ class SqlBaseBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements SqlBa
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitSubqueryExpression(SqlBaseParser.SubqueryExpressionContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitCase(SqlBaseParser.CaseContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
@ -676,6 +683,13 @@ class SqlBaseBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements SqlBa
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitString(SqlBaseParser.StringContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitWhenClause(SqlBaseParser.WhenClauseContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*

View File

@ -1,13 +1,16 @@
// ANTLR GENERATED CODE: DO NOT EDIT
package org.elasticsearch.xpack.sql.parser;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.RuntimeMetaData;
import org.antlr.v4.runtime.Vocabulary;
import org.antlr.v4.runtime.VocabularyImpl;
import org.antlr.v4.runtime.atn.ATN;
import org.antlr.v4.runtime.atn.ATNDeserializer;
import org.antlr.v4.runtime.atn.LexerATNSimulator;
import org.antlr.v4.runtime.atn.PredictionContextCache;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.*;
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
class SqlBaseLexer extends Lexer {
@ -18,88 +21,90 @@ class SqlBaseLexer extends Lexer {
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, CATALOG=15, CATALOGS=16, COLUMNS=17,
CONVERT=18, CURRENT_DATE=19, CURRENT_TIME=20, CURRENT_TIMESTAMP=21, DAY=22,
DAYS=23, DEBUG=24, DESC=25, DESCRIBE=26, DISTINCT=27, ESCAPE=28, EXECUTABLE=29,
EXISTS=30, EXPLAIN=31, EXTRACT=32, FALSE=33, FIRST=34, FORMAT=35, FROM=36,
FULL=37, FUNCTIONS=38, GRAPHVIZ=39, GROUP=40, HAVING=41, HOUR=42, HOURS=43,
IN=44, INNER=45, INTERVAL=46, IS=47, JOIN=48, LAST=49, LEFT=50, LIKE=51,
LIMIT=52, MAPPED=53, MATCH=54, MINUTE=55, MINUTES=56, MONTH=57, MONTHS=58,
NATURAL=59, NOT=60, NULL=61, NULLS=62, ON=63, OPTIMIZED=64, OR=65, ORDER=66,
OUTER=67, PARSED=68, PHYSICAL=69, PLAN=70, RIGHT=71, RLIKE=72, QUERY=73,
SCHEMAS=74, SECOND=75, SECONDS=76, SELECT=77, SHOW=78, SYS=79, TABLE=80,
TABLES=81, TEXT=82, TRUE=83, TO=84, TYPE=85, TYPES=86, USING=87, VERIFY=88,
WHERE=89, WITH=90, YEAR=91, YEARS=92, ESCAPE_ESC=93, FUNCTION_ESC=94,
LIMIT_ESC=95, DATE_ESC=96, TIME_ESC=97, TIMESTAMP_ESC=98, GUID_ESC=99,
ESC_END=100, EQ=101, NULLEQ=102, NEQ=103, LT=104, LTE=105, GT=106, GTE=107,
PLUS=108, MINUS=109, ASTERISK=110, SLASH=111, PERCENT=112, CAST_OP=113,
CONCAT=114, DOT=115, PARAM=116, STRING=117, INTEGER_VALUE=118, DECIMAL_VALUE=119,
IDENTIFIER=120, DIGIT_IDENTIFIER=121, TABLE_IDENTIFIER=122, QUOTED_IDENTIFIER=123,
BACKQUOTED_IDENTIFIER=124, SIMPLE_COMMENT=125, BRACKETED_COMMENT=126,
WS=127, UNRECOGNIZED=128;
AS=10, ASC=11, BETWEEN=12, BY=13, CASE=14, CAST=15, CATALOG=16, CATALOGS=17,
COLUMNS=18, CONVERT=19, CURRENT_DATE=20, CURRENT_TIME=21, CURRENT_TIMESTAMP=22,
DAY=23, DAYS=24, DEBUG=25, DESC=26, DESCRIBE=27, DISTINCT=28, ELSE=29,
END=30, ESCAPE=31, EXECUTABLE=32, EXISTS=33, EXPLAIN=34, EXTRACT=35, FALSE=36,
FIRST=37, FORMAT=38, FROM=39, FULL=40, FUNCTIONS=41, GRAPHVIZ=42, GROUP=43,
HAVING=44, HOUR=45, HOURS=46, IN=47, INNER=48, INTERVAL=49, IS=50, JOIN=51,
LAST=52, LEFT=53, LIKE=54, LIMIT=55, MAPPED=56, MATCH=57, MINUTE=58, MINUTES=59,
MONTH=60, MONTHS=61, NATURAL=62, NOT=63, NULL=64, NULLS=65, ON=66, OPTIMIZED=67,
OR=68, ORDER=69, OUTER=70, PARSED=71, PHYSICAL=72, PLAN=73, RIGHT=74,
RLIKE=75, QUERY=76, SCHEMAS=77, SECOND=78, SECONDS=79, SELECT=80, SHOW=81,
SYS=82, TABLE=83, TABLES=84, TEXT=85, THEN=86, TRUE=87, TO=88, TYPE=89,
TYPES=90, USING=91, VERIFY=92, WHEN=93, WHERE=94, WITH=95, YEAR=96, YEARS=97,
ESCAPE_ESC=98, FUNCTION_ESC=99, LIMIT_ESC=100, DATE_ESC=101, TIME_ESC=102,
TIMESTAMP_ESC=103, GUID_ESC=104, ESC_END=105, EQ=106, NULLEQ=107, NEQ=108,
LT=109, LTE=110, GT=111, GTE=112, PLUS=113, MINUS=114, ASTERISK=115, SLASH=116,
PERCENT=117, CAST_OP=118, CONCAT=119, DOT=120, PARAM=121, STRING=122,
INTEGER_VALUE=123, DECIMAL_VALUE=124, IDENTIFIER=125, DIGIT_IDENTIFIER=126,
TABLE_IDENTIFIER=127, QUOTED_IDENTIFIER=128, BACKQUOTED_IDENTIFIER=129,
SIMPLE_COMMENT=130, BRACKETED_COMMENT=131, WS=132, UNRECOGNIZED=133;
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", "CATALOG", "CATALOGS", "COLUMNS",
"AS", "ASC", "BETWEEN", "BY", "CASE", "CAST", "CATALOG", "CATALOGS", "COLUMNS",
"CONVERT", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "DAY",
"DAYS", "DEBUG", "DESC", "DESCRIBE", "DISTINCT", "ESCAPE", "EXECUTABLE",
"EXISTS", "EXPLAIN", "EXTRACT", "FALSE", "FIRST", "FORMAT", "FROM", "FULL",
"FUNCTIONS", "GRAPHVIZ", "GROUP", "HAVING", "HOUR", "HOURS", "IN", "INNER",
"INTERVAL", "IS", "JOIN", "LAST", "LEFT", "LIKE", "LIMIT", "MAPPED", "MATCH",
"MINUTE", "MINUTES", "MONTH", "MONTHS", "NATURAL", "NOT", "NULL", "NULLS",
"ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED", "PHYSICAL", "PLAN",
"RIGHT", "RLIKE", "QUERY", "SCHEMAS", "SECOND", "SECONDS", "SELECT", "SHOW",
"SYS", "TABLE", "TABLES", "TEXT", "TRUE", "TO", "TYPE", "TYPES", "USING",
"VERIFY", "WHERE", "WITH", "YEAR", "YEARS", "ESCAPE_ESC", "FUNCTION_ESC",
"LIMIT_ESC", "DATE_ESC", "TIME_ESC", "TIMESTAMP_ESC", "GUID_ESC", "ESC_END",
"EQ", "NULLEQ", "NEQ", "LT", "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK",
"SLASH", "PERCENT", "CAST_OP", "CONCAT", "DOT", "PARAM", "STRING", "INTEGER_VALUE",
"DECIMAL_VALUE", "IDENTIFIER", "DIGIT_IDENTIFIER", "TABLE_IDENTIFIER",
"QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", "EXPONENT", "DIGIT", "LETTER",
"SIMPLE_COMMENT", "BRACKETED_COMMENT", "WS", "UNRECOGNIZED"
"DAYS", "DEBUG", "DESC", "DESCRIBE", "DISTINCT", "ELSE", "END", "ESCAPE",
"EXECUTABLE", "EXISTS", "EXPLAIN", "EXTRACT", "FALSE", "FIRST", "FORMAT",
"FROM", "FULL", "FUNCTIONS", "GRAPHVIZ", "GROUP", "HAVING", "HOUR", "HOURS",
"IN", "INNER", "INTERVAL", "IS", "JOIN", "LAST", "LEFT", "LIKE", "LIMIT",
"MAPPED", "MATCH", "MINUTE", "MINUTES", "MONTH", "MONTHS", "NATURAL",
"NOT", "NULL", "NULLS", "ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED",
"PHYSICAL", "PLAN", "RIGHT", "RLIKE", "QUERY", "SCHEMAS", "SECOND", "SECONDS",
"SELECT", "SHOW", "SYS", "TABLE", "TABLES", "TEXT", "THEN", "TRUE", "TO",
"TYPE", "TYPES", "USING", "VERIFY", "WHEN", "WHERE", "WITH", "YEAR", "YEARS",
"ESCAPE_ESC", "FUNCTION_ESC", "LIMIT_ESC", "DATE_ESC", "TIME_ESC", "TIMESTAMP_ESC",
"GUID_ESC", "ESC_END", "EQ", "NULLEQ", "NEQ", "LT", "LTE", "GT", "GTE",
"PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "CAST_OP", "CONCAT",
"DOT", "PARAM", "STRING", "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER",
"DIGIT_IDENTIFIER", "TABLE_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'", "'CATALOG'",
"'CATALOGS'", "'COLUMNS'", "'CONVERT'", "'CURRENT_DATE'", "'CURRENT_TIME'",
"'CURRENT_TIMESTAMP'", "'DAY'", "'DAYS'", "'DEBUG'", "'DESC'", "'DESCRIBE'",
"'DISTINCT'", "'ESCAPE'", "'EXECUTABLE'", "'EXISTS'", "'EXPLAIN'", "'EXTRACT'",
"'FALSE'", "'FIRST'", "'FORMAT'", "'FROM'", "'FULL'", "'FUNCTIONS'", "'GRAPHVIZ'",
"'GROUP'", "'HAVING'", "'HOUR'", "'HOURS'", "'IN'", "'INNER'", "'INTERVAL'",
"'IS'", "'JOIN'", "'LAST'", "'LEFT'", "'LIKE'", "'LIMIT'", "'MAPPED'",
"'MATCH'", "'MINUTE'", "'MINUTES'", "'MONTH'", "'MONTHS'", "'NATURAL'",
"'NOT'", "'NULL'", "'NULLS'", "'ON'", "'OPTIMIZED'", "'OR'", "'ORDER'",
"'OUTER'", "'PARSED'", "'PHYSICAL'", "'PLAN'", "'RIGHT'", "'RLIKE'", "'QUERY'",
"'SCHEMAS'", "'SECOND'", "'SECONDS'", "'SELECT'", "'SHOW'", "'SYS'", "'TABLE'",
"'TABLES'", "'TEXT'", "'TRUE'", "'TO'", "'TYPE'", "'TYPES'", "'USING'",
"'VERIFY'", "'WHERE'", "'WITH'", "'YEAR'", "'YEARS'", "'{ESCAPE'", "'{FN'",
"'{LIMIT'", "'{D'", "'{T'", "'{TS'", "'{GUID'", "'}'", "'='", "'<=>'",
null, "'<'", "'<='", "'>'", "'>='", "'+'", "'-'", "'*'", "'/'", "'%'",
"'::'", "'||'", "'.'", "'?'"
"'AND'", "'ANY'", "'AS'", "'ASC'", "'BETWEEN'", "'BY'", "'CASE'", "'CAST'",
"'CATALOG'", "'CATALOGS'", "'COLUMNS'", "'CONVERT'", "'CURRENT_DATE'",
"'CURRENT_TIME'", "'CURRENT_TIMESTAMP'", "'DAY'", "'DAYS'", "'DEBUG'",
"'DESC'", "'DESCRIBE'", "'DISTINCT'", "'ELSE'", "'END'", "'ESCAPE'", "'EXECUTABLE'",
"'EXISTS'", "'EXPLAIN'", "'EXTRACT'", "'FALSE'", "'FIRST'", "'FORMAT'",
"'FROM'", "'FULL'", "'FUNCTIONS'", "'GRAPHVIZ'", "'GROUP'", "'HAVING'",
"'HOUR'", "'HOURS'", "'IN'", "'INNER'", "'INTERVAL'", "'IS'", "'JOIN'",
"'LAST'", "'LEFT'", "'LIKE'", "'LIMIT'", "'MAPPED'", "'MATCH'", "'MINUTE'",
"'MINUTES'", "'MONTH'", "'MONTHS'", "'NATURAL'", "'NOT'", "'NULL'", "'NULLS'",
"'ON'", "'OPTIMIZED'", "'OR'", "'ORDER'", "'OUTER'", "'PARSED'", "'PHYSICAL'",
"'PLAN'", "'RIGHT'", "'RLIKE'", "'QUERY'", "'SCHEMAS'", "'SECOND'", "'SECONDS'",
"'SELECT'", "'SHOW'", "'SYS'", "'TABLE'", "'TABLES'", "'TEXT'", "'THEN'",
"'TRUE'", "'TO'", "'TYPE'", "'TYPES'", "'USING'", "'VERIFY'", "'WHEN'",
"'WHERE'", "'WITH'", "'YEAR'", "'YEARS'", "'{ESCAPE'", "'{FN'", "'{LIMIT'",
"'{D'", "'{T'", "'{TS'", "'{GUID'", "'}'", "'='", "'<=>'", null, "'<'",
"'<='", "'>'", "'>='", "'+'", "'-'", "'*'", "'/'", "'%'", "'::'", "'||'",
"'.'", "'?'"
};
private static final String[] _SYMBOLIC_NAMES = {
null, null, null, null, null, "ALL", "ANALYZE", "ANALYZED", "AND", "ANY",
"AS", "ASC", "BETWEEN", "BY", "CAST", "CATALOG", "CATALOGS", "COLUMNS",
"AS", "ASC", "BETWEEN", "BY", "CASE", "CAST", "CATALOG", "CATALOGS", "COLUMNS",
"CONVERT", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "DAY",
"DAYS", "DEBUG", "DESC", "DESCRIBE", "DISTINCT", "ESCAPE", "EXECUTABLE",
"EXISTS", "EXPLAIN", "EXTRACT", "FALSE", "FIRST", "FORMAT", "FROM", "FULL",
"FUNCTIONS", "GRAPHVIZ", "GROUP", "HAVING", "HOUR", "HOURS", "IN", "INNER",
"INTERVAL", "IS", "JOIN", "LAST", "LEFT", "LIKE", "LIMIT", "MAPPED", "MATCH",
"MINUTE", "MINUTES", "MONTH", "MONTHS", "NATURAL", "NOT", "NULL", "NULLS",
"ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED", "PHYSICAL", "PLAN",
"RIGHT", "RLIKE", "QUERY", "SCHEMAS", "SECOND", "SECONDS", "SELECT", "SHOW",
"SYS", "TABLE", "TABLES", "TEXT", "TRUE", "TO", "TYPE", "TYPES", "USING",
"VERIFY", "WHERE", "WITH", "YEAR", "YEARS", "ESCAPE_ESC", "FUNCTION_ESC",
"LIMIT_ESC", "DATE_ESC", "TIME_ESC", "TIMESTAMP_ESC", "GUID_ESC", "ESC_END",
"EQ", "NULLEQ", "NEQ", "LT", "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK",
"SLASH", "PERCENT", "CAST_OP", "CONCAT", "DOT", "PARAM", "STRING", "INTEGER_VALUE",
"DECIMAL_VALUE", "IDENTIFIER", "DIGIT_IDENTIFIER", "TABLE_IDENTIFIER",
"QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", "SIMPLE_COMMENT", "BRACKETED_COMMENT",
"WS", "UNRECOGNIZED"
"DAYS", "DEBUG", "DESC", "DESCRIBE", "DISTINCT", "ELSE", "END", "ESCAPE",
"EXECUTABLE", "EXISTS", "EXPLAIN", "EXTRACT", "FALSE", "FIRST", "FORMAT",
"FROM", "FULL", "FUNCTIONS", "GRAPHVIZ", "GROUP", "HAVING", "HOUR", "HOURS",
"IN", "INNER", "INTERVAL", "IS", "JOIN", "LAST", "LEFT", "LIKE", "LIMIT",
"MAPPED", "MATCH", "MINUTE", "MINUTES", "MONTH", "MONTHS", "NATURAL",
"NOT", "NULL", "NULLS", "ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED",
"PHYSICAL", "PLAN", "RIGHT", "RLIKE", "QUERY", "SCHEMAS", "SECOND", "SECONDS",
"SELECT", "SHOW", "SYS", "TABLE", "TABLES", "TEXT", "THEN", "TRUE", "TO",
"TYPE", "TYPES", "USING", "VERIFY", "WHEN", "WHERE", "WITH", "YEAR", "YEARS",
"ESCAPE_ESC", "FUNCTION_ESC", "LIMIT_ESC", "DATE_ESC", "TIME_ESC", "TIMESTAMP_ESC",
"GUID_ESC", "ESC_END", "EQ", "NULLEQ", "NEQ", "LT", "LTE", "GT", "GTE",
"PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "CAST_OP", "CONCAT",
"DOT", "PARAM", "STRING", "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER",
"DIGIT_IDENTIFIER", "TABLE_IDENTIFIER", "QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER",
"SIMPLE_COMMENT", "BRACKETED_COMMENT", "WS", "UNRECOGNIZED"
};
public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
@ -156,7 +161,7 @@ class SqlBaseLexer extends Lexer {
public ATN getATN() { return _ATN; }
public static final String _serializedATN =
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2\u0082\u043c\b\1\4"+
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2\u0087\u045e\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"+
@ -171,55 +176,60 @@ class SqlBaseLexer extends Lexer {
"\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{\4|\t|\4}\t}\4~\t~\4\177\t\177\4\u0080\t"+
"\u0080\4\u0081\t\u0081\4\u0082\t\u0082\4\u0083\t\u0083\4\u0084\t\u0084"+
"\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\21\3\21\3\21\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"+
"\24\3\24\3\24\3\24\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\25\3\25\3\26\3\26\3"+
"\26\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3"+
"\26\3\26\3\27\3\27\3\27\3\27\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\33\3\33\3\33\3\33\3\33\3\33\3"+
"\33\3\33\3\33\3\34\3\34\3\34\3\34\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\36\3\36\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\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,\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\61\3\61\3\61\3\61\3\61"+
"\3\62\3\62\3\62\3\62\3\62\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\66"+
"\3\67\3\67\3\67\3\67\3\67\3\67\38\38\38\38\38\38\38\39\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@\3A\3A\3A\3A\3A"+
"\3A\3A\3A\3A\3A\3B\3B\3B\3C\3C\3C\3C\3C\3C\3D\3D\3D\3D\3D\3D\3E\3E\3E"+
"\3E\3E\3E\3E\3F\3F\3F\3F\3F\3F\3F\3F\3F\3G\3G\3G\3G\3G\3H\3H\3H\3H\3H"+
"\3H\3I\3I\3I\3I\3I\3I\3J\3J\3J\3J\3J\3J\3K\3K\3K\3K\3K\3K\3K\3K\3L\3L"+
"\3L\3L\3L\3L\3L\3M\3M\3M\3M\3M\3M\3M\3M\3N\3N\3N\3N\3N\3N\3N\3O\3O\3O"+
"\3O\3O\3P\3P\3P\3P\3Q\3Q\3Q\3Q\3Q\3Q\3R\3R\3R\3R\3R\3R\3R\3S\3S\3S\3S"+
"\3S\3T\3T\3T\3T\3T\3U\3U\3U\3V\3V\3V\3V\3V\3W\3W\3W\3W\3W\3W\3X\3X\3X"+
"\3X\3X\3X\3Y\3Y\3Y\3Y\3Y\3Y\3Y\3Z\3Z\3Z\3Z\3Z\3Z\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\3b\3b\3b\3c\3c\3c\3c\3d\3d\3d\3d\3d\3d\3"+
"e\3e\3f\3f\3g\3g\3g\3g\3h\3h\3h\3h\5h\u037a\nh\3i\3i\3j\3j\3j\3k\3k\3"+
"l\3l\3l\3m\3m\3n\3n\3o\3o\3p\3p\3q\3q\3r\3r\3r\3s\3s\3s\3t\3t\3u\3u\3"+
"v\3v\3v\3v\7v\u039e\nv\fv\16v\u03a1\13v\3v\3v\3w\6w\u03a6\nw\rw\16w\u03a7"+
"\3x\6x\u03ab\nx\rx\16x\u03ac\3x\3x\7x\u03b1\nx\fx\16x\u03b4\13x\3x\3x"+
"\6x\u03b8\nx\rx\16x\u03b9\3x\6x\u03bd\nx\rx\16x\u03be\3x\3x\7x\u03c3\n"+
"x\fx\16x\u03c6\13x\5x\u03c8\nx\3x\3x\3x\3x\6x\u03ce\nx\rx\16x\u03cf\3"+
"x\3x\5x\u03d4\nx\3y\3y\5y\u03d8\ny\3y\3y\3y\7y\u03dd\ny\fy\16y\u03e0\13"+
"y\3z\3z\3z\3z\6z\u03e6\nz\rz\16z\u03e7\3{\3{\3{\6{\u03ed\n{\r{\16{\u03ee"+
"\3|\3|\3|\3|\7|\u03f5\n|\f|\16|\u03f8\13|\3|\3|\3}\3}\3}\3}\7}\u0400\n"+
"}\f}\16}\u0403\13}\3}\3}\3~\3~\5~\u0409\n~\3~\6~\u040c\n~\r~\16~\u040d"+
"\3\177\3\177\3\u0080\3\u0080\3\u0081\3\u0081\3\u0081\3\u0081\7\u0081\u0418"+
"\n\u0081\f\u0081\16\u0081\u041b\13\u0081\3\u0081\5\u0081\u041e\n\u0081"+
"\3\u0081\5\u0081\u0421\n\u0081\3\u0081\3\u0081\3\u0082\3\u0082\3\u0082"+
"\3\u0082\3\u0082\7\u0082\u042a\n\u0082\f\u0082\16\u0082\u042d\13\u0082"+
"\3\u0082\3\u0082\3\u0082\3\u0082\3\u0082\3\u0083\6\u0083\u0435\n\u0083"+
"\r\u0083\16\u0083\u0436\3\u0083\3\u0083\3\u0084\3\u0084\3\u042b\2\u0085"+
"\4\u0085\t\u0085\4\u0086\t\u0086\4\u0087\t\u0087\4\u0088\t\u0088\4\u0089"+
"\t\u0089\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\21\3\21\3\21\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\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\25\3\25\3\26\3\26\3\26\3\26\3\26\3\26\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\27\3\27"+
"\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\31\3\31"+
"\3\31\3\31\3\31\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\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\36\3\36\3\36\3\36\3\36\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+\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\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\62\3\62\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\66\3\66\3\66\3\66\3\66\3\67\3\67\3\67\3\67\3\67\38\3"+
"8\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?\3?\3@\3@\3@\3@\3A\3A\3A\3A\3A\3B\3B\3B\3B\3B\3B\3"+
"C\3C\3C\3D\3D\3D\3D\3D\3D\3D\3D\3D\3D\3E\3E\3E\3F\3F\3F\3F\3F\3F\3G\3"+
"G\3G\3G\3G\3G\3H\3H\3H\3H\3H\3H\3H\3I\3I\3I\3I\3I\3I\3I\3I\3I\3J\3J\3"+
"J\3J\3J\3K\3K\3K\3K\3K\3K\3L\3L\3L\3L\3L\3L\3M\3M\3M\3M\3M\3M\3N\3N\3"+
"N\3N\3N\3N\3N\3N\3O\3O\3O\3O\3O\3O\3O\3P\3P\3P\3P\3P\3P\3P\3P\3Q\3Q\3"+
"Q\3Q\3Q\3Q\3Q\3R\3R\3R\3R\3R\3S\3S\3S\3S\3T\3T\3T\3T\3T\3T\3U\3U\3U\3"+
"U\3U\3U\3U\3V\3V\3V\3V\3V\3W\3W\3W\3W\3W\3X\3X\3X\3X\3X\3Y\3Y\3Y\3Z\3"+
"Z\3Z\3Z\3Z\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\3"+
"b\3b\3b\3b\3b\3b\3c\3c\3c\3c\3c\3c\3c\3c\3d\3d\3d\3d\3e\3e\3e\3e\3e\3"+
"e\3e\3f\3f\3f\3g\3g\3g\3h\3h\3h\3h\3i\3i\3i\3i\3i\3i\3j\3j\3k\3k\3l\3"+
"l\3l\3l\3m\3m\3m\3m\5m\u039c\nm\3n\3n\3o\3o\3o\3p\3p\3q\3q\3q\3r\3r\3"+
"s\3s\3t\3t\3u\3u\3v\3v\3w\3w\3w\3x\3x\3x\3y\3y\3z\3z\3{\3{\3{\3{\7{\u03c0"+
"\n{\f{\16{\u03c3\13{\3{\3{\3|\6|\u03c8\n|\r|\16|\u03c9\3}\6}\u03cd\n}"+
"\r}\16}\u03ce\3}\3}\7}\u03d3\n}\f}\16}\u03d6\13}\3}\3}\6}\u03da\n}\r}"+
"\16}\u03db\3}\6}\u03df\n}\r}\16}\u03e0\3}\3}\7}\u03e5\n}\f}\16}\u03e8"+
"\13}\5}\u03ea\n}\3}\3}\3}\3}\6}\u03f0\n}\r}\16}\u03f1\3}\3}\5}\u03f6\n"+
"}\3~\3~\5~\u03fa\n~\3~\3~\3~\7~\u03ff\n~\f~\16~\u0402\13~\3\177\3\177"+
"\3\177\3\177\6\177\u0408\n\177\r\177\16\177\u0409\3\u0080\3\u0080\3\u0080"+
"\6\u0080\u040f\n\u0080\r\u0080\16\u0080\u0410\3\u0081\3\u0081\3\u0081"+
"\3\u0081\7\u0081\u0417\n\u0081\f\u0081\16\u0081\u041a\13\u0081\3\u0081"+
"\3\u0081\3\u0082\3\u0082\3\u0082\3\u0082\7\u0082\u0422\n\u0082\f\u0082"+
"\16\u0082\u0425\13\u0082\3\u0082\3\u0082\3\u0083\3\u0083\5\u0083\u042b"+
"\n\u0083\3\u0083\6\u0083\u042e\n\u0083\r\u0083\16\u0083\u042f\3\u0084"+
"\3\u0084\3\u0085\3\u0085\3\u0086\3\u0086\3\u0086\3\u0086\7\u0086\u043a"+
"\n\u0086\f\u0086\16\u0086\u043d\13\u0086\3\u0086\5\u0086\u0440\n\u0086"+
"\3\u0086\5\u0086\u0443\n\u0086\3\u0086\3\u0086\3\u0087\3\u0087\3\u0087"+
"\3\u0087\3\u0087\7\u0087\u044c\n\u0087\f\u0087\16\u0087\u044f\13\u0087"+
"\3\u0087\3\u0087\3\u0087\3\u0087\3\u0087\3\u0088\6\u0088\u0457\n\u0088"+
"\r\u0088\16\u0088\u0458\3\u0088\3\u0088\3\u0089\3\u0089\3\u044d\2\u008a"+
"\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\67m8o"+
@ -229,302 +239,313 @@ class SqlBaseLexer extends Lexer {
"\\\u00b7]\u00b9^\u00bb_\u00bd`\u00bfa\u00c1b\u00c3c\u00c5d\u00c7e\u00c9"+
"f\u00cbg\u00cdh\u00cfi\u00d1j\u00d3k\u00d5l\u00d7m\u00d9n\u00dbo\u00dd"+
"p\u00dfq\u00e1r\u00e3s\u00e5t\u00e7u\u00e9v\u00ebw\u00edx\u00efy\u00f1"+
"z\u00f3{\u00f5|\u00f7}\u00f9~\u00fb\2\u00fd\2\u00ff\2\u0101\177\u0103"+
"\u0080\u0105\u0081\u0107\u0082\3\2\13\3\2))\4\2BBaa\3\2$$\3\2bb\4\2--"+
"//\3\2\62;\3\2C\\\4\2\f\f\17\17\5\2\13\f\17\17\"\"\u045c\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\2"+
"K\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\2"+
"q\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\u00ab\3\2\2\2\2\u00ad\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\2\u00b7\3\2\2\2\2\u00b9\3\2\2\2\2\u00bb\3\2\2"+
"\2\2\u00bd\3\2\2\2\2\u00bf\3\2\2\2\2\u00c1\3\2\2\2\2\u00c3\3\2\2\2\2\u00c5"+
"\3\2\2\2\2\u00c7\3\2\2\2\2\u00c9\3\2\2\2\2\u00cb\3\2\2\2\2\u00cd\3\2\2"+
"\2\2\u00cf\3\2\2\2\2\u00d1\3\2\2\2\2\u00d3\3\2\2\2\2\u00d5\3\2\2\2\2\u00d7"+
"\3\2\2\2\2\u00d9\3\2\2\2\2\u00db\3\2\2\2\2\u00dd\3\2\2\2\2\u00df\3\2\2"+
"\2\2\u00e1\3\2\2\2\2\u00e3\3\2\2\2\2\u00e5\3\2\2\2\2\u00e7\3\2\2\2\2\u00e9"+
"\3\2\2\2\2\u00eb\3\2\2\2\2\u00ed\3\2\2\2\2\u00ef\3\2\2\2\2\u00f1\3\2\2"+
"\2\2\u00f3\3\2\2\2\2\u00f5\3\2\2\2\2\u00f7\3\2\2\2\2\u00f9\3\2\2\2\2\u0101"+
"\3\2\2\2\2\u0103\3\2\2\2\2\u0105\3\2\2\2\2\u0107\3\2\2\2\3\u0109\3\2\2"+
"\2\5\u010b\3\2\2\2\7\u010d\3\2\2\2\t\u010f\3\2\2\2\13\u0111\3\2\2\2\r"+
"\u0115\3\2\2\2\17\u011d\3\2\2\2\21\u0126\3\2\2\2\23\u012a\3\2\2\2\25\u012e"+
"\3\2\2\2\27\u0131\3\2\2\2\31\u0135\3\2\2\2\33\u013d\3\2\2\2\35\u0140\3"+
"\2\2\2\37\u0145\3\2\2\2!\u014d\3\2\2\2#\u0156\3\2\2\2%\u015e\3\2\2\2\'"+
"\u0166\3\2\2\2)\u0173\3\2\2\2+\u0180\3\2\2\2-\u0192\3\2\2\2/\u0196\3\2"+
"\2\2\61\u019b\3\2\2\2\63\u01a1\3\2\2\2\65\u01a6\3\2\2\2\67\u01af\3\2\2"+
"\29\u01b8\3\2\2\2;\u01bf\3\2\2\2=\u01ca\3\2\2\2?\u01d1\3\2\2\2A\u01d9"+
"\3\2\2\2C\u01e1\3\2\2\2E\u01e7\3\2\2\2G\u01ed\3\2\2\2I\u01f4\3\2\2\2K"+
"\u01f9\3\2\2\2M\u01fe\3\2\2\2O\u0208\3\2\2\2Q\u0211\3\2\2\2S\u0217\3\2"+
"\2\2U\u021e\3\2\2\2W\u0223\3\2\2\2Y\u0229\3\2\2\2[\u022c\3\2\2\2]\u0232"+
"\3\2\2\2_\u023b\3\2\2\2a\u023e\3\2\2\2c\u0243\3\2\2\2e\u0248\3\2\2\2g"+
"\u024d\3\2\2\2i\u0252\3\2\2\2k\u0258\3\2\2\2m\u025f\3\2\2\2o\u0265\3\2"+
"\2\2q\u026c\3\2\2\2s\u0274\3\2\2\2u\u027a\3\2\2\2w\u0281\3\2\2\2y\u0289"+
"\3\2\2\2{\u028d\3\2\2\2}\u0292\3\2\2\2\177\u0298\3\2\2\2\u0081\u029b\3"+
"\2\2\2\u0083\u02a5\3\2\2\2\u0085\u02a8\3\2\2\2\u0087\u02ae\3\2\2\2\u0089"+
"\u02b4\3\2\2\2\u008b\u02bb\3\2\2\2\u008d\u02c4\3\2\2\2\u008f\u02c9\3\2"+
"\2\2\u0091\u02cf\3\2\2\2\u0093\u02d5\3\2\2\2\u0095\u02db\3\2\2\2\u0097"+
"\u02e3\3\2\2\2\u0099\u02ea\3\2\2\2\u009b\u02f2\3\2\2\2\u009d\u02f9\3\2"+
"\2\2\u009f\u02fe\3\2\2\2\u00a1\u0302\3\2\2\2\u00a3\u0308\3\2\2\2\u00a5"+
"\u030f\3\2\2\2\u00a7\u0314\3\2\2\2\u00a9\u0319\3\2\2\2\u00ab\u031c\3\2"+
"\2\2\u00ad\u0321\3\2\2\2\u00af\u0327\3\2\2\2\u00b1\u032d\3\2\2\2\u00b3"+
"\u0334\3\2\2\2\u00b5\u033a\3\2\2\2\u00b7\u033f\3\2\2\2\u00b9\u0344\3\2"+
"\2\2\u00bb\u034a\3\2\2\2\u00bd\u0352\3\2\2\2\u00bf\u0356\3\2\2\2\u00c1"+
"\u035d\3\2\2\2\u00c3\u0360\3\2\2\2\u00c5\u0363\3\2\2\2\u00c7\u0367\3\2"+
"\2\2\u00c9\u036d\3\2\2\2\u00cb\u036f\3\2\2\2\u00cd\u0371\3\2\2\2\u00cf"+
"\u0379\3\2\2\2\u00d1\u037b\3\2\2\2\u00d3\u037d\3\2\2\2\u00d5\u0380\3\2"+
"\2\2\u00d7\u0382\3\2\2\2\u00d9\u0385\3\2\2\2\u00db\u0387\3\2\2\2\u00dd"+
"\u0389\3\2\2\2\u00df\u038b\3\2\2\2\u00e1\u038d\3\2\2\2\u00e3\u038f\3\2"+
"\2\2\u00e5\u0392\3\2\2\2\u00e7\u0395\3\2\2\2\u00e9\u0397\3\2\2\2\u00eb"+
"\u0399\3\2\2\2\u00ed\u03a5\3\2\2\2\u00ef\u03d3\3\2\2\2\u00f1\u03d7\3\2"+
"\2\2\u00f3\u03e1\3\2\2\2\u00f5\u03ec\3\2\2\2\u00f7\u03f0\3\2\2\2\u00f9"+
"\u03fb\3\2\2\2\u00fb\u0406\3\2\2\2\u00fd\u040f\3\2\2\2\u00ff\u0411\3\2"+
"\2\2\u0101\u0413\3\2\2\2\u0103\u0424\3\2\2\2\u0105\u0434\3\2\2\2\u0107"+
"\u043a\3\2\2\2\u0109\u010a\7*\2\2\u010a\4\3\2\2\2\u010b\u010c\7+\2\2\u010c"+
"\6\3\2\2\2\u010d\u010e\7.\2\2\u010e\b\3\2\2\2\u010f\u0110\7<\2\2\u0110"+
"\n\3\2\2\2\u0111\u0112\7C\2\2\u0112\u0113\7N\2\2\u0113\u0114\7N\2\2\u0114"+
"\f\3\2\2\2\u0115\u0116\7C\2\2\u0116\u0117\7P\2\2\u0117\u0118\7C\2\2\u0118"+
"\u0119\7N\2\2\u0119\u011a\7[\2\2\u011a\u011b\7\\\2\2\u011b\u011c\7G\2"+
"\2\u011c\16\3\2\2\2\u011d\u011e\7C\2\2\u011e\u011f\7P\2\2\u011f\u0120"+
"\7C\2\2\u0120\u0121\7N\2\2\u0121\u0122\7[\2\2\u0122\u0123\7\\\2\2\u0123"+
"\u0124\7G\2\2\u0124\u0125\7F\2\2\u0125\20\3\2\2\2\u0126\u0127\7C\2\2\u0127"+
"\u0128\7P\2\2\u0128\u0129\7F\2\2\u0129\22\3\2\2\2\u012a\u012b\7C\2\2\u012b"+
"\u012c\7P\2\2\u012c\u012d\7[\2\2\u012d\24\3\2\2\2\u012e\u012f\7C\2\2\u012f"+
"\u0130\7U\2\2\u0130\26\3\2\2\2\u0131\u0132\7C\2\2\u0132\u0133\7U\2\2\u0133"+
"\u0134\7E\2\2\u0134\30\3\2\2\2\u0135\u0136\7D\2\2\u0136\u0137\7G\2\2\u0137"+
"\u0138\7V\2\2\u0138\u0139\7Y\2\2\u0139\u013a\7G\2\2\u013a\u013b\7G\2\2"+
"\u013b\u013c\7P\2\2\u013c\32\3\2\2\2\u013d\u013e\7D\2\2\u013e\u013f\7"+
"[\2\2\u013f\34\3\2\2\2\u0140\u0141\7E\2\2\u0141\u0142\7C\2\2\u0142\u0143"+
"\7U\2\2\u0143\u0144\7V\2\2\u0144\36\3\2\2\2\u0145\u0146\7E\2\2\u0146\u0147"+
"\7C\2\2\u0147\u0148\7V\2\2\u0148\u0149\7C\2\2\u0149\u014a\7N\2\2\u014a"+
"\u014b\7Q\2\2\u014b\u014c\7I\2\2\u014c \3\2\2\2\u014d\u014e\7E\2\2\u014e"+
"\u014f\7C\2\2\u014f\u0150\7V\2\2\u0150\u0151\7C\2\2\u0151\u0152\7N\2\2"+
"\u0152\u0153\7Q\2\2\u0153\u0154\7I\2\2\u0154\u0155\7U\2\2\u0155\"\3\2"+
"\2\2\u0156\u0157\7E\2\2\u0157\u0158\7Q\2\2\u0158\u0159\7N\2\2\u0159\u015a"+
"\7W\2\2\u015a\u015b\7O\2\2\u015b\u015c\7P\2\2\u015c\u015d\7U\2\2\u015d"+
"$\3\2\2\2\u015e\u015f\7E\2\2\u015f\u0160\7Q\2\2\u0160\u0161\7P\2\2\u0161"+
"\u0162\7X\2\2\u0162\u0163\7G\2\2\u0163\u0164\7T\2\2\u0164\u0165\7V\2\2"+
"\u0165&\3\2\2\2\u0166\u0167\7E\2\2\u0167\u0168\7W\2\2\u0168\u0169\7T\2"+
"\2\u0169\u016a\7T\2\2\u016a\u016b\7G\2\2\u016b\u016c\7P\2\2\u016c\u016d"+
"\7V\2\2\u016d\u016e\7a\2\2\u016e\u016f\7F\2\2\u016f\u0170\7C\2\2\u0170"+
"\u0171\7V\2\2\u0171\u0172\7G\2\2\u0172(\3\2\2\2\u0173\u0174\7E\2\2\u0174"+
"\u0175\7W\2\2\u0175\u0176\7T\2\2\u0176\u0177\7T\2\2\u0177\u0178\7G\2\2"+
"\u0178\u0179\7P\2\2\u0179\u017a\7V\2\2\u017a\u017b\7a\2\2\u017b\u017c"+
"\7V\2\2\u017c\u017d\7K\2\2\u017d\u017e\7O\2\2\u017e\u017f\7G\2\2\u017f"+
"*\3\2\2\2\u0180\u0181\7E\2\2\u0181\u0182\7W\2\2\u0182\u0183\7T\2\2\u0183"+
"\u0184\7T\2\2\u0184\u0185\7G\2\2\u0185\u0186\7P\2\2\u0186\u0187\7V\2\2"+
"\u0187\u0188\7a\2\2\u0188\u0189\7V\2\2\u0189\u018a\7K\2\2\u018a\u018b"+
"\7O\2\2\u018b\u018c\7G\2\2\u018c\u018d\7U\2\2\u018d\u018e\7V\2\2\u018e"+
"\u018f\7C\2\2\u018f\u0190\7O\2\2\u0190\u0191\7R\2\2\u0191,\3\2\2\2\u0192"+
"\u0193\7F\2\2\u0193\u0194\7C\2\2\u0194\u0195\7[\2\2\u0195.\3\2\2\2\u0196"+
"\u0197\7F\2\2\u0197\u0198\7C\2\2\u0198\u0199\7[\2\2\u0199\u019a\7U\2\2"+
"\u019a\60\3\2\2\2\u019b\u019c\7F\2\2\u019c\u019d\7G\2\2\u019d\u019e\7"+
"D\2\2\u019e\u019f\7W\2\2\u019f\u01a0\7I\2\2\u01a0\62\3\2\2\2\u01a1\u01a2"+
"\7F\2\2\u01a2\u01a3\7G\2\2\u01a3\u01a4\7U\2\2\u01a4\u01a5\7E\2\2\u01a5"+
"\64\3\2\2\2\u01a6\u01a7\7F\2\2\u01a7\u01a8\7G\2\2\u01a8\u01a9\7U\2\2\u01a9"+
"\u01aa\7E\2\2\u01aa\u01ab\7T\2\2\u01ab\u01ac\7K\2\2\u01ac\u01ad\7D\2\2"+
"\u01ad\u01ae\7G\2\2\u01ae\66\3\2\2\2\u01af\u01b0\7F\2\2\u01b0\u01b1\7"+
"K\2\2\u01b1\u01b2\7U\2\2\u01b2\u01b3\7V\2\2\u01b3\u01b4\7K\2\2\u01b4\u01b5"+
"\7P\2\2\u01b5\u01b6\7E\2\2\u01b6\u01b7\7V\2\2\u01b78\3\2\2\2\u01b8\u01b9"+
"\7G\2\2\u01b9\u01ba\7U\2\2\u01ba\u01bb\7E\2\2\u01bb\u01bc\7C\2\2\u01bc"+
"\u01bd\7R\2\2\u01bd\u01be\7G\2\2\u01be:\3\2\2\2\u01bf\u01c0\7G\2\2\u01c0"+
"\u01c1\7Z\2\2\u01c1\u01c2\7G\2\2\u01c2\u01c3\7E\2\2\u01c3\u01c4\7W\2\2"+
"\u01c4\u01c5\7V\2\2\u01c5\u01c6\7C\2\2\u01c6\u01c7\7D\2\2\u01c7\u01c8"+
"\7N\2\2\u01c8\u01c9\7G\2\2\u01c9<\3\2\2\2\u01ca\u01cb\7G\2\2\u01cb\u01cc"+
"\7Z\2\2\u01cc\u01cd\7K\2\2\u01cd\u01ce\7U\2\2\u01ce\u01cf\7V\2\2\u01cf"+
"\u01d0\7U\2\2\u01d0>\3\2\2\2\u01d1\u01d2\7G\2\2\u01d2\u01d3\7Z\2\2\u01d3"+
"\u01d4\7R\2\2\u01d4\u01d5\7N\2\2\u01d5\u01d6\7C\2\2\u01d6\u01d7\7K\2\2"+
"\u01d7\u01d8\7P\2\2\u01d8@\3\2\2\2\u01d9\u01da\7G\2\2\u01da\u01db\7Z\2"+
"\2\u01db\u01dc\7V\2\2\u01dc\u01dd\7T\2\2\u01dd\u01de\7C\2\2\u01de\u01df"+
"\7E\2\2\u01df\u01e0\7V\2\2\u01e0B\3\2\2\2\u01e1\u01e2\7H\2\2\u01e2\u01e3"+
"\7C\2\2\u01e3\u01e4\7N\2\2\u01e4\u01e5\7U\2\2\u01e5\u01e6\7G\2\2\u01e6"+
"D\3\2\2\2\u01e7\u01e8\7H\2\2\u01e8\u01e9\7K\2\2\u01e9\u01ea\7T\2\2\u01ea"+
"\u01eb\7U\2\2\u01eb\u01ec\7V\2\2\u01ecF\3\2\2\2\u01ed\u01ee\7H\2\2\u01ee"+
"\u01ef\7Q\2\2\u01ef\u01f0\7T\2\2\u01f0\u01f1\7O\2\2\u01f1\u01f2\7C\2\2"+
"\u01f2\u01f3\7V\2\2\u01f3H\3\2\2\2\u01f4\u01f5\7H\2\2\u01f5\u01f6\7T\2"+
"\2\u01f6\u01f7\7Q\2\2\u01f7\u01f8\7O\2\2\u01f8J\3\2\2\2\u01f9\u01fa\7"+
"H\2\2\u01fa\u01fb\7W\2\2\u01fb\u01fc\7N\2\2\u01fc\u01fd\7N\2\2\u01fdL"+
"\3\2\2\2\u01fe\u01ff\7H\2\2\u01ff\u0200\7W\2\2\u0200\u0201\7P\2\2\u0201"+
"\u0202\7E\2\2\u0202\u0203\7V\2\2\u0203\u0204\7K\2\2\u0204\u0205\7Q\2\2"+
"\u0205\u0206\7P\2\2\u0206\u0207\7U\2\2\u0207N\3\2\2\2\u0208\u0209\7I\2"+
"\2\u0209\u020a\7T\2\2\u020a\u020b\7C\2\2\u020b\u020c\7R\2\2\u020c\u020d"+
"\7J\2\2\u020d\u020e\7X\2\2\u020e\u020f\7K\2\2\u020f\u0210\7\\\2\2\u0210"+
"P\3\2\2\2\u0211\u0212\7I\2\2\u0212\u0213\7T\2\2\u0213\u0214\7Q\2\2\u0214"+
"\u0215\7W\2\2\u0215\u0216\7R\2\2\u0216R\3\2\2\2\u0217\u0218\7J\2\2\u0218"+
"\u0219\7C\2\2\u0219\u021a\7X\2\2\u021a\u021b\7K\2\2\u021b\u021c\7P\2\2"+
"\u021c\u021d\7I\2\2\u021dT\3\2\2\2\u021e\u021f\7J\2\2\u021f\u0220\7Q\2"+
"\2\u0220\u0221\7W\2\2\u0221\u0222\7T\2\2\u0222V\3\2\2\2\u0223\u0224\7"+
"J\2\2\u0224\u0225\7Q\2\2\u0225\u0226\7W\2\2\u0226\u0227\7T\2\2\u0227\u0228"+
"\7U\2\2\u0228X\3\2\2\2\u0229\u022a\7K\2\2\u022a\u022b\7P\2\2\u022bZ\3"+
"\2\2\2\u022c\u022d\7K\2\2\u022d\u022e\7P\2\2\u022e\u022f\7P\2\2\u022f"+
"\u0230\7G\2\2\u0230\u0231\7T\2\2\u0231\\\3\2\2\2\u0232\u0233\7K\2\2\u0233"+
"\u0234\7P\2\2\u0234\u0235\7V\2\2\u0235\u0236\7G\2\2\u0236\u0237\7T\2\2"+
"\u0237\u0238\7X\2\2\u0238\u0239\7C\2\2\u0239\u023a\7N\2\2\u023a^\3\2\2"+
"\2\u023b\u023c\7K\2\2\u023c\u023d\7U\2\2\u023d`\3\2\2\2\u023e\u023f\7"+
"L\2\2\u023f\u0240\7Q\2\2\u0240\u0241\7K\2\2\u0241\u0242\7P\2\2\u0242b"+
"\3\2\2\2\u0243\u0244\7N\2\2\u0244\u0245\7C\2\2\u0245\u0246\7U\2\2\u0246"+
"\u0247\7V\2\2\u0247d\3\2\2\2\u0248\u0249\7N\2\2\u0249\u024a\7G\2\2\u024a"+
"\u024b\7H\2\2\u024b\u024c\7V\2\2\u024cf\3\2\2\2\u024d\u024e\7N\2\2\u024e"+
"\u024f\7K\2\2\u024f\u0250\7M\2\2\u0250\u0251\7G\2\2\u0251h\3\2\2\2\u0252"+
"\u0253\7N\2\2\u0253\u0254\7K\2\2\u0254\u0255\7O\2\2\u0255\u0256\7K\2\2"+
"\u0256\u0257\7V\2\2\u0257j\3\2\2\2\u0258\u0259\7O\2\2\u0259\u025a\7C\2"+
"\2\u025a\u025b\7R\2\2\u025b\u025c\7R\2\2\u025c\u025d\7G\2\2\u025d\u025e"+
"\7F\2\2\u025el\3\2\2\2\u025f\u0260\7O\2\2\u0260\u0261\7C\2\2\u0261\u0262"+
"\7V\2\2\u0262\u0263\7E\2\2\u0263\u0264\7J\2\2\u0264n\3\2\2\2\u0265\u0266"+
"\7O\2\2\u0266\u0267\7K\2\2\u0267\u0268\7P\2\2\u0268\u0269\7W\2\2\u0269"+
"\u026a\7V\2\2\u026a\u026b\7G\2\2\u026bp\3\2\2\2\u026c\u026d\7O\2\2\u026d"+
"\u026e\7K\2\2\u026e\u026f\7P\2\2\u026f\u0270\7W\2\2\u0270\u0271\7V\2\2"+
"\u0271\u0272\7G\2\2\u0272\u0273\7U\2\2\u0273r\3\2\2\2\u0274\u0275\7O\2"+
"\2\u0275\u0276\7Q\2\2\u0276\u0277\7P\2\2\u0277\u0278\7V\2\2\u0278\u0279"+
"\7J\2\2\u0279t\3\2\2\2\u027a\u027b\7O\2\2\u027b\u027c\7Q\2\2\u027c\u027d"+
"\7P\2\2\u027d\u027e\7V\2\2\u027e\u027f\7J\2\2\u027f\u0280\7U\2\2\u0280"+
"v\3\2\2\2\u0281\u0282\7P\2\2\u0282\u0283\7C\2\2\u0283\u0284\7V\2\2\u0284"+
"\u0285\7W\2\2\u0285\u0286\7T\2\2\u0286\u0287\7C\2\2\u0287\u0288\7N\2\2"+
"\u0288x\3\2\2\2\u0289\u028a\7P\2\2\u028a\u028b\7Q\2\2\u028b\u028c\7V\2"+
"\2\u028cz\3\2\2\2\u028d\u028e\7P\2\2\u028e\u028f\7W\2\2\u028f\u0290\7"+
"N\2\2\u0290\u0291\7N\2\2\u0291|\3\2\2\2\u0292\u0293\7P\2\2\u0293\u0294"+
"\7W\2\2\u0294\u0295\7N\2\2\u0295\u0296\7N\2\2\u0296\u0297\7U\2\2\u0297"+
"~\3\2\2\2\u0298\u0299\7Q\2\2\u0299\u029a\7P\2\2\u029a\u0080\3\2\2\2\u029b"+
"\u029c\7Q\2\2\u029c\u029d\7R\2\2\u029d\u029e\7V\2\2\u029e\u029f\7K\2\2"+
"\u029f\u02a0\7O\2\2\u02a0\u02a1\7K\2\2\u02a1\u02a2\7\\\2\2\u02a2\u02a3"+
"\7G\2\2\u02a3\u02a4\7F\2\2\u02a4\u0082\3\2\2\2\u02a5\u02a6\7Q\2\2\u02a6"+
"\u02a7\7T\2\2\u02a7\u0084\3\2\2\2\u02a8\u02a9\7Q\2\2\u02a9\u02aa\7T\2"+
"\2\u02aa\u02ab\7F\2\2\u02ab\u02ac\7G\2\2\u02ac\u02ad\7T\2\2\u02ad\u0086"+
"\3\2\2\2\u02ae\u02af\7Q\2\2\u02af\u02b0\7W\2\2\u02b0\u02b1\7V\2\2\u02b1"+
"\u02b2\7G\2\2\u02b2\u02b3\7T\2\2\u02b3\u0088\3\2\2\2\u02b4\u02b5\7R\2"+
"\2\u02b5\u02b6\7C\2\2\u02b6\u02b7\7T\2\2\u02b7\u02b8\7U\2\2\u02b8\u02b9"+
"\7G\2\2\u02b9\u02ba\7F\2\2\u02ba\u008a\3\2\2\2\u02bb\u02bc\7R\2\2\u02bc"+
"\u02bd\7J\2\2\u02bd\u02be\7[\2\2\u02be\u02bf\7U\2\2\u02bf\u02c0\7K\2\2"+
"\u02c0\u02c1\7E\2\2\u02c1\u02c2\7C\2\2\u02c2\u02c3\7N\2\2\u02c3\u008c"+
"\3\2\2\2\u02c4\u02c5\7R\2\2\u02c5\u02c6\7N\2\2\u02c6\u02c7\7C\2\2\u02c7"+
"\u02c8\7P\2\2\u02c8\u008e\3\2\2\2\u02c9\u02ca\7T\2\2\u02ca\u02cb\7K\2"+
"\2\u02cb\u02cc\7I\2\2\u02cc\u02cd\7J\2\2\u02cd\u02ce\7V\2\2\u02ce\u0090"+
"\3\2\2\2\u02cf\u02d0\7T\2\2\u02d0\u02d1\7N\2\2\u02d1\u02d2\7K\2\2\u02d2"+
"\u02d3\7M\2\2\u02d3\u02d4\7G\2\2\u02d4\u0092\3\2\2\2\u02d5\u02d6\7S\2"+
"\2\u02d6\u02d7\7W\2\2\u02d7\u02d8\7G\2\2\u02d8\u02d9\7T\2\2\u02d9\u02da"+
"\7[\2\2\u02da\u0094\3\2\2\2\u02db\u02dc\7U\2\2\u02dc\u02dd\7E\2\2\u02dd"+
"\u02de\7J\2\2\u02de\u02df\7G\2\2\u02df\u02e0\7O\2\2\u02e0\u02e1\7C\2\2"+
"\u02e1\u02e2\7U\2\2\u02e2\u0096\3\2\2\2\u02e3\u02e4\7U\2\2\u02e4\u02e5"+
"\7G\2\2\u02e5\u02e6\7E\2\2\u02e6\u02e7\7Q\2\2\u02e7\u02e8\7P\2\2\u02e8"+
"\u02e9\7F\2\2\u02e9\u0098\3\2\2\2\u02ea\u02eb\7U\2\2\u02eb\u02ec\7G\2"+
"\2\u02ec\u02ed\7E\2\2\u02ed\u02ee\7Q\2\2\u02ee\u02ef\7P\2\2\u02ef\u02f0"+
"\7F\2\2\u02f0\u02f1\7U\2\2\u02f1\u009a\3\2\2\2\u02f2\u02f3\7U\2\2\u02f3"+
"\u02f4\7G\2\2\u02f4\u02f5\7N\2\2\u02f5\u02f6\7G\2\2\u02f6\u02f7\7E\2\2"+
"\u02f7\u02f8\7V\2\2\u02f8\u009c\3\2\2\2\u02f9\u02fa\7U\2\2\u02fa\u02fb"+
"\7J\2\2\u02fb\u02fc\7Q\2\2\u02fc\u02fd\7Y\2\2\u02fd\u009e\3\2\2\2\u02fe"+
"\u02ff\7U\2\2\u02ff\u0300\7[\2\2\u0300\u0301\7U\2\2\u0301\u00a0\3\2\2"+
"\2\u0302\u0303\7V\2\2\u0303\u0304\7C\2\2\u0304\u0305\7D\2\2\u0305\u0306"+
"\7N\2\2\u0306\u0307\7G\2\2\u0307\u00a2\3\2\2\2\u0308\u0309\7V\2\2\u0309"+
"\u030a\7C\2\2\u030a\u030b\7D\2\2\u030b\u030c\7N\2\2\u030c\u030d\7G\2\2"+
"\u030d\u030e\7U\2\2\u030e\u00a4\3\2\2\2\u030f\u0310\7V\2\2\u0310\u0311"+
"\7G\2\2\u0311\u0312\7Z\2\2\u0312\u0313\7V\2\2\u0313\u00a6\3\2\2\2\u0314"+
"\u0315\7V\2\2\u0315\u0316\7T\2\2\u0316\u0317\7W\2\2\u0317\u0318\7G\2\2"+
"\u0318\u00a8\3\2\2\2\u0319\u031a\7V\2\2\u031a\u031b\7Q\2\2\u031b\u00aa"+
"\3\2\2\2\u031c\u031d\7V\2\2\u031d\u031e\7[\2\2\u031e\u031f\7R\2\2\u031f"+
"\u0320\7G\2\2\u0320\u00ac\3\2\2\2\u0321\u0322\7V\2\2\u0322\u0323\7[\2"+
"\2\u0323\u0324\7R\2\2\u0324\u0325\7G\2\2\u0325\u0326\7U\2\2\u0326\u00ae"+
"\3\2\2\2\u0327\u0328\7W\2\2\u0328\u0329\7U\2\2\u0329\u032a\7K\2\2\u032a"+
"\u032b\7P\2\2\u032b\u032c\7I\2\2\u032c\u00b0\3\2\2\2\u032d\u032e\7X\2"+
"\2\u032e\u032f\7G\2\2\u032f\u0330\7T\2\2\u0330\u0331\7K\2\2\u0331\u0332"+
"\7H\2\2\u0332\u0333\7[\2\2\u0333\u00b2\3\2\2\2\u0334\u0335\7Y\2\2\u0335"+
"\u0336\7J\2\2\u0336\u0337\7G\2\2\u0337\u0338\7T\2\2\u0338\u0339\7G\2\2"+
"\u0339\u00b4\3\2\2\2\u033a\u033b\7Y\2\2\u033b\u033c\7K\2\2\u033c\u033d"+
"\7V\2\2\u033d\u033e\7J\2\2\u033e\u00b6\3\2\2\2\u033f\u0340\7[\2\2\u0340"+
"\u0341\7G\2\2\u0341\u0342\7C\2\2\u0342\u0343\7T\2\2\u0343\u00b8\3\2\2"+
"\2\u0344\u0345\7[\2\2\u0345\u0346\7G\2\2\u0346\u0347\7C\2\2\u0347\u0348"+
"\7T\2\2\u0348\u0349\7U\2\2\u0349\u00ba\3\2\2\2\u034a\u034b\7}\2\2\u034b"+
"\u034c\7G\2\2\u034c\u034d\7U\2\2\u034d\u034e\7E\2\2\u034e\u034f\7C\2\2"+
"\u034f\u0350\7R\2\2\u0350\u0351\7G\2\2\u0351\u00bc\3\2\2\2\u0352\u0353"+
"\7}\2\2\u0353\u0354\7H\2\2\u0354\u0355\7P\2\2\u0355\u00be\3\2\2\2\u0356"+
"\u0357\7}\2\2\u0357\u0358\7N\2\2\u0358\u0359\7K\2\2\u0359\u035a\7O\2\2"+
"\u035a\u035b\7K\2\2\u035b\u035c\7V\2\2\u035c\u00c0\3\2\2\2\u035d\u035e"+
"\7}\2\2\u035e\u035f\7F\2\2\u035f\u00c2\3\2\2\2\u0360\u0361\7}\2\2\u0361"+
"\u0362\7V\2\2\u0362\u00c4\3\2\2\2\u0363\u0364\7}\2\2\u0364\u0365\7V\2"+
"\2\u0365\u0366\7U\2\2\u0366\u00c6\3\2\2\2\u0367\u0368\7}\2\2\u0368\u0369"+
"\7I\2\2\u0369\u036a\7W\2\2\u036a\u036b\7K\2\2\u036b\u036c\7F\2\2\u036c"+
"\u00c8\3\2\2\2\u036d\u036e\7\177\2\2\u036e\u00ca\3\2\2\2\u036f\u0370\7"+
"?\2\2\u0370\u00cc\3\2\2\2\u0371\u0372\7>\2\2\u0372\u0373\7?\2\2\u0373"+
"\u0374\7@\2\2\u0374\u00ce\3\2\2\2\u0375\u0376\7>\2\2\u0376\u037a\7@\2"+
"\2\u0377\u0378\7#\2\2\u0378\u037a\7?\2\2\u0379\u0375\3\2\2\2\u0379\u0377"+
"\3\2\2\2\u037a\u00d0\3\2\2\2\u037b\u037c\7>\2\2\u037c\u00d2\3\2\2\2\u037d"+
"\u037e\7>\2\2\u037e\u037f\7?\2\2\u037f\u00d4\3\2\2\2\u0380\u0381\7@\2"+
"\2\u0381\u00d6\3\2\2\2\u0382\u0383\7@\2\2\u0383\u0384\7?\2\2\u0384\u00d8"+
"\3\2\2\2\u0385\u0386\7-\2\2\u0386\u00da\3\2\2\2\u0387\u0388\7/\2\2\u0388"+
"\u00dc\3\2\2\2\u0389\u038a\7,\2\2\u038a\u00de\3\2\2\2\u038b\u038c\7\61"+
"\2\2\u038c\u00e0\3\2\2\2\u038d\u038e\7\'\2\2\u038e\u00e2\3\2\2\2\u038f"+
"\u0390\7<\2\2\u0390\u0391\7<\2\2\u0391\u00e4\3\2\2\2\u0392\u0393\7~\2"+
"\2\u0393\u0394\7~\2\2\u0394\u00e6\3\2\2\2\u0395\u0396\7\60\2\2\u0396\u00e8"+
"\3\2\2\2\u0397\u0398\7A\2\2\u0398\u00ea\3\2\2\2\u0399\u039f\7)\2\2\u039a"+
"\u039e\n\2\2\2\u039b\u039c\7)\2\2\u039c\u039e\7)\2\2\u039d\u039a\3\2\2"+
"\2\u039d\u039b\3\2\2\2\u039e\u03a1\3\2\2\2\u039f\u039d\3\2\2\2\u039f\u03a0"+
"\3\2\2\2\u03a0\u03a2\3\2\2\2\u03a1\u039f\3\2\2\2\u03a2\u03a3\7)\2\2\u03a3"+
"\u00ec\3\2\2\2\u03a4\u03a6\5\u00fd\177\2\u03a5\u03a4\3\2\2\2\u03a6\u03a7"+
"\3\2\2\2\u03a7\u03a5\3\2\2\2\u03a7\u03a8\3\2\2\2\u03a8\u00ee\3\2\2\2\u03a9"+
"\u03ab\5\u00fd\177\2\u03aa\u03a9\3\2\2\2\u03ab\u03ac\3\2\2\2\u03ac\u03aa"+
"\3\2\2\2\u03ac\u03ad\3\2\2\2\u03ad\u03ae\3\2\2\2\u03ae\u03b2\5\u00e7t"+
"\2\u03af\u03b1\5\u00fd\177\2\u03b0\u03af\3\2\2\2\u03b1\u03b4\3\2\2\2\u03b2"+
"\u03b0\3\2\2\2\u03b2\u03b3\3\2\2\2\u03b3\u03d4\3\2\2\2\u03b4\u03b2\3\2"+
"\2\2\u03b5\u03b7\5\u00e7t\2\u03b6\u03b8\5\u00fd\177\2\u03b7\u03b6\3\2"+
"\2\2\u03b8\u03b9\3\2\2\2\u03b9\u03b7\3\2\2\2\u03b9\u03ba\3\2\2\2\u03ba"+
"\u03d4\3\2\2\2\u03bb\u03bd\5\u00fd\177\2\u03bc\u03bb\3\2\2\2\u03bd\u03be"+
"\3\2\2\2\u03be\u03bc\3\2\2\2\u03be\u03bf\3\2\2\2\u03bf\u03c7\3\2\2\2\u03c0"+
"\u03c4\5\u00e7t\2\u03c1\u03c3\5\u00fd\177\2\u03c2\u03c1\3\2\2\2\u03c3"+
"\u03c6\3\2\2\2\u03c4\u03c2\3\2\2\2\u03c4\u03c5\3\2\2\2\u03c5\u03c8\3\2"+
"\2\2\u03c6\u03c4\3\2\2\2\u03c7\u03c0\3\2\2\2\u03c7\u03c8\3\2\2\2\u03c8"+
"\u03c9\3\2\2\2\u03c9\u03ca\5\u00fb~\2\u03ca\u03d4\3\2\2\2\u03cb\u03cd"+
"\5\u00e7t\2\u03cc\u03ce\5\u00fd\177\2\u03cd\u03cc\3\2\2\2\u03ce\u03cf"+
"\3\2\2\2\u03cf\u03cd\3\2\2\2\u03cf\u03d0\3\2\2\2\u03d0\u03d1\3\2\2\2\u03d1"+
"\u03d2\5\u00fb~\2\u03d2\u03d4\3\2\2\2\u03d3\u03aa\3\2\2\2\u03d3\u03b5"+
"\3\2\2\2\u03d3\u03bc\3\2\2\2\u03d3\u03cb\3\2\2\2\u03d4\u00f0\3\2\2\2\u03d5"+
"\u03d8\5\u00ff\u0080\2\u03d6\u03d8\7a\2\2\u03d7\u03d5\3\2\2\2\u03d7\u03d6"+
"\3\2\2\2\u03d8\u03de\3\2\2\2\u03d9\u03dd\5\u00ff\u0080\2\u03da\u03dd\5"+
"\u00fd\177\2\u03db\u03dd\t\3\2\2\u03dc\u03d9\3\2\2\2\u03dc\u03da\3\2\2"+
"\2\u03dc\u03db\3\2\2\2\u03dd\u03e0\3\2\2\2\u03de\u03dc\3\2\2\2\u03de\u03df"+
"\3\2\2\2\u03df\u00f2\3\2\2\2\u03e0\u03de\3\2\2\2\u03e1\u03e5\5\u00fd\177"+
"\2\u03e2\u03e6\5\u00ff\u0080\2\u03e3\u03e6\5\u00fd\177\2\u03e4\u03e6\t"+
"\3\2\2\u03e5\u03e2\3\2\2\2\u03e5\u03e3\3\2\2\2\u03e5\u03e4\3\2\2\2\u03e6"+
"\u03e7\3\2\2\2\u03e7\u03e5\3\2\2\2\u03e7\u03e8\3\2\2\2\u03e8\u00f4\3\2"+
"\2\2\u03e9\u03ed\5\u00ff\u0080\2\u03ea\u03ed\5\u00fd\177\2\u03eb\u03ed"+
"\7a\2\2\u03ec\u03e9\3\2\2\2\u03ec\u03ea\3\2\2\2\u03ec\u03eb\3\2\2\2\u03ed"+
"\u03ee\3\2\2\2\u03ee\u03ec\3\2\2\2\u03ee\u03ef\3\2\2\2\u03ef\u00f6\3\2"+
"\2\2\u03f0\u03f6\7$\2\2\u03f1\u03f5\n\4\2\2\u03f2\u03f3\7$\2\2\u03f3\u03f5"+
"\7$\2\2\u03f4\u03f1\3\2\2\2\u03f4\u03f2\3\2\2\2\u03f5\u03f8\3\2\2\2\u03f6"+
"\u03f4\3\2\2\2\u03f6\u03f7\3\2\2\2\u03f7\u03f9\3\2\2\2\u03f8\u03f6\3\2"+
"\2\2\u03f9\u03fa\7$\2\2\u03fa\u00f8\3\2\2\2\u03fb\u0401\7b\2\2\u03fc\u0400"+
"\n\5\2\2\u03fd\u03fe\7b\2\2\u03fe\u0400\7b\2\2\u03ff\u03fc\3\2\2\2\u03ff"+
"\u03fd\3\2\2\2\u0400\u0403\3\2\2\2\u0401\u03ff\3\2\2\2\u0401\u0402\3\2"+
"\2\2\u0402\u0404\3\2\2\2\u0403\u0401\3\2\2\2\u0404\u0405\7b\2\2\u0405"+
"\u00fa\3\2\2\2\u0406\u0408\7G\2\2\u0407\u0409\t\6\2\2\u0408\u0407\3\2"+
"\2\2\u0408\u0409\3\2\2\2\u0409\u040b\3\2\2\2\u040a\u040c\5\u00fd\177\2"+
"\u040b\u040a\3\2\2\2\u040c\u040d\3\2\2\2\u040d\u040b\3\2\2\2\u040d\u040e"+
"\3\2\2\2\u040e\u00fc\3\2\2\2\u040f\u0410\t\7\2\2\u0410\u00fe\3\2\2\2\u0411"+
"\u0412\t\b\2\2\u0412\u0100\3\2\2\2\u0413\u0414\7/\2\2\u0414\u0415\7/\2"+
"\2\u0415\u0419\3\2\2\2\u0416\u0418\n\t\2\2\u0417\u0416\3\2\2\2\u0418\u041b"+
"\3\2\2\2\u0419\u0417\3\2\2\2\u0419\u041a\3\2\2\2\u041a\u041d\3\2\2\2\u041b"+
"\u0419\3\2\2\2\u041c\u041e\7\17\2\2\u041d\u041c\3\2\2\2\u041d\u041e\3"+
"\2\2\2\u041e\u0420\3\2\2\2\u041f\u0421\7\f\2\2\u0420\u041f\3\2\2\2\u0420"+
"\u0421\3\2\2\2\u0421\u0422\3\2\2\2\u0422\u0423\b\u0081\2\2\u0423\u0102"+
"\3\2\2\2\u0424\u0425\7\61\2\2\u0425\u0426\7,\2\2\u0426\u042b\3\2\2\2\u0427"+
"\u042a\5\u0103\u0082\2\u0428\u042a\13\2\2\2\u0429\u0427\3\2\2\2\u0429"+
"\u0428\3\2\2\2\u042a\u042d\3\2\2\2\u042b\u042c\3\2\2\2\u042b\u0429\3\2"+
"\2\2\u042c\u042e\3\2\2\2\u042d\u042b\3\2\2\2\u042e\u042f\7,\2\2\u042f"+
"\u0430\7\61\2\2\u0430\u0431\3\2\2\2\u0431\u0432\b\u0082\2\2\u0432\u0104"+
"\3\2\2\2\u0433\u0435\t\n\2\2\u0434\u0433\3\2\2\2\u0435\u0436\3\2\2\2\u0436"+
"\u0434\3\2\2\2\u0436\u0437\3\2\2\2\u0437\u0438\3\2\2\2\u0438\u0439\b\u0083"+
"\2\2\u0439\u0106\3\2\2\2\u043a\u043b\13\2\2\2\u043b\u0108\3\2\2\2\"\2"+
"\u0379\u039d\u039f\u03a7\u03ac\u03b2\u03b9\u03be\u03c4\u03c7\u03cf\u03d3"+
"\u03d7\u03dc\u03de\u03e5\u03e7\u03ec\u03ee\u03f4\u03f6\u03ff\u0401\u0408"+
"\u040d\u0419\u041d\u0420\u0429\u042b\u0436\3\2\3\2";
"z\u00f3{\u00f5|\u00f7}\u00f9~\u00fb\177\u00fd\u0080\u00ff\u0081\u0101"+
"\u0082\u0103\u0083\u0105\2\u0107\2\u0109\2\u010b\u0084\u010d\u0085\u010f"+
"\u0086\u0111\u0087\3\2\13\3\2))\4\2BBaa\3\2$$\3\2bb\4\2--//\3\2\62;\3"+
"\2C\\\4\2\f\f\17\17\5\2\13\f\17\17\"\"\u047e\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\2"+
"M\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\2"+
"s\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\u00ab\3\2\2"+
"\2\2\u00ad\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\2\u00b7\3\2\2\2\2\u00b9\3\2\2\2\2\u00bb\3\2\2\2\2\u00bd\3\2\2"+
"\2\2\u00bf\3\2\2\2\2\u00c1\3\2\2\2\2\u00c3\3\2\2\2\2\u00c5\3\2\2\2\2\u00c7"+
"\3\2\2\2\2\u00c9\3\2\2\2\2\u00cb\3\2\2\2\2\u00cd\3\2\2\2\2\u00cf\3\2\2"+
"\2\2\u00d1\3\2\2\2\2\u00d3\3\2\2\2\2\u00d5\3\2\2\2\2\u00d7\3\2\2\2\2\u00d9"+
"\3\2\2\2\2\u00db\3\2\2\2\2\u00dd\3\2\2\2\2\u00df\3\2\2\2\2\u00e1\3\2\2"+
"\2\2\u00e3\3\2\2\2\2\u00e5\3\2\2\2\2\u00e7\3\2\2\2\2\u00e9\3\2\2\2\2\u00eb"+
"\3\2\2\2\2\u00ed\3\2\2\2\2\u00ef\3\2\2\2\2\u00f1\3\2\2\2\2\u00f3\3\2\2"+
"\2\2\u00f5\3\2\2\2\2\u00f7\3\2\2\2\2\u00f9\3\2\2\2\2\u00fb\3\2\2\2\2\u00fd"+
"\3\2\2\2\2\u00ff\3\2\2\2\2\u0101\3\2\2\2\2\u0103\3\2\2\2\2\u010b\3\2\2"+
"\2\2\u010d\3\2\2\2\2\u010f\3\2\2\2\2\u0111\3\2\2\2\3\u0113\3\2\2\2\5\u0115"+
"\3\2\2\2\7\u0117\3\2\2\2\t\u0119\3\2\2\2\13\u011b\3\2\2\2\r\u011f\3\2"+
"\2\2\17\u0127\3\2\2\2\21\u0130\3\2\2\2\23\u0134\3\2\2\2\25\u0138\3\2\2"+
"\2\27\u013b\3\2\2\2\31\u013f\3\2\2\2\33\u0147\3\2\2\2\35\u014a\3\2\2\2"+
"\37\u014f\3\2\2\2!\u0154\3\2\2\2#\u015c\3\2\2\2%\u0165\3\2\2\2\'\u016d"+
"\3\2\2\2)\u0175\3\2\2\2+\u0182\3\2\2\2-\u018f\3\2\2\2/\u01a1\3\2\2\2\61"+
"\u01a5\3\2\2\2\63\u01aa\3\2\2\2\65\u01b0\3\2\2\2\67\u01b5\3\2\2\29\u01be"+
"\3\2\2\2;\u01c7\3\2\2\2=\u01cc\3\2\2\2?\u01d0\3\2\2\2A\u01d7\3\2\2\2C"+
"\u01e2\3\2\2\2E\u01e9\3\2\2\2G\u01f1\3\2\2\2I\u01f9\3\2\2\2K\u01ff\3\2"+
"\2\2M\u0205\3\2\2\2O\u020c\3\2\2\2Q\u0211\3\2\2\2S\u0216\3\2\2\2U\u0220"+
"\3\2\2\2W\u0229\3\2\2\2Y\u022f\3\2\2\2[\u0236\3\2\2\2]\u023b\3\2\2\2_"+
"\u0241\3\2\2\2a\u0244\3\2\2\2c\u024a\3\2\2\2e\u0253\3\2\2\2g\u0256\3\2"+
"\2\2i\u025b\3\2\2\2k\u0260\3\2\2\2m\u0265\3\2\2\2o\u026a\3\2\2\2q\u0270"+
"\3\2\2\2s\u0277\3\2\2\2u\u027d\3\2\2\2w\u0284\3\2\2\2y\u028c\3\2\2\2{"+
"\u0292\3\2\2\2}\u0299\3\2\2\2\177\u02a1\3\2\2\2\u0081\u02a5\3\2\2\2\u0083"+
"\u02aa\3\2\2\2\u0085\u02b0\3\2\2\2\u0087\u02b3\3\2\2\2\u0089\u02bd\3\2"+
"\2\2\u008b\u02c0\3\2\2\2\u008d\u02c6\3\2\2\2\u008f\u02cc\3\2\2\2\u0091"+
"\u02d3\3\2\2\2\u0093\u02dc\3\2\2\2\u0095\u02e1\3\2\2\2\u0097\u02e7\3\2"+
"\2\2\u0099\u02ed\3\2\2\2\u009b\u02f3\3\2\2\2\u009d\u02fb\3\2\2\2\u009f"+
"\u0302\3\2\2\2\u00a1\u030a\3\2\2\2\u00a3\u0311\3\2\2\2\u00a5\u0316\3\2"+
"\2\2\u00a7\u031a\3\2\2\2\u00a9\u0320\3\2\2\2\u00ab\u0327\3\2\2\2\u00ad"+
"\u032c\3\2\2\2\u00af\u0331\3\2\2\2\u00b1\u0336\3\2\2\2\u00b3\u0339\3\2"+
"\2\2\u00b5\u033e\3\2\2\2\u00b7\u0344\3\2\2\2\u00b9\u034a\3\2\2\2\u00bb"+
"\u0351\3\2\2\2\u00bd\u0356\3\2\2\2\u00bf\u035c\3\2\2\2\u00c1\u0361\3\2"+
"\2\2\u00c3\u0366\3\2\2\2\u00c5\u036c\3\2\2\2\u00c7\u0374\3\2\2\2\u00c9"+
"\u0378\3\2\2\2\u00cb\u037f\3\2\2\2\u00cd\u0382\3\2\2\2\u00cf\u0385\3\2"+
"\2\2\u00d1\u0389\3\2\2\2\u00d3\u038f\3\2\2\2\u00d5\u0391\3\2\2\2\u00d7"+
"\u0393\3\2\2\2\u00d9\u039b\3\2\2\2\u00db\u039d\3\2\2\2\u00dd\u039f\3\2"+
"\2\2\u00df\u03a2\3\2\2\2\u00e1\u03a4\3\2\2\2\u00e3\u03a7\3\2\2\2\u00e5"+
"\u03a9\3\2\2\2\u00e7\u03ab\3\2\2\2\u00e9\u03ad\3\2\2\2\u00eb\u03af\3\2"+
"\2\2\u00ed\u03b1\3\2\2\2\u00ef\u03b4\3\2\2\2\u00f1\u03b7\3\2\2\2\u00f3"+
"\u03b9\3\2\2\2\u00f5\u03bb\3\2\2\2\u00f7\u03c7\3\2\2\2\u00f9\u03f5\3\2"+
"\2\2\u00fb\u03f9\3\2\2\2\u00fd\u0403\3\2\2\2\u00ff\u040e\3\2\2\2\u0101"+
"\u0412\3\2\2\2\u0103\u041d\3\2\2\2\u0105\u0428\3\2\2\2\u0107\u0431\3\2"+
"\2\2\u0109\u0433\3\2\2\2\u010b\u0435\3\2\2\2\u010d\u0446\3\2\2\2\u010f"+
"\u0456\3\2\2\2\u0111\u045c\3\2\2\2\u0113\u0114\7*\2\2\u0114\4\3\2\2\2"+
"\u0115\u0116\7+\2\2\u0116\6\3\2\2\2\u0117\u0118\7.\2\2\u0118\b\3\2\2\2"+
"\u0119\u011a\7<\2\2\u011a\n\3\2\2\2\u011b\u011c\7C\2\2\u011c\u011d\7N"+
"\2\2\u011d\u011e\7N\2\2\u011e\f\3\2\2\2\u011f\u0120\7C\2\2\u0120\u0121"+
"\7P\2\2\u0121\u0122\7C\2\2\u0122\u0123\7N\2\2\u0123\u0124\7[\2\2\u0124"+
"\u0125\7\\\2\2\u0125\u0126\7G\2\2\u0126\16\3\2\2\2\u0127\u0128\7C\2\2"+
"\u0128\u0129\7P\2\2\u0129\u012a\7C\2\2\u012a\u012b\7N\2\2\u012b\u012c"+
"\7[\2\2\u012c\u012d\7\\\2\2\u012d\u012e\7G\2\2\u012e\u012f\7F\2\2\u012f"+
"\20\3\2\2\2\u0130\u0131\7C\2\2\u0131\u0132\7P\2\2\u0132\u0133\7F\2\2\u0133"+
"\22\3\2\2\2\u0134\u0135\7C\2\2\u0135\u0136\7P\2\2\u0136\u0137\7[\2\2\u0137"+
"\24\3\2\2\2\u0138\u0139\7C\2\2\u0139\u013a\7U\2\2\u013a\26\3\2\2\2\u013b"+
"\u013c\7C\2\2\u013c\u013d\7U\2\2\u013d\u013e\7E\2\2\u013e\30\3\2\2\2\u013f"+
"\u0140\7D\2\2\u0140\u0141\7G\2\2\u0141\u0142\7V\2\2\u0142\u0143\7Y\2\2"+
"\u0143\u0144\7G\2\2\u0144\u0145\7G\2\2\u0145\u0146\7P\2\2\u0146\32\3\2"+
"\2\2\u0147\u0148\7D\2\2\u0148\u0149\7[\2\2\u0149\34\3\2\2\2\u014a\u014b"+
"\7E\2\2\u014b\u014c\7C\2\2\u014c\u014d\7U\2\2\u014d\u014e\7G\2\2\u014e"+
"\36\3\2\2\2\u014f\u0150\7E\2\2\u0150\u0151\7C\2\2\u0151\u0152\7U\2\2\u0152"+
"\u0153\7V\2\2\u0153 \3\2\2\2\u0154\u0155\7E\2\2\u0155\u0156\7C\2\2\u0156"+
"\u0157\7V\2\2\u0157\u0158\7C\2\2\u0158\u0159\7N\2\2\u0159\u015a\7Q\2\2"+
"\u015a\u015b\7I\2\2\u015b\"\3\2\2\2\u015c\u015d\7E\2\2\u015d\u015e\7C"+
"\2\2\u015e\u015f\7V\2\2\u015f\u0160\7C\2\2\u0160\u0161\7N\2\2\u0161\u0162"+
"\7Q\2\2\u0162\u0163\7I\2\2\u0163\u0164\7U\2\2\u0164$\3\2\2\2\u0165\u0166"+
"\7E\2\2\u0166\u0167\7Q\2\2\u0167\u0168\7N\2\2\u0168\u0169\7W\2\2\u0169"+
"\u016a\7O\2\2\u016a\u016b\7P\2\2\u016b\u016c\7U\2\2\u016c&\3\2\2\2\u016d"+
"\u016e\7E\2\2\u016e\u016f\7Q\2\2\u016f\u0170\7P\2\2\u0170\u0171\7X\2\2"+
"\u0171\u0172\7G\2\2\u0172\u0173\7T\2\2\u0173\u0174\7V\2\2\u0174(\3\2\2"+
"\2\u0175\u0176\7E\2\2\u0176\u0177\7W\2\2\u0177\u0178\7T\2\2\u0178\u0179"+
"\7T\2\2\u0179\u017a\7G\2\2\u017a\u017b\7P\2\2\u017b\u017c\7V\2\2\u017c"+
"\u017d\7a\2\2\u017d\u017e\7F\2\2\u017e\u017f\7C\2\2\u017f\u0180\7V\2\2"+
"\u0180\u0181\7G\2\2\u0181*\3\2\2\2\u0182\u0183\7E\2\2\u0183\u0184\7W\2"+
"\2\u0184\u0185\7T\2\2\u0185\u0186\7T\2\2\u0186\u0187\7G\2\2\u0187\u0188"+
"\7P\2\2\u0188\u0189\7V\2\2\u0189\u018a\7a\2\2\u018a\u018b\7V\2\2\u018b"+
"\u018c\7K\2\2\u018c\u018d\7O\2\2\u018d\u018e\7G\2\2\u018e,\3\2\2\2\u018f"+
"\u0190\7E\2\2\u0190\u0191\7W\2\2\u0191\u0192\7T\2\2\u0192\u0193\7T\2\2"+
"\u0193\u0194\7G\2\2\u0194\u0195\7P\2\2\u0195\u0196\7V\2\2\u0196\u0197"+
"\7a\2\2\u0197\u0198\7V\2\2\u0198\u0199\7K\2\2\u0199\u019a\7O\2\2\u019a"+
"\u019b\7G\2\2\u019b\u019c\7U\2\2\u019c\u019d\7V\2\2\u019d\u019e\7C\2\2"+
"\u019e\u019f\7O\2\2\u019f\u01a0\7R\2\2\u01a0.\3\2\2\2\u01a1\u01a2\7F\2"+
"\2\u01a2\u01a3\7C\2\2\u01a3\u01a4\7[\2\2\u01a4\60\3\2\2\2\u01a5\u01a6"+
"\7F\2\2\u01a6\u01a7\7C\2\2\u01a7\u01a8\7[\2\2\u01a8\u01a9\7U\2\2\u01a9"+
"\62\3\2\2\2\u01aa\u01ab\7F\2\2\u01ab\u01ac\7G\2\2\u01ac\u01ad\7D\2\2\u01ad"+
"\u01ae\7W\2\2\u01ae\u01af\7I\2\2\u01af\64\3\2\2\2\u01b0\u01b1\7F\2\2\u01b1"+
"\u01b2\7G\2\2\u01b2\u01b3\7U\2\2\u01b3\u01b4\7E\2\2\u01b4\66\3\2\2\2\u01b5"+
"\u01b6\7F\2\2\u01b6\u01b7\7G\2\2\u01b7\u01b8\7U\2\2\u01b8\u01b9\7E\2\2"+
"\u01b9\u01ba\7T\2\2\u01ba\u01bb\7K\2\2\u01bb\u01bc\7D\2\2\u01bc\u01bd"+
"\7G\2\2\u01bd8\3\2\2\2\u01be\u01bf\7F\2\2\u01bf\u01c0\7K\2\2\u01c0\u01c1"+
"\7U\2\2\u01c1\u01c2\7V\2\2\u01c2\u01c3\7K\2\2\u01c3\u01c4\7P\2\2\u01c4"+
"\u01c5\7E\2\2\u01c5\u01c6\7V\2\2\u01c6:\3\2\2\2\u01c7\u01c8\7G\2\2\u01c8"+
"\u01c9\7N\2\2\u01c9\u01ca\7U\2\2\u01ca\u01cb\7G\2\2\u01cb<\3\2\2\2\u01cc"+
"\u01cd\7G\2\2\u01cd\u01ce\7P\2\2\u01ce\u01cf\7F\2\2\u01cf>\3\2\2\2\u01d0"+
"\u01d1\7G\2\2\u01d1\u01d2\7U\2\2\u01d2\u01d3\7E\2\2\u01d3\u01d4\7C\2\2"+
"\u01d4\u01d5\7R\2\2\u01d5\u01d6\7G\2\2\u01d6@\3\2\2\2\u01d7\u01d8\7G\2"+
"\2\u01d8\u01d9\7Z\2\2\u01d9\u01da\7G\2\2\u01da\u01db\7E\2\2\u01db\u01dc"+
"\7W\2\2\u01dc\u01dd\7V\2\2\u01dd\u01de\7C\2\2\u01de\u01df\7D\2\2\u01df"+
"\u01e0\7N\2\2\u01e0\u01e1\7G\2\2\u01e1B\3\2\2\2\u01e2\u01e3\7G\2\2\u01e3"+
"\u01e4\7Z\2\2\u01e4\u01e5\7K\2\2\u01e5\u01e6\7U\2\2\u01e6\u01e7\7V\2\2"+
"\u01e7\u01e8\7U\2\2\u01e8D\3\2\2\2\u01e9\u01ea\7G\2\2\u01ea\u01eb\7Z\2"+
"\2\u01eb\u01ec\7R\2\2\u01ec\u01ed\7N\2\2\u01ed\u01ee\7C\2\2\u01ee\u01ef"+
"\7K\2\2\u01ef\u01f0\7P\2\2\u01f0F\3\2\2\2\u01f1\u01f2\7G\2\2\u01f2\u01f3"+
"\7Z\2\2\u01f3\u01f4\7V\2\2\u01f4\u01f5\7T\2\2\u01f5\u01f6\7C\2\2\u01f6"+
"\u01f7\7E\2\2\u01f7\u01f8\7V\2\2\u01f8H\3\2\2\2\u01f9\u01fa\7H\2\2\u01fa"+
"\u01fb\7C\2\2\u01fb\u01fc\7N\2\2\u01fc\u01fd\7U\2\2\u01fd\u01fe\7G\2\2"+
"\u01feJ\3\2\2\2\u01ff\u0200\7H\2\2\u0200\u0201\7K\2\2\u0201\u0202\7T\2"+
"\2\u0202\u0203\7U\2\2\u0203\u0204\7V\2\2\u0204L\3\2\2\2\u0205\u0206\7"+
"H\2\2\u0206\u0207\7Q\2\2\u0207\u0208\7T\2\2\u0208\u0209\7O\2\2\u0209\u020a"+
"\7C\2\2\u020a\u020b\7V\2\2\u020bN\3\2\2\2\u020c\u020d\7H\2\2\u020d\u020e"+
"\7T\2\2\u020e\u020f\7Q\2\2\u020f\u0210\7O\2\2\u0210P\3\2\2\2\u0211\u0212"+
"\7H\2\2\u0212\u0213\7W\2\2\u0213\u0214\7N\2\2\u0214\u0215\7N\2\2\u0215"+
"R\3\2\2\2\u0216\u0217\7H\2\2\u0217\u0218\7W\2\2\u0218\u0219\7P\2\2\u0219"+
"\u021a\7E\2\2\u021a\u021b\7V\2\2\u021b\u021c\7K\2\2\u021c\u021d\7Q\2\2"+
"\u021d\u021e\7P\2\2\u021e\u021f\7U\2\2\u021fT\3\2\2\2\u0220\u0221\7I\2"+
"\2\u0221\u0222\7T\2\2\u0222\u0223\7C\2\2\u0223\u0224\7R\2\2\u0224\u0225"+
"\7J\2\2\u0225\u0226\7X\2\2\u0226\u0227\7K\2\2\u0227\u0228\7\\\2\2\u0228"+
"V\3\2\2\2\u0229\u022a\7I\2\2\u022a\u022b\7T\2\2\u022b\u022c\7Q\2\2\u022c"+
"\u022d\7W\2\2\u022d\u022e\7R\2\2\u022eX\3\2\2\2\u022f\u0230\7J\2\2\u0230"+
"\u0231\7C\2\2\u0231\u0232\7X\2\2\u0232\u0233\7K\2\2\u0233\u0234\7P\2\2"+
"\u0234\u0235\7I\2\2\u0235Z\3\2\2\2\u0236\u0237\7J\2\2\u0237\u0238\7Q\2"+
"\2\u0238\u0239\7W\2\2\u0239\u023a\7T\2\2\u023a\\\3\2\2\2\u023b\u023c\7"+
"J\2\2\u023c\u023d\7Q\2\2\u023d\u023e\7W\2\2\u023e\u023f\7T\2\2\u023f\u0240"+
"\7U\2\2\u0240^\3\2\2\2\u0241\u0242\7K\2\2\u0242\u0243\7P\2\2\u0243`\3"+
"\2\2\2\u0244\u0245\7K\2\2\u0245\u0246\7P\2\2\u0246\u0247\7P\2\2\u0247"+
"\u0248\7G\2\2\u0248\u0249\7T\2\2\u0249b\3\2\2\2\u024a\u024b\7K\2\2\u024b"+
"\u024c\7P\2\2\u024c\u024d\7V\2\2\u024d\u024e\7G\2\2\u024e\u024f\7T\2\2"+
"\u024f\u0250\7X\2\2\u0250\u0251\7C\2\2\u0251\u0252\7N\2\2\u0252d\3\2\2"+
"\2\u0253\u0254\7K\2\2\u0254\u0255\7U\2\2\u0255f\3\2\2\2\u0256\u0257\7"+
"L\2\2\u0257\u0258\7Q\2\2\u0258\u0259\7K\2\2\u0259\u025a\7P\2\2\u025ah"+
"\3\2\2\2\u025b\u025c\7N\2\2\u025c\u025d\7C\2\2\u025d\u025e\7U\2\2\u025e"+
"\u025f\7V\2\2\u025fj\3\2\2\2\u0260\u0261\7N\2\2\u0261\u0262\7G\2\2\u0262"+
"\u0263\7H\2\2\u0263\u0264\7V\2\2\u0264l\3\2\2\2\u0265\u0266\7N\2\2\u0266"+
"\u0267\7K\2\2\u0267\u0268\7M\2\2\u0268\u0269\7G\2\2\u0269n\3\2\2\2\u026a"+
"\u026b\7N\2\2\u026b\u026c\7K\2\2\u026c\u026d\7O\2\2\u026d\u026e\7K\2\2"+
"\u026e\u026f\7V\2\2\u026fp\3\2\2\2\u0270\u0271\7O\2\2\u0271\u0272\7C\2"+
"\2\u0272\u0273\7R\2\2\u0273\u0274\7R\2\2\u0274\u0275\7G\2\2\u0275\u0276"+
"\7F\2\2\u0276r\3\2\2\2\u0277\u0278\7O\2\2\u0278\u0279\7C\2\2\u0279\u027a"+
"\7V\2\2\u027a\u027b\7E\2\2\u027b\u027c\7J\2\2\u027ct\3\2\2\2\u027d\u027e"+
"\7O\2\2\u027e\u027f\7K\2\2\u027f\u0280\7P\2\2\u0280\u0281\7W\2\2\u0281"+
"\u0282\7V\2\2\u0282\u0283\7G\2\2\u0283v\3\2\2\2\u0284\u0285\7O\2\2\u0285"+
"\u0286\7K\2\2\u0286\u0287\7P\2\2\u0287\u0288\7W\2\2\u0288\u0289\7V\2\2"+
"\u0289\u028a\7G\2\2\u028a\u028b\7U\2\2\u028bx\3\2\2\2\u028c\u028d\7O\2"+
"\2\u028d\u028e\7Q\2\2\u028e\u028f\7P\2\2\u028f\u0290\7V\2\2\u0290\u0291"+
"\7J\2\2\u0291z\3\2\2\2\u0292\u0293\7O\2\2\u0293\u0294\7Q\2\2\u0294\u0295"+
"\7P\2\2\u0295\u0296\7V\2\2\u0296\u0297\7J\2\2\u0297\u0298\7U\2\2\u0298"+
"|\3\2\2\2\u0299\u029a\7P\2\2\u029a\u029b\7C\2\2\u029b\u029c\7V\2\2\u029c"+
"\u029d\7W\2\2\u029d\u029e\7T\2\2\u029e\u029f\7C\2\2\u029f\u02a0\7N\2\2"+
"\u02a0~\3\2\2\2\u02a1\u02a2\7P\2\2\u02a2\u02a3\7Q\2\2\u02a3\u02a4\7V\2"+
"\2\u02a4\u0080\3\2\2\2\u02a5\u02a6\7P\2\2\u02a6\u02a7\7W\2\2\u02a7\u02a8"+
"\7N\2\2\u02a8\u02a9\7N\2\2\u02a9\u0082\3\2\2\2\u02aa\u02ab\7P\2\2\u02ab"+
"\u02ac\7W\2\2\u02ac\u02ad\7N\2\2\u02ad\u02ae\7N\2\2\u02ae\u02af\7U\2\2"+
"\u02af\u0084\3\2\2\2\u02b0\u02b1\7Q\2\2\u02b1\u02b2\7P\2\2\u02b2\u0086"+
"\3\2\2\2\u02b3\u02b4\7Q\2\2\u02b4\u02b5\7R\2\2\u02b5\u02b6\7V\2\2\u02b6"+
"\u02b7\7K\2\2\u02b7\u02b8\7O\2\2\u02b8\u02b9\7K\2\2\u02b9\u02ba\7\\\2"+
"\2\u02ba\u02bb\7G\2\2\u02bb\u02bc\7F\2\2\u02bc\u0088\3\2\2\2\u02bd\u02be"+
"\7Q\2\2\u02be\u02bf\7T\2\2\u02bf\u008a\3\2\2\2\u02c0\u02c1\7Q\2\2\u02c1"+
"\u02c2\7T\2\2\u02c2\u02c3\7F\2\2\u02c3\u02c4\7G\2\2\u02c4\u02c5\7T\2\2"+
"\u02c5\u008c\3\2\2\2\u02c6\u02c7\7Q\2\2\u02c7\u02c8\7W\2\2\u02c8\u02c9"+
"\7V\2\2\u02c9\u02ca\7G\2\2\u02ca\u02cb\7T\2\2\u02cb\u008e\3\2\2\2\u02cc"+
"\u02cd\7R\2\2\u02cd\u02ce\7C\2\2\u02ce\u02cf\7T\2\2\u02cf\u02d0\7U\2\2"+
"\u02d0\u02d1\7G\2\2\u02d1\u02d2\7F\2\2\u02d2\u0090\3\2\2\2\u02d3\u02d4"+
"\7R\2\2\u02d4\u02d5\7J\2\2\u02d5\u02d6\7[\2\2\u02d6\u02d7\7U\2\2\u02d7"+
"\u02d8\7K\2\2\u02d8\u02d9\7E\2\2\u02d9\u02da\7C\2\2\u02da\u02db\7N\2\2"+
"\u02db\u0092\3\2\2\2\u02dc\u02dd\7R\2\2\u02dd\u02de\7N\2\2\u02de\u02df"+
"\7C\2\2\u02df\u02e0\7P\2\2\u02e0\u0094\3\2\2\2\u02e1\u02e2\7T\2\2\u02e2"+
"\u02e3\7K\2\2\u02e3\u02e4\7I\2\2\u02e4\u02e5\7J\2\2\u02e5\u02e6\7V\2\2"+
"\u02e6\u0096\3\2\2\2\u02e7\u02e8\7T\2\2\u02e8\u02e9\7N\2\2\u02e9\u02ea"+
"\7K\2\2\u02ea\u02eb\7M\2\2\u02eb\u02ec\7G\2\2\u02ec\u0098\3\2\2\2\u02ed"+
"\u02ee\7S\2\2\u02ee\u02ef\7W\2\2\u02ef\u02f0\7G\2\2\u02f0\u02f1\7T\2\2"+
"\u02f1\u02f2\7[\2\2\u02f2\u009a\3\2\2\2\u02f3\u02f4\7U\2\2\u02f4\u02f5"+
"\7E\2\2\u02f5\u02f6\7J\2\2\u02f6\u02f7\7G\2\2\u02f7\u02f8\7O\2\2\u02f8"+
"\u02f9\7C\2\2\u02f9\u02fa\7U\2\2\u02fa\u009c\3\2\2\2\u02fb\u02fc\7U\2"+
"\2\u02fc\u02fd\7G\2\2\u02fd\u02fe\7E\2\2\u02fe\u02ff\7Q\2\2\u02ff\u0300"+
"\7P\2\2\u0300\u0301\7F\2\2\u0301\u009e\3\2\2\2\u0302\u0303\7U\2\2\u0303"+
"\u0304\7G\2\2\u0304\u0305\7E\2\2\u0305\u0306\7Q\2\2\u0306\u0307\7P\2\2"+
"\u0307\u0308\7F\2\2\u0308\u0309\7U\2\2\u0309\u00a0\3\2\2\2\u030a\u030b"+
"\7U\2\2\u030b\u030c\7G\2\2\u030c\u030d\7N\2\2\u030d\u030e\7G\2\2\u030e"+
"\u030f\7E\2\2\u030f\u0310\7V\2\2\u0310\u00a2\3\2\2\2\u0311\u0312\7U\2"+
"\2\u0312\u0313\7J\2\2\u0313\u0314\7Q\2\2\u0314\u0315\7Y\2\2\u0315\u00a4"+
"\3\2\2\2\u0316\u0317\7U\2\2\u0317\u0318\7[\2\2\u0318\u0319\7U\2\2\u0319"+
"\u00a6\3\2\2\2\u031a\u031b\7V\2\2\u031b\u031c\7C\2\2\u031c\u031d\7D\2"+
"\2\u031d\u031e\7N\2\2\u031e\u031f\7G\2\2\u031f\u00a8\3\2\2\2\u0320\u0321"+
"\7V\2\2\u0321\u0322\7C\2\2\u0322\u0323\7D\2\2\u0323\u0324\7N\2\2\u0324"+
"\u0325\7G\2\2\u0325\u0326\7U\2\2\u0326\u00aa\3\2\2\2\u0327\u0328\7V\2"+
"\2\u0328\u0329\7G\2\2\u0329\u032a\7Z\2\2\u032a\u032b\7V\2\2\u032b\u00ac"+
"\3\2\2\2\u032c\u032d\7V\2\2\u032d\u032e\7J\2\2\u032e\u032f\7G\2\2\u032f"+
"\u0330\7P\2\2\u0330\u00ae\3\2\2\2\u0331\u0332\7V\2\2\u0332\u0333\7T\2"+
"\2\u0333\u0334\7W\2\2\u0334\u0335\7G\2\2\u0335\u00b0\3\2\2\2\u0336\u0337"+
"\7V\2\2\u0337\u0338\7Q\2\2\u0338\u00b2\3\2\2\2\u0339\u033a\7V\2\2\u033a"+
"\u033b\7[\2\2\u033b\u033c\7R\2\2\u033c\u033d\7G\2\2\u033d\u00b4\3\2\2"+
"\2\u033e\u033f\7V\2\2\u033f\u0340\7[\2\2\u0340\u0341\7R\2\2\u0341\u0342"+
"\7G\2\2\u0342\u0343\7U\2\2\u0343\u00b6\3\2\2\2\u0344\u0345\7W\2\2\u0345"+
"\u0346\7U\2\2\u0346\u0347\7K\2\2\u0347\u0348\7P\2\2\u0348\u0349\7I\2\2"+
"\u0349\u00b8\3\2\2\2\u034a\u034b\7X\2\2\u034b\u034c\7G\2\2\u034c\u034d"+
"\7T\2\2\u034d\u034e\7K\2\2\u034e\u034f\7H\2\2\u034f\u0350\7[\2\2\u0350"+
"\u00ba\3\2\2\2\u0351\u0352\7Y\2\2\u0352\u0353\7J\2\2\u0353\u0354\7G\2"+
"\2\u0354\u0355\7P\2\2\u0355\u00bc\3\2\2\2\u0356\u0357\7Y\2\2\u0357\u0358"+
"\7J\2\2\u0358\u0359\7G\2\2\u0359\u035a\7T\2\2\u035a\u035b\7G\2\2\u035b"+
"\u00be\3\2\2\2\u035c\u035d\7Y\2\2\u035d\u035e\7K\2\2\u035e\u035f\7V\2"+
"\2\u035f\u0360\7J\2\2\u0360\u00c0\3\2\2\2\u0361\u0362\7[\2\2\u0362\u0363"+
"\7G\2\2\u0363\u0364\7C\2\2\u0364\u0365\7T\2\2\u0365\u00c2\3\2\2\2\u0366"+
"\u0367\7[\2\2\u0367\u0368\7G\2\2\u0368\u0369\7C\2\2\u0369\u036a\7T\2\2"+
"\u036a\u036b\7U\2\2\u036b\u00c4\3\2\2\2\u036c\u036d\7}\2\2\u036d\u036e"+
"\7G\2\2\u036e\u036f\7U\2\2\u036f\u0370\7E\2\2\u0370\u0371\7C\2\2\u0371"+
"\u0372\7R\2\2\u0372\u0373\7G\2\2\u0373\u00c6\3\2\2\2\u0374\u0375\7}\2"+
"\2\u0375\u0376\7H\2\2\u0376\u0377\7P\2\2\u0377\u00c8\3\2\2\2\u0378\u0379"+
"\7}\2\2\u0379\u037a\7N\2\2\u037a\u037b\7K\2\2\u037b\u037c\7O\2\2\u037c"+
"\u037d\7K\2\2\u037d\u037e\7V\2\2\u037e\u00ca\3\2\2\2\u037f\u0380\7}\2"+
"\2\u0380\u0381\7F\2\2\u0381\u00cc\3\2\2\2\u0382\u0383\7}\2\2\u0383\u0384"+
"\7V\2\2\u0384\u00ce\3\2\2\2\u0385\u0386\7}\2\2\u0386\u0387\7V\2\2\u0387"+
"\u0388\7U\2\2\u0388\u00d0\3\2\2\2\u0389\u038a\7}\2\2\u038a\u038b\7I\2"+
"\2\u038b\u038c\7W\2\2\u038c\u038d\7K\2\2\u038d\u038e\7F\2\2\u038e\u00d2"+
"\3\2\2\2\u038f\u0390\7\177\2\2\u0390\u00d4\3\2\2\2\u0391\u0392\7?\2\2"+
"\u0392\u00d6\3\2\2\2\u0393\u0394\7>\2\2\u0394\u0395\7?\2\2\u0395\u0396"+
"\7@\2\2\u0396\u00d8\3\2\2\2\u0397\u0398\7>\2\2\u0398\u039c\7@\2\2\u0399"+
"\u039a\7#\2\2\u039a\u039c\7?\2\2\u039b\u0397\3\2\2\2\u039b\u0399\3\2\2"+
"\2\u039c\u00da\3\2\2\2\u039d\u039e\7>\2\2\u039e\u00dc\3\2\2\2\u039f\u03a0"+
"\7>\2\2\u03a0\u03a1\7?\2\2\u03a1\u00de\3\2\2\2\u03a2\u03a3\7@\2\2\u03a3"+
"\u00e0\3\2\2\2\u03a4\u03a5\7@\2\2\u03a5\u03a6\7?\2\2\u03a6\u00e2\3\2\2"+
"\2\u03a7\u03a8\7-\2\2\u03a8\u00e4\3\2\2\2\u03a9\u03aa\7/\2\2\u03aa\u00e6"+
"\3\2\2\2\u03ab\u03ac\7,\2\2\u03ac\u00e8\3\2\2\2\u03ad\u03ae\7\61\2\2\u03ae"+
"\u00ea\3\2\2\2\u03af\u03b0\7\'\2\2\u03b0\u00ec\3\2\2\2\u03b1\u03b2\7<"+
"\2\2\u03b2\u03b3\7<\2\2\u03b3\u00ee\3\2\2\2\u03b4\u03b5\7~\2\2\u03b5\u03b6"+
"\7~\2\2\u03b6\u00f0\3\2\2\2\u03b7\u03b8\7\60\2\2\u03b8\u00f2\3\2\2\2\u03b9"+
"\u03ba\7A\2\2\u03ba\u00f4\3\2\2\2\u03bb\u03c1\7)\2\2\u03bc\u03c0\n\2\2"+
"\2\u03bd\u03be\7)\2\2\u03be\u03c0\7)\2\2\u03bf\u03bc\3\2\2\2\u03bf\u03bd"+
"\3\2\2\2\u03c0\u03c3\3\2\2\2\u03c1\u03bf\3\2\2\2\u03c1\u03c2\3\2\2\2\u03c2"+
"\u03c4\3\2\2\2\u03c3\u03c1\3\2\2\2\u03c4\u03c5\7)\2\2\u03c5\u00f6\3\2"+
"\2\2\u03c6\u03c8\5\u0107\u0084\2\u03c7\u03c6\3\2\2\2\u03c8\u03c9\3\2\2"+
"\2\u03c9\u03c7\3\2\2\2\u03c9\u03ca\3\2\2\2\u03ca\u00f8\3\2\2\2\u03cb\u03cd"+
"\5\u0107\u0084\2\u03cc\u03cb\3\2\2\2\u03cd\u03ce\3\2\2\2\u03ce\u03cc\3"+
"\2\2\2\u03ce\u03cf\3\2\2\2\u03cf\u03d0\3\2\2\2\u03d0\u03d4\5\u00f1y\2"+
"\u03d1\u03d3\5\u0107\u0084\2\u03d2\u03d1\3\2\2\2\u03d3\u03d6\3\2\2\2\u03d4"+
"\u03d2\3\2\2\2\u03d4\u03d5\3\2\2\2\u03d5\u03f6\3\2\2\2\u03d6\u03d4\3\2"+
"\2\2\u03d7\u03d9\5\u00f1y\2\u03d8\u03da\5\u0107\u0084\2\u03d9\u03d8\3"+
"\2\2\2\u03da\u03db\3\2\2\2\u03db\u03d9\3\2\2\2\u03db\u03dc\3\2\2\2\u03dc"+
"\u03f6\3\2\2\2\u03dd\u03df\5\u0107\u0084\2\u03de\u03dd\3\2\2\2\u03df\u03e0"+
"\3\2\2\2\u03e0\u03de\3\2\2\2\u03e0\u03e1\3\2\2\2\u03e1\u03e9\3\2\2\2\u03e2"+
"\u03e6\5\u00f1y\2\u03e3\u03e5\5\u0107\u0084\2\u03e4\u03e3\3\2\2\2\u03e5"+
"\u03e8\3\2\2\2\u03e6\u03e4\3\2\2\2\u03e6\u03e7\3\2\2\2\u03e7\u03ea\3\2"+
"\2\2\u03e8\u03e6\3\2\2\2\u03e9\u03e2\3\2\2\2\u03e9\u03ea\3\2\2\2\u03ea"+
"\u03eb\3\2\2\2\u03eb\u03ec\5\u0105\u0083\2\u03ec\u03f6\3\2\2\2\u03ed\u03ef"+
"\5\u00f1y\2\u03ee\u03f0\5\u0107\u0084\2\u03ef\u03ee\3\2\2\2\u03f0\u03f1"+
"\3\2\2\2\u03f1\u03ef\3\2\2\2\u03f1\u03f2\3\2\2\2\u03f2\u03f3\3\2\2\2\u03f3"+
"\u03f4\5\u0105\u0083\2\u03f4\u03f6\3\2\2\2\u03f5\u03cc\3\2\2\2\u03f5\u03d7"+
"\3\2\2\2\u03f5\u03de\3\2\2\2\u03f5\u03ed\3\2\2\2\u03f6\u00fa\3\2\2\2\u03f7"+
"\u03fa\5\u0109\u0085\2\u03f8\u03fa\7a\2\2\u03f9\u03f7\3\2\2\2\u03f9\u03f8"+
"\3\2\2\2\u03fa\u0400\3\2\2\2\u03fb\u03ff\5\u0109\u0085\2\u03fc\u03ff\5"+
"\u0107\u0084\2\u03fd\u03ff\t\3\2\2\u03fe\u03fb\3\2\2\2\u03fe\u03fc\3\2"+
"\2\2\u03fe\u03fd\3\2\2\2\u03ff\u0402\3\2\2\2\u0400\u03fe\3\2\2\2\u0400"+
"\u0401\3\2\2\2\u0401\u00fc\3\2\2\2\u0402\u0400\3\2\2\2\u0403\u0407\5\u0107"+
"\u0084\2\u0404\u0408\5\u0109\u0085\2\u0405\u0408\5\u0107\u0084\2\u0406"+
"\u0408\t\3\2\2\u0407\u0404\3\2\2\2\u0407\u0405\3\2\2\2\u0407\u0406\3\2"+
"\2\2\u0408\u0409\3\2\2\2\u0409\u0407\3\2\2\2\u0409\u040a\3\2\2\2\u040a"+
"\u00fe\3\2\2\2\u040b\u040f\5\u0109\u0085\2\u040c\u040f\5\u0107\u0084\2"+
"\u040d\u040f\7a\2\2\u040e\u040b\3\2\2\2\u040e\u040c\3\2\2\2\u040e\u040d"+
"\3\2\2\2\u040f\u0410\3\2\2\2\u0410\u040e\3\2\2\2\u0410\u0411\3\2\2\2\u0411"+
"\u0100\3\2\2\2\u0412\u0418\7$\2\2\u0413\u0417\n\4\2\2\u0414\u0415\7$\2"+
"\2\u0415\u0417\7$\2\2\u0416\u0413\3\2\2\2\u0416\u0414\3\2\2\2\u0417\u041a"+
"\3\2\2\2\u0418\u0416\3\2\2\2\u0418\u0419\3\2\2\2\u0419\u041b\3\2\2\2\u041a"+
"\u0418\3\2\2\2\u041b\u041c\7$\2\2\u041c\u0102\3\2\2\2\u041d\u0423\7b\2"+
"\2\u041e\u0422\n\5\2\2\u041f\u0420\7b\2\2\u0420\u0422\7b\2\2\u0421\u041e"+
"\3\2\2\2\u0421\u041f\3\2\2\2\u0422\u0425\3\2\2\2\u0423\u0421\3\2\2\2\u0423"+
"\u0424\3\2\2\2\u0424\u0426\3\2\2\2\u0425\u0423\3\2\2\2\u0426\u0427\7b"+
"\2\2\u0427\u0104\3\2\2\2\u0428\u042a\7G\2\2\u0429\u042b\t\6\2\2\u042a"+
"\u0429\3\2\2\2\u042a\u042b\3\2\2\2\u042b\u042d\3\2\2\2\u042c\u042e\5\u0107"+
"\u0084\2\u042d\u042c\3\2\2\2\u042e\u042f\3\2\2\2\u042f\u042d\3\2\2\2\u042f"+
"\u0430\3\2\2\2\u0430\u0106\3\2\2\2\u0431\u0432\t\7\2\2\u0432\u0108\3\2"+
"\2\2\u0433\u0434\t\b\2\2\u0434\u010a\3\2\2\2\u0435\u0436\7/\2\2\u0436"+
"\u0437\7/\2\2\u0437\u043b\3\2\2\2\u0438\u043a\n\t\2\2\u0439\u0438\3\2"+
"\2\2\u043a\u043d\3\2\2\2\u043b\u0439\3\2\2\2\u043b\u043c\3\2\2\2\u043c"+
"\u043f\3\2\2\2\u043d\u043b\3\2\2\2\u043e\u0440\7\17\2\2\u043f\u043e\3"+
"\2\2\2\u043f\u0440\3\2\2\2\u0440\u0442\3\2\2\2\u0441\u0443\7\f\2\2\u0442"+
"\u0441\3\2\2\2\u0442\u0443\3\2\2\2\u0443\u0444\3\2\2\2\u0444\u0445\b\u0086"+
"\2\2\u0445\u010c\3\2\2\2\u0446\u0447\7\61\2\2\u0447\u0448\7,\2\2\u0448"+
"\u044d\3\2\2\2\u0449\u044c\5\u010d\u0087\2\u044a\u044c\13\2\2\2\u044b"+
"\u0449\3\2\2\2\u044b\u044a\3\2\2\2\u044c\u044f\3\2\2\2\u044d\u044e\3\2"+
"\2\2\u044d\u044b\3\2\2\2\u044e\u0450\3\2\2\2\u044f\u044d\3\2\2\2\u0450"+
"\u0451\7,\2\2\u0451\u0452\7\61\2\2\u0452\u0453\3\2\2\2\u0453\u0454\b\u0087"+
"\2\2\u0454\u010e\3\2\2\2\u0455\u0457\t\n\2\2\u0456\u0455\3\2\2\2\u0457"+
"\u0458\3\2\2\2\u0458\u0456\3\2\2\2\u0458\u0459\3\2\2\2\u0459\u045a\3\2"+
"\2\2\u045a\u045b\b\u0088\2\2\u045b\u0110\3\2\2\2\u045c\u045d\13\2\2\2"+
"\u045d\u0112\3\2\2\2\"\2\u039b\u03bf\u03c1\u03c9\u03ce\u03d4\u03db\u03e0"+
"\u03e6\u03e9\u03f1\u03f5\u03f9\u03fe\u0400\u0407\u0409\u040e\u0410\u0416"+
"\u0418\u0421\u0423\u042a\u042f\u043b\u043f\u0442\u044b\u044d\u0458\3\2"+
"\3\2";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {

View File

@ -693,6 +693,18 @@ interface SqlBaseListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitSubqueryExpression(SqlBaseParser.SubqueryExpressionContext ctx);
/**
* Enter a parse tree produced by the {@code case}
* labeled alternative in {@link SqlBaseParser#primaryExpression}.
* @param ctx the parse tree
*/
void enterCase(SqlBaseParser.CaseContext ctx);
/**
* Exit a parse tree produced by the {@code case}
* labeled alternative in {@link SqlBaseParser#primaryExpression}.
* @param ctx the parse tree
*/
void exitCase(SqlBaseParser.CaseContext ctx);
/**
* Enter a parse tree produced by {@link SqlBaseParser#builtinDateTimeFunction}.
* @param ctx the parse tree
@ -1067,6 +1079,16 @@ interface SqlBaseListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitString(SqlBaseParser.StringContext ctx);
/**
* Enter a parse tree produced by {@link SqlBaseParser#whenClause}.
* @param ctx the parse tree
*/
void enterWhenClause(SqlBaseParser.WhenClauseContext ctx);
/**
* Exit a parse tree produced by {@link SqlBaseParser#whenClause}.
* @param ctx the parse tree
*/
void exitWhenClause(SqlBaseParser.WhenClauseContext ctx);
/**
* Enter a parse tree produced by {@link SqlBaseParser#nonReserved}.
* @param ctx the parse tree

View File

@ -414,6 +414,13 @@ interface SqlBaseVisitor<T> extends ParseTreeVisitor<T> {
* @return the visitor result
*/
T visitSubqueryExpression(SqlBaseParser.SubqueryExpressionContext ctx);
/**
* Visit a parse tree produced by the {@code case}
* labeled alternative in {@link SqlBaseParser#primaryExpression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitCase(SqlBaseParser.CaseContext ctx);
/**
* Visit a parse tree produced by {@link SqlBaseParser#builtinDateTimeFunction}.
* @param ctx the parse tree
@ -635,6 +642,12 @@ interface SqlBaseVisitor<T> extends ParseTreeVisitor<T> {
* @return the visitor result
*/
T visitString(SqlBaseParser.StringContext ctx);
/**
* Visit a parse tree produced by {@link SqlBaseParser#whenClause}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitWhenClause(SqlBaseParser.WhenClauseContext ctx);
/**
* Visit a parse tree produced by {@link SqlBaseParser#nonReserved}.
* @param ctx the parse tree

View File

@ -47,8 +47,9 @@ class org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalS
Boolean isNotNull(Object)
#
# Null
# Conditional
#
def caseFunction(java.util.List)
def coalesce(java.util.List)
def greatest(java.util.List)
def least(java.util.List)

View File

@ -598,6 +598,26 @@ public class VerifierErrorMessagesTests extends ESTestCase {
error("SELECT 1 = 1 OR " + arbirtraryArgsFunction + "(null, 3, '4') > 1"));
}
public void testCaseWithNonBooleanConditionExpression() {
assertEquals("1:8: condition of [WHEN abs(int) THEN 'foo'] must be [boolean], found value [abs(int)] type [integer]",
error("SELECT CASE WHEN int = 1 THEN 'one' WHEN abs(int) THEN 'foo' END FROM test"));
}
public void testCaseWithDifferentResultDataTypes() {
assertEquals("1:8: result of [WHEN int > 10 THEN 10] must be [keyword], found value [10] type [integer]",
error("SELECT CASE WHEN int > 20 THEN 'foo' WHEN int > 10 THEN 10 ELSE 'bar' END FROM test"));
}
public void testCaseWithDifferentResultAndDefaultValueDataTypes() {
assertEquals("1:8: ELSE clause of [date] must be [keyword], found value [date] type [datetime]",
error("SELECT CASE WHEN int > 20 THEN 'foo' ELSE date END FROM test"));
}
public void testCaseWithDifferentResultAndDefaultValueDataTypesAndNullTypesSkipped() {
assertEquals("1:8: ELSE clause of [date] must be [keyword], found value [date] type [datetime]",
error("SELECT CASE WHEN int > 20 THEN null WHEN int > 10 THEN null WHEN int > 5 THEN 'foo' ELSE date END FROM test"));
}
public void testAggsInWhere() {
assertEquals("1:33: Cannot use WHERE filtering on aggregate function [MAX(int)], use HAVING instead",
error("SELECT MAX(int) FROM test WHERE MAX(int) > 10 GROUP BY bool"));

View File

@ -0,0 +1,100 @@
/*
* 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.predicate.conditional;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.function.scalar.FunctionTestUtils;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.Equals;
import org.elasticsearch.xpack.sql.tree.AbstractNodeTestCase;
import org.elasticsearch.xpack.sql.tree.NodeSubclassTests;
import org.elasticsearch.xpack.sql.tree.Source;
import org.elasticsearch.xpack.sql.tree.SourceTests;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static org.elasticsearch.xpack.sql.expression.function.scalar.FunctionTestUtils.randomIntLiteral;
import static org.elasticsearch.xpack.sql.expression.function.scalar.FunctionTestUtils.randomStringLiteral;
import static org.elasticsearch.xpack.sql.tree.SourceTests.randomSource;
/**
* Needed to override tests in {@link NodeSubclassTests} as Case is special since its children are not usual
* expressions but {@link IfConditional}s.
*/
public class CaseTests extends AbstractNodeTestCase<Case, Expression> {
public static Case randomCase() {
int noConditionals = randomIntBetween(1, 5);
List<Expression> expressions = new ArrayList<>(noConditionals + 1);
for (int i = 0; i < noConditionals; i++) {
expressions.add(new IfConditional(
randomSource(), new Equals(randomSource(), randomStringLiteral(), randomStringLiteral()), randomIntLiteral()));
}
// default else
expressions.add(randomIntLiteral());
return new Case(randomSource(), expressions);
}
@Override
protected Case randomInstance() {
return randomCase();
}
@Override
protected Case mutate(Case instance) {
Case c = randomCase();
return new Case(c.source(), mutateChildren(c));
}
@Override
protected Case copy(Case instance) {
return new Case(instance.source(), instance.children());
}
@Override
public void testTransform() {
Case c = randomCase();
Source newSource = randomValueOtherThan(c.source(), SourceTests::randomSource);
assertEquals(new Case(c.source(), c.children()),
c.transformPropertiesOnly(p -> Objects.equals(p, c.source()) ? newSource: p, Object.class));
String newName = randomValueOtherThan(c.name(), () -> randomAlphaOfLength(5));
assertEquals(new Case(c.source(), c.children()),
c.transformPropertiesOnly(p -> Objects.equals(p, c.name()) ? newName : p, Object.class));
}
@Override
public void testReplaceChildren() {
Case c = randomCase();
List<Expression> newChildren = mutateChildren(c);
assertEquals(new Case(c.source(), newChildren), c.replaceChildren(newChildren));
}
private List<Expression> mutateChildren(Case c) {
boolean removeConditional = randomBoolean();
List<Expression> expressions = new ArrayList<>(c.children().size());
if (removeConditional) {
expressions.addAll(c.children().subList(0, c.children().size() - 2));
} else {
int rndIdx = randomInt(c.conditions().size());
for (int i = 0; i < c.conditions().size(); i++) {
if (i == rndIdx) {
expressions.add(new IfConditional(randomValueOtherThan(c.conditions().get(i).source(), SourceTests::randomSource),
new Equals(randomSource(), randomStringLiteral(), randomStringLiteral()),
randomValueOtherThan(c.conditions().get(i).condition(), FunctionTestUtils::randomStringLiteral)));
} else {
expressions.add(c.conditions().get(i));
}
}
}
expressions.add(c.defaultElse());
return expressions;
}
}

View File

@ -44,9 +44,11 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.string.Repeat;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
import org.elasticsearch.xpack.sql.expression.predicate.Range;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.ArbitraryConditionalFunction;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Case;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Coalesce;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.ConditionalFunction;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Greatest;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IfConditional;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IfNull;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Least;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.NullIf;
@ -112,9 +114,12 @@ import static org.elasticsearch.xpack.sql.expression.Literal.FALSE;
import static org.elasticsearch.xpack.sql.expression.Literal.NULL;
import static org.elasticsearch.xpack.sql.expression.Literal.TRUE;
import static org.elasticsearch.xpack.sql.expression.Literal.of;
import static org.elasticsearch.xpack.sql.optimizer.Optimizer.SimplifyCase;
import static org.elasticsearch.xpack.sql.optimizer.Optimizer.SortAggregateOnOrderBy;
import static org.elasticsearch.xpack.sql.tree.Source.EMPTY;
import static org.elasticsearch.xpack.sql.util.DateUtils.UTC;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.startsWith;
public class OptimizerTests extends ESTestCase {
@ -595,6 +600,91 @@ public class OptimizerTests extends ESTestCase {
assertEquals(StringUtils.EMPTY, foldNull.rule(new Concat(EMPTY, NULL, NULL)).fold());
}
public void testSimplifyCaseConditionsFoldWhenFalse() {
// CASE WHEN a = 1 THEN 'foo1'
// WHEN 1 = 2 THEN 'bar1'
// WHEN 2 = 1 THEN 'bar2'
// WHEN a > 1 THEN 'foo2'
// ELSE 'default'
// END
//
// ==>
//
// CASE WHEN a = 1 THEN 'foo1'
// WHEN a > 1 THEN 'foo2'
// ELSE 'default'
// END
Case c = new Case(EMPTY, Arrays.asList(
new IfConditional(EMPTY, new Equals(EMPTY, getFieldAttribute(), ONE), Literal.of(EMPTY, "foo1")),
new IfConditional(EMPTY, new Equals(EMPTY, ONE, TWO), Literal.of(EMPTY, "bar1")),
new IfConditional(EMPTY, new Equals(EMPTY, TWO, ONE), Literal.of(EMPTY, "bar2")),
new IfConditional(EMPTY, new GreaterThan(EMPTY, getFieldAttribute(), ONE), Literal.of(EMPTY, "foo2")),
Literal.of(EMPTY, "default")));
Expression e = new SimplifyCase().rule(c);
assertEquals(Case.class, e.getClass());
c = (Case) e;
assertEquals(2, c.conditions().size());
assertThat(c.conditions().get(0).condition().toString(), startsWith("Equals[a{f}#"));
assertThat(c.conditions().get(1).condition().toString(), startsWith("GreaterThan[a{f}#"));
assertFalse(c.foldable());
}
public void testSimplifyCaseConditionsFoldWhenTrue() {
// CASE WHEN a = 1 THEN 'foo1'
// WHEN 1 = 1 THEN 'bar1'
// WHEN 2 = 1 THEN 'bar2'
// WHEN a > 1 THEN 'foo2'
// ELSE 'default'
// END
//
// ==>
//
// CASE WHEN a = 1 THEN 'foo1'
// WHEN 1 = 1 THEN 'bar1'
// ELSE 'default'
// END
SimplifyCase rule = new SimplifyCase();
Case c = new Case(EMPTY, Arrays.asList(
new IfConditional(EMPTY, new Equals(EMPTY, getFieldAttribute(), ONE), Literal.of(EMPTY, "foo1")),
new IfConditional(EMPTY, new Equals(EMPTY, ONE, ONE), Literal.of(EMPTY, "bar1")),
new IfConditional(EMPTY, new Equals(EMPTY, TWO, ONE), Literal.of(EMPTY, "bar2")),
new IfConditional(EMPTY, new GreaterThan(EMPTY, getFieldAttribute(), ONE), Literal.of(EMPTY, "foo2")),
Literal.of(EMPTY, "default")));
Expression e = rule.rule(c);
assertEquals(Case.class, e.getClass());
c = (Case) e;
assertEquals(2, c.conditions().size());
assertThat(c.conditions().get(0).condition().toString(), startsWith("Equals[a{f}#"));
assertThat(c.conditions().get(1).condition().toString(), startsWith("Equals[=1,=1]#"));
assertFalse(c.foldable());
}
public void testSimplifyCaseConditionsFoldCompletely() {
// CASE WHEN 1 = 2 THEN 'foo1'
// WHEN 1 = 1 THEN 'foo2'
// ELSE 'default'
// END
//
// ==>
//
// 'foo2'
SimplifyCase rule = new SimplifyCase();
Case c = new Case(EMPTY, Arrays.asList(
new IfConditional(EMPTY, new Equals(EMPTY, ONE, TWO), Literal.of(EMPTY, "foo1")),
new IfConditional(EMPTY, new Equals(EMPTY, ONE, ONE), Literal.of(EMPTY, "foo2")),
Literal.of(EMPTY, "default")));
Expression e = rule.rule(c);
assertEquals(Case.class, e.getClass());
c = (Case) e;
assertEquals(1, c.conditions().size());
assertThat(c.conditions().get(0).condition().toString(), startsWith("Equals[=1,=1]#"));
assertTrue(c.foldable());
assertEquals("foo2", c.fold());
}
//
// Logical simplifications
//
@ -1318,7 +1408,7 @@ public class OptimizerTests extends ESTestCase {
OrderBy orderByPlan = new OrderBy(EMPTY,
new Aggregate(EMPTY, FROM(), Arrays.asList(secondField, firstField), Arrays.asList(secondAlias, firstAlias)),
Arrays.asList(firstOrderBy, secondOrderBy));
LogicalPlan result = new Optimizer.SortAggregateOnOrderBy().apply(orderByPlan);
LogicalPlan result = new SortAggregateOnOrderBy().apply(orderByPlan);
assertTrue(result instanceof OrderBy);
List<Order> order = ((OrderBy) result).order();
@ -1350,7 +1440,7 @@ public class OptimizerTests extends ESTestCase {
OrderBy orderByPlan = new OrderBy(EMPTY,
new Aggregate(EMPTY, FROM(), Arrays.asList(secondAlias, firstAlias), Arrays.asList(secondAlias, firstAlias)),
Arrays.asList(firstOrderBy, secondOrderBy));
LogicalPlan result = new Optimizer.SortAggregateOnOrderBy().apply(orderByPlan);
LogicalPlan result = new SortAggregateOnOrderBy().apply(orderByPlan);
assertTrue(result instanceof OrderBy);
List<Order> order = ((OrderBy) result).order();

View File

@ -11,6 +11,8 @@ import org.elasticsearch.xpack.sql.expression.Literal;
import org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction;
import org.elasticsearch.xpack.sql.expression.function.scalar.Cast;
import org.elasticsearch.xpack.sql.expression.literal.Interval;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Case;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IfConditional;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Add;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Mul;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Neg;
@ -26,6 +28,7 @@ import java.time.temporal.TemporalAmount;
import java.util.Locale;
import static java.lang.String.format;
import static org.hamcrest.Matchers.startsWith;
public class ExpressionTests extends ESTestCase {
@ -447,4 +450,63 @@ public class ExpressionTests extends ESTestCase {
Expression expr = parser.createExpression(s);
assertEquals(s, expr.sourceText());
}
public void testCaseWithoutOperand() {
Expression expr = parser.createExpression(
"CASE WHEN a = 1 THEN 'one'" +
" WHEN a > 2 THEN 'a few'" +
" WHEN a > 10 THEN 'many' " +
"END");
assertEquals(Case.class, expr.getClass());
Case c = (Case) expr;
assertEquals(3, c.conditions().size());
IfConditional ifc = c.conditions().get(0);
assertEquals("WHEN a = 1 THEN 'one'", ifc.sourceText());
assertThat(ifc.condition().toString(), startsWith("Equals[?a,1]#"));
assertEquals("'one'=one", ifc.result().toString());
assertEquals(Literal.NULL, c.defaultElse());
expr = parser.createExpression(
"CASE WHEN a = 1 THEN 'one'" +
" WHEN a <= 2 THEN 'a few'" +
"ELSE 'many' " +
"END");
assertEquals(Case.class, expr.getClass());
c = (Case) expr;
assertEquals(2, c.conditions().size());
ifc = c.conditions().get(0);
assertEquals("WHEN a = 1 THEN 'one'", ifc.sourceText());
assertEquals("'many'=many", c.defaultElse().toString());
}
public void testCaseWithOperand() {
Expression expr = parser.createExpression(
"CASE a WHEN 1 THEN 'one'" +
" WHEN 2 THEN 'two'" +
" WHEN 3 THEN 'three' " +
"END");
assertEquals(Case.class, expr.getClass());
Case c = (Case) expr;
assertEquals(3, c.conditions().size());
IfConditional ifc = c.conditions().get(0);
assertEquals("WHEN 1 THEN 'one'", ifc.sourceText());
assertThat(ifc.condition().toString(), startsWith("Equals[?a,1]#"));
assertEquals("'one'=one", ifc.result().toString());
assertEquals(Literal.NULL, c.defaultElse());
expr = parser.createExpression(
"CASE a WHEN 1 THEN 'one'" +
" WHEN 2 THEN 'two'" +
"ELSE 'many' " +
"END");
assertEquals(Case.class, expr.getClass());
c = (Case) expr;
assertEquals(2, c.conditions().size());
ifc = c.conditions().get(0);
assertEquals("WHEN 1 THEN 'one'", ifc.sourceText());
assertEquals("'many'=many", c.defaultElse().toString());
}
}

View File

@ -587,6 +587,21 @@ public class QueryTranslatorTests extends ESTestCase {
assertEquals("[{v=int}, {v=10}]", scriptTemplate.params().toString());
}
public void testTranslateCase_GroupBy_Painless() {
LogicalPlan p = plan("SELECT CASE WHEN int > 10 THEN 'foo' WHEN int > 20 THEN 'bar' ELSE 'default' END FROM test GROUP BY 1");
assertTrue(p instanceof Aggregate);
Expression condition = ((Aggregate) p).groupings().get(0);
assertFalse(condition.foldable());
QueryTranslator.GroupingContext groupingContext = QueryTranslator.groupBy(((Aggregate) p).groupings());
assertNotNull(groupingContext);
ScriptTemplate scriptTemplate = groupingContext.tail.script();
assertEquals("InternalSqlScriptUtils.caseFunction([InternalSqlScriptUtils.gt(InternalSqlScriptUtils.docValue(" + "" +
"doc,params.v0),params.v1),params.v2,InternalSqlScriptUtils.gt(InternalSqlScriptUtils.docValue(doc,params.v3)," +
"params.v4),params.v5,params.v6])",
scriptTemplate.toString());
assertEquals("[{v=int}, {v=10}, {v=foo}, {v=int}, {v=20}, {v=bar}, {v=default}]", scriptTemplate.params().toString());
}
public void testGroupByDateHistogram() {
LogicalPlan p = plan("SELECT MAX(int) FROM test GROUP BY HISTOGRAM(int, 1000)");
assertTrue(p instanceof Aggregate);

View File

@ -6,7 +6,6 @@
package org.elasticsearch.xpack.sql.tree;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.test.ESTestCase;
@ -29,6 +28,7 @@ import org.elasticsearch.xpack.sql.expression.gen.pipeline.BinaryPipesTests;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
import org.elasticsearch.xpack.sql.expression.gen.processor.ConstantProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IfConditional;
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IfNull;
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.FullTextPredicate;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.In;
@ -92,8 +92,8 @@ import static org.mockito.Mockito.mock;
*/
public class NodeSubclassTests<T extends B, B extends Node<B>> extends ESTestCase {
private static final List<Class<?>> CLASSES_WITH_MIN_TWO_CHILDREN = Arrays.asList(IfNull.class, In.class, InPipe.class,
Percentile.class, Percentiles.class, PercentileRanks.class);
private static final List<Class<?>> CLASSES_WITH_MIN_TWO_CHILDREN = Arrays.asList(IfConditional.class, IfNull.class,
In.class, InPipe.class, Percentile.class, Percentiles.class, PercentileRanks.class);
private final Class<T> subclass;