SQL: Introduce support for NULL values (#34573)

Make SQL aware of missing and/or unmapped fields treating them as NULL
Make _all_ functions and operators null-safe aware, including when used
in filtering or sorting contexts
Add missing and null-safe doc value extractor
Modify dataset to have null fields spread around (in groups of 10)
Enforce missing last and unmapped_type inside sorting
Consolidate Predicate templating and declaration
Add support for Like/RLike in scripting
Generalize NULLS LAST/FIRST
Introduce early schema declaration for CSV spec tests: to keep the doc
snippets in place (introduce schema:: prefix for declaration)
upfront.

Fix #32079
This commit is contained in:
Costin Leau 2018-10-19 16:44:33 +03:00 committed by GitHub
parent 4236358f5d
commit 52104aac27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
118 changed files with 3373 additions and 2945 deletions

View File

@ -43,7 +43,9 @@ Which returns:
"sort" : [
{
"page_count" : {
"order" : "desc"
"order" : "desc",
"missing" : "_first",
"unmapped_type" : "short"
}
}
]

View File

@ -88,7 +88,7 @@ queryTerm
;
orderBy
: expression ordering=(ASC | DESC)?
: expression ordering=(ASC | DESC)? (NULLS nullOrdering=(FIRST | LAST))?
;
querySpecification
@ -357,6 +357,7 @@ EXISTS: 'EXISTS';
EXPLAIN: 'EXPLAIN';
EXTRACT: 'EXTRACT';
FALSE: 'FALSE';
FIRST: 'FIRST';
FORMAT: 'FORMAT';
FROM: 'FROM';
FULL: 'FULL';
@ -368,6 +369,7 @@ IN: 'IN';
INNER: 'INNER';
IS: 'IS';
JOIN: 'JOIN';
LAST: 'LAST';
LEFT: 'LEFT';
LIKE: 'LIKE';
LIMIT: 'LIMIT';
@ -376,6 +378,7 @@ MATCH: 'MATCH';
NATURAL: 'NATURAL';
NOT: 'NOT';
NULL: 'NULL';
NULLS: 'NULLS';
ON: 'ON';
OPTIMIZED: 'OPTIMIZED';
OR: 'OR';

View File

@ -25,85 +25,88 @@ EXISTS=24
EXPLAIN=25
EXTRACT=26
FALSE=27
FORMAT=28
FROM=29
FULL=30
FUNCTIONS=31
GRAPHVIZ=32
GROUP=33
HAVING=34
IN=35
INNER=36
IS=37
JOIN=38
LEFT=39
LIKE=40
LIMIT=41
MAPPED=42
MATCH=43
NATURAL=44
NOT=45
NULL=46
ON=47
OPTIMIZED=48
OR=49
ORDER=50
OUTER=51
PARSED=52
PHYSICAL=53
PLAN=54
RIGHT=55
RLIKE=56
QUERY=57
SCHEMAS=58
SELECT=59
SHOW=60
SYS=61
TABLE=62
TABLES=63
TEXT=64
TRUE=65
TYPE=66
TYPES=67
USING=68
VERIFY=69
WHERE=70
WITH=71
ESCAPE_ESC=72
FUNCTION_ESC=73
LIMIT_ESC=74
DATE_ESC=75
TIME_ESC=76
TIMESTAMP_ESC=77
GUID_ESC=78
ESC_END=79
EQ=80
NEQ=81
LT=82
LTE=83
GT=84
GTE=85
PLUS=86
MINUS=87
ASTERISK=88
SLASH=89
PERCENT=90
CONCAT=91
DOT=92
PARAM=93
STRING=94
INTEGER_VALUE=95
DECIMAL_VALUE=96
IDENTIFIER=97
DIGIT_IDENTIFIER=98
TABLE_IDENTIFIER=99
QUOTED_IDENTIFIER=100
BACKQUOTED_IDENTIFIER=101
SIMPLE_COMMENT=102
BRACKETED_COMMENT=103
WS=104
UNRECOGNIZED=105
DELIMITER=106
FIRST=28
FORMAT=29
FROM=30
FULL=31
FUNCTIONS=32
GRAPHVIZ=33
GROUP=34
HAVING=35
IN=36
INNER=37
IS=38
JOIN=39
LAST=40
LEFT=41
LIKE=42
LIMIT=43
MAPPED=44
MATCH=45
NATURAL=46
NOT=47
NULL=48
NULLS=49
ON=50
OPTIMIZED=51
OR=52
ORDER=53
OUTER=54
PARSED=55
PHYSICAL=56
PLAN=57
RIGHT=58
RLIKE=59
QUERY=60
SCHEMAS=61
SELECT=62
SHOW=63
SYS=64
TABLE=65
TABLES=66
TEXT=67
TRUE=68
TYPE=69
TYPES=70
USING=71
VERIFY=72
WHERE=73
WITH=74
ESCAPE_ESC=75
FUNCTION_ESC=76
LIMIT_ESC=77
DATE_ESC=78
TIME_ESC=79
TIMESTAMP_ESC=80
GUID_ESC=81
ESC_END=82
EQ=83
NEQ=84
LT=85
LTE=86
GT=87
GTE=88
PLUS=89
MINUS=90
ASTERISK=91
SLASH=92
PERCENT=93
CONCAT=94
DOT=95
PARAM=96
STRING=97
INTEGER_VALUE=98
DECIMAL_VALUE=99
IDENTIFIER=100
DIGIT_IDENTIFIER=101
TABLE_IDENTIFIER=102
QUOTED_IDENTIFIER=103
BACKQUOTED_IDENTIFIER=104
SIMPLE_COMMENT=105
BRACKETED_COMMENT=106
WS=107
UNRECOGNIZED=108
DELIMITER=109
'('=1
')'=2
','=3
@ -131,68 +134,71 @@ DELIMITER=106
'EXPLAIN'=25
'EXTRACT'=26
'FALSE'=27
'FORMAT'=28
'FROM'=29
'FULL'=30
'FUNCTIONS'=31
'GRAPHVIZ'=32
'GROUP'=33
'HAVING'=34
'IN'=35
'INNER'=36
'IS'=37
'JOIN'=38
'LEFT'=39
'LIKE'=40
'LIMIT'=41
'MAPPED'=42
'MATCH'=43
'NATURAL'=44
'NOT'=45
'NULL'=46
'ON'=47
'OPTIMIZED'=48
'OR'=49
'ORDER'=50
'OUTER'=51
'PARSED'=52
'PHYSICAL'=53
'PLAN'=54
'RIGHT'=55
'RLIKE'=56
'QUERY'=57
'SCHEMAS'=58
'SELECT'=59
'SHOW'=60
'SYS'=61
'TABLE'=62
'TABLES'=63
'TEXT'=64
'TRUE'=65
'TYPE'=66
'TYPES'=67
'USING'=68
'VERIFY'=69
'WHERE'=70
'WITH'=71
'{ESCAPE'=72
'{FN'=73
'{LIMIT'=74
'{D'=75
'{T'=76
'{TS'=77
'{GUID'=78
'}'=79
'='=80
'<'=82
'<='=83
'>'=84
'>='=85
'+'=86
'-'=87
'*'=88
'/'=89
'%'=90
'||'=91
'.'=92
'?'=93
'FIRST'=28
'FORMAT'=29
'FROM'=30
'FULL'=31
'FUNCTIONS'=32
'GRAPHVIZ'=33
'GROUP'=34
'HAVING'=35
'IN'=36
'INNER'=37
'IS'=38
'JOIN'=39
'LAST'=40
'LEFT'=41
'LIKE'=42
'LIMIT'=43
'MAPPED'=44
'MATCH'=45
'NATURAL'=46
'NOT'=47
'NULL'=48
'NULLS'=49
'ON'=50
'OPTIMIZED'=51
'OR'=52
'ORDER'=53
'OUTER'=54
'PARSED'=55
'PHYSICAL'=56
'PLAN'=57
'RIGHT'=58
'RLIKE'=59
'QUERY'=60
'SCHEMAS'=61
'SELECT'=62
'SHOW'=63
'SYS'=64
'TABLE'=65
'TABLES'=66
'TEXT'=67
'TRUE'=68
'TYPE'=69
'TYPES'=70
'USING'=71
'VERIFY'=72
'WHERE'=73
'WITH'=74
'{ESCAPE'=75
'{FN'=76
'{LIMIT'=77
'{D'=78
'{T'=79
'{TS'=80
'{GUID'=81
'}'=82
'='=83
'<'=85
'<='=86
'>'=87
'>='=88
'+'=89
'-'=90
'*'=91
'/'=92
'%'=93
'||'=94
'.'=95
'?'=96

View File

@ -25,84 +25,87 @@ EXISTS=24
EXPLAIN=25
EXTRACT=26
FALSE=27
FORMAT=28
FROM=29
FULL=30
FUNCTIONS=31
GRAPHVIZ=32
GROUP=33
HAVING=34
IN=35
INNER=36
IS=37
JOIN=38
LEFT=39
LIKE=40
LIMIT=41
MAPPED=42
MATCH=43
NATURAL=44
NOT=45
NULL=46
ON=47
OPTIMIZED=48
OR=49
ORDER=50
OUTER=51
PARSED=52
PHYSICAL=53
PLAN=54
RIGHT=55
RLIKE=56
QUERY=57
SCHEMAS=58
SELECT=59
SHOW=60
SYS=61
TABLE=62
TABLES=63
TEXT=64
TRUE=65
TYPE=66
TYPES=67
USING=68
VERIFY=69
WHERE=70
WITH=71
ESCAPE_ESC=72
FUNCTION_ESC=73
LIMIT_ESC=74
DATE_ESC=75
TIME_ESC=76
TIMESTAMP_ESC=77
GUID_ESC=78
ESC_END=79
EQ=80
NEQ=81
LT=82
LTE=83
GT=84
GTE=85
PLUS=86
MINUS=87
ASTERISK=88
SLASH=89
PERCENT=90
CONCAT=91
DOT=92
PARAM=93
STRING=94
INTEGER_VALUE=95
DECIMAL_VALUE=96
IDENTIFIER=97
DIGIT_IDENTIFIER=98
TABLE_IDENTIFIER=99
QUOTED_IDENTIFIER=100
BACKQUOTED_IDENTIFIER=101
SIMPLE_COMMENT=102
BRACKETED_COMMENT=103
WS=104
UNRECOGNIZED=105
FIRST=28
FORMAT=29
FROM=30
FULL=31
FUNCTIONS=32
GRAPHVIZ=33
GROUP=34
HAVING=35
IN=36
INNER=37
IS=38
JOIN=39
LAST=40
LEFT=41
LIKE=42
LIMIT=43
MAPPED=44
MATCH=45
NATURAL=46
NOT=47
NULL=48
NULLS=49
ON=50
OPTIMIZED=51
OR=52
ORDER=53
OUTER=54
PARSED=55
PHYSICAL=56
PLAN=57
RIGHT=58
RLIKE=59
QUERY=60
SCHEMAS=61
SELECT=62
SHOW=63
SYS=64
TABLE=65
TABLES=66
TEXT=67
TRUE=68
TYPE=69
TYPES=70
USING=71
VERIFY=72
WHERE=73
WITH=74
ESCAPE_ESC=75
FUNCTION_ESC=76
LIMIT_ESC=77
DATE_ESC=78
TIME_ESC=79
TIMESTAMP_ESC=80
GUID_ESC=81
ESC_END=82
EQ=83
NEQ=84
LT=85
LTE=86
GT=87
GTE=88
PLUS=89
MINUS=90
ASTERISK=91
SLASH=92
PERCENT=93
CONCAT=94
DOT=95
PARAM=96
STRING=97
INTEGER_VALUE=98
DECIMAL_VALUE=99
IDENTIFIER=100
DIGIT_IDENTIFIER=101
TABLE_IDENTIFIER=102
QUOTED_IDENTIFIER=103
BACKQUOTED_IDENTIFIER=104
SIMPLE_COMMENT=105
BRACKETED_COMMENT=106
WS=107
UNRECOGNIZED=108
'('=1
')'=2
','=3
@ -130,68 +133,71 @@ UNRECOGNIZED=105
'EXPLAIN'=25
'EXTRACT'=26
'FALSE'=27
'FORMAT'=28
'FROM'=29
'FULL'=30
'FUNCTIONS'=31
'GRAPHVIZ'=32
'GROUP'=33
'HAVING'=34
'IN'=35
'INNER'=36
'IS'=37
'JOIN'=38
'LEFT'=39
'LIKE'=40
'LIMIT'=41
'MAPPED'=42
'MATCH'=43
'NATURAL'=44
'NOT'=45
'NULL'=46
'ON'=47
'OPTIMIZED'=48
'OR'=49
'ORDER'=50
'OUTER'=51
'PARSED'=52
'PHYSICAL'=53
'PLAN'=54
'RIGHT'=55
'RLIKE'=56
'QUERY'=57
'SCHEMAS'=58
'SELECT'=59
'SHOW'=60
'SYS'=61
'TABLE'=62
'TABLES'=63
'TEXT'=64
'TRUE'=65
'TYPE'=66
'TYPES'=67
'USING'=68
'VERIFY'=69
'WHERE'=70
'WITH'=71
'{ESCAPE'=72
'{FN'=73
'{LIMIT'=74
'{D'=75
'{T'=76
'{TS'=77
'{GUID'=78
'}'=79
'='=80
'<'=82
'<='=83
'>'=84
'>='=85
'+'=86
'-'=87
'*'=88
'/'=89
'%'=90
'||'=91
'.'=92
'?'=93
'FIRST'=28
'FORMAT'=29
'FROM'=30
'FULL'=31
'FUNCTIONS'=32
'GRAPHVIZ'=33
'GROUP'=34
'HAVING'=35
'IN'=36
'INNER'=37
'IS'=38
'JOIN'=39
'LAST'=40
'LEFT'=41
'LIKE'=42
'LIMIT'=43
'MAPPED'=44
'MATCH'=45
'NATURAL'=46
'NOT'=47
'NULL'=48
'NULLS'=49
'ON'=50
'OPTIMIZED'=51
'OR'=52
'ORDER'=53
'OUTER'=54
'PARSED'=55
'PHYSICAL'=56
'PLAN'=57
'RIGHT'=58
'RLIKE'=59
'QUERY'=60
'SCHEMAS'=61
'SELECT'=62
'SHOW'=63
'SYS'=64
'TABLE'=65
'TABLES'=66
'TEXT'=67
'TRUE'=68
'TYPE'=69
'TYPES'=70
'USING'=71
'VERIFY'=72
'WHERE'=73
'WITH'=74
'{ESCAPE'=75
'{FN'=76
'{LIMIT'=77
'{D'=78
'{T'=79
'{TS'=80
'{GUID'=81
'}'=82
'='=83
'<'=85
'<='=86
'>'=87
'>='=88
'+'=89
'-'=90
'*'=91
'/'=92
'%'=93
'||'=94
'.'=95
'?'=96

View File

@ -487,7 +487,8 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
if (ordinal != null) {
changed = true;
if (ordinal > 0 && ordinal <= max) {
newOrder.add(new Order(order.location(), orderBy.child().output().get(ordinal - 1), order.direction()));
newOrder.add(new Order(order.location(), orderBy.child().output().get(ordinal - 1), order.direction(),
order.nullsPosition()));
}
else {
throw new AnalysisException(order, "Invalid %d specified in OrderBy (valid range is [1, %d])", ordinal, max);

View File

@ -110,9 +110,15 @@ public abstract class SourceGenerator {
FieldAttribute fa = (FieldAttribute) attr;
fa = fa.isInexact() ? fa.exactAttribute() : fa;
sortBuilder = fieldSort(fa.name());
sortBuilder = fieldSort(fa.name())
.missing(as.missing().position())
.unmappedType(fa.dataType().esType);
if (fa.isNested()) {
FieldSortBuilder fieldSort = fieldSort(fa.name());
FieldSortBuilder fieldSort = fieldSort(fa.name())
.missing(as.missing().position())
.unmappedType(fa.dataType().esType);
NestedSortBuilder newSort = new NestedSortBuilder(fa.nestedParent().name());
NestedSortBuilder nestedSort = fieldSort.getNestedSort();

View File

@ -107,7 +107,9 @@ public class CompositeKeyExtractor implements BucketExtractor {
Object object = ((Map<?, ?>) m).get(key);
if (timeZone != null) {
if (object instanceof Long) {
if (object == null) {
return object;
} else if (object instanceof Long) {
object = new DateTime(((Long) object).longValue(), DateTimeZone.forTimeZone(timeZone));
} else {
throw new SqlIllegalArgumentException("Invalid date key returned: {}", object);

View File

@ -20,18 +20,24 @@ public class Order extends Expression {
ASC, DESC
}
public enum NullsPosition {
FIRST, LAST;
}
private final Expression child;
private final OrderDirection direction;
private final NullsPosition nulls;
public Order(Location location, Expression child, OrderDirection direction) {
public Order(Location location, Expression child, OrderDirection direction, NullsPosition nulls) {
super(location, singletonList(child));
this.child = child;
this.direction = direction;
this.nulls = nulls == null ? (direction == OrderDirection.DESC ? NullsPosition.FIRST : NullsPosition.LAST) : nulls;
}
@Override
protected NodeInfo<Order> info() {
return NodeInfo.create(this, Order::new, child, direction);
return NodeInfo.create(this, Order::new, child, direction, nulls);
}
@Override
@ -49,7 +55,7 @@ public class Order extends Expression {
if (newChildren.size() != 1) {
throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]");
}
return new Order(location(), newChildren.get(0), direction);
return new Order(location(), newChildren.get(0), direction, nulls);
}
public Expression child() {
@ -60,6 +66,10 @@ public class Order extends Expression {
return direction;
}
public NullsPosition nullsPosition() {
return nulls;
}
@Override
public boolean foldable() {
return false;
@ -67,7 +77,7 @@ public class Order extends Expression {
@Override
public int hashCode() {
return Objects.hash(child, direction);
return Objects.hash(child, direction, nulls);
}
@Override
@ -82,6 +92,7 @@ public class Order extends Expression {
Order other = (Order) obj;
return Objects.equals(direction, other.direction)
&& Objects.equals(nulls, other.nulls)
&& Objects.equals(child, other.child);
}
}

View File

@ -7,10 +7,12 @@ package org.elasticsearch.xpack.sql.expression.function.scalar;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.expression.gen.script.Scripts;
import org.elasticsearch.xpack.sql.tree.Location;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
public abstract class BinaryScalarFunction extends ScalarFunction {
@ -53,5 +55,11 @@ public abstract class BinaryScalarFunction extends ScalarFunction {
return asScriptFrom(leftScript, rightScript);
}
protected abstract ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript);
protected ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript) {
return Scripts.binaryMethod(scriptMethodName(), leftScript, rightScript, dataType());
}
protected String scriptMethodName() {
return getClass().getSimpleName().toLowerCase(Locale.ROOT);
}
}

View File

@ -12,7 +12,9 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunctio
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType;
import org.joda.time.DateTime;
import java.util.Objects;
import java.util.TimeZone;
abstract class BaseDateTimeFunction extends UnaryScalarFunction {
@ -60,4 +62,32 @@ abstract class BaseDateTimeFunction extends UnaryScalarFunction {
public boolean foldable() {
return field().foldable();
}
}
@Override
public Object fold() {
DateTime folded = (DateTime) field().fold();
if (folded == null) {
return null;
}
return doFold(folded.getMillis(), timeZone().getID());
}
protected abstract Object doFold(long millis, String tzId);
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
BaseDateTimeFunction other = (BaseDateTimeFunction) obj;
return Objects.equals(other.field(), field())
&& Objects.equals(other.timeZone(), timeZone());
}
@Override
public int hashCode() {
return Objects.hash(field(), timeZone());
}
}

View File

@ -15,13 +15,11 @@ import org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType;
import org.joda.time.DateTime;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoField;
import java.util.Objects;
import java.util.TimeZone;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
@ -33,13 +31,8 @@ public abstract class DateTimeFunction extends BaseDateTimeFunction {
}
@Override
public Object fold() {
DateTime folded = (DateTime) field().fold();
if (folded == null) {
return null;
}
return dateTimeChrono(folded.getMillis(), timeZone().getID(), chronoField().name());
protected Object doFold(long millis, String tzId) {
return dateTimeChrono(millis, tzId, chronoField().name());
}
public static Integer dateTimeChrono(long millis, String tzId, String chronoName) {
@ -47,12 +40,17 @@ public abstract class DateTimeFunction extends BaseDateTimeFunction {
return Integer.valueOf(time.get(ChronoField.valueOf(chronoName)));
}
public static Integer dateTimeChrono(ZonedDateTime millis, String tzId, String chronoName) {
ZonedDateTime time = millis.withZoneSameInstant(ZoneId.of(tzId));
return Integer.valueOf(time.get(ChronoField.valueOf(chronoName)));
}
@Override
public ScriptTemplate scriptWithField(FieldAttribute field) {
ParamsBuilder params = paramsBuilder();
String template = null;
template = formatTemplate("{sql}.dateTimeChrono(doc[{}].value.millis, {}, {})");
template = formatTemplate("{sql}.dateTimeChrono(doc[{}].value, {}, {})");
params.variable(field.name())
.variable(timeZone().getID())
.variable(chronoField().name());
@ -79,19 +77,4 @@ public abstract class DateTimeFunction extends BaseDateTimeFunction {
// used for applying ranges
public abstract String dateTimeFormat();
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
DateTimeFunction other = (DateTimeFunction) obj;
return Objects.equals(other.field(), field())
&& Objects.equals(other.timeZone(), timeZone());
}
@Override
public int hashCode() {
return Objects.hash(field(), timeZone());
}
}

View File

@ -18,7 +18,7 @@ import java.util.TimeZone;
public class DayName extends NamedDateTimeFunction {
public DayName(Location location, Expression field, TimeZone timeZone) {
super(location, field, timeZone);
super(location, field, timeZone, NameExtractor.DAY_NAME);
}
@Override
@ -30,9 +30,4 @@ public class DayName extends NamedDateTimeFunction {
protected DayName replaceChild(Expression newChild) {
return new DayName(location(), newChild, timeZone());
}
@Override
protected NameExtractor nameExtractor() {
return NameExtractor.DAY_NAME;
}
}
}

View File

@ -18,7 +18,7 @@ import java.util.TimeZone;
public class MonthName extends NamedDateTimeFunction {
public MonthName(Location location, Expression field, TimeZone timeZone) {
super(location, field, timeZone);
super(location, field, timeZone, NameExtractor.MONTH_NAME);
}
@Override
@ -30,10 +30,4 @@ public class MonthName extends NamedDateTimeFunction {
protected MonthName replaceChild(Expression newChild) {
return new MonthName(location(), newChild, timeZone());
}
@Override
protected NameExtractor nameExtractor() {
return NameExtractor.MONTH_NAME;
}
}
}

View File

@ -15,10 +15,8 @@ import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.util.StringUtils;
import org.joda.time.DateTime;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;
import static java.lang.String.format;
@ -29,25 +27,23 @@ import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.pa
*/
abstract class NamedDateTimeFunction extends BaseDateTimeFunction {
NamedDateTimeFunction(Location location, Expression field, TimeZone timeZone) {
private final NameExtractor nameExtractor;
NamedDateTimeFunction(Location location, Expression field, TimeZone timeZone, NameExtractor nameExtractor) {
super(location, field, timeZone);
this.nameExtractor = nameExtractor;
}
@Override
public Object fold() {
DateTime folded = (DateTime) field().fold();
if (folded == null) {
return null;
}
return nameExtractor().extract(folded.getMillis(), timeZone().getID());
protected Object doFold(long millis, String tzId) {
return nameExtractor.extract(millis, tzId);
}
@Override
public ScriptTemplate scriptWithField(FieldAttribute field) {
return new ScriptTemplate(
formatTemplate(format(Locale.ROOT, "{sql}.%s(doc[{}].value.millis, {})",
StringUtils.underscoreToLowerCamelCase(nameExtractor().name()))),
formatTemplate(format(Locale.ROOT, "{sql}.%s(doc[{}].value, {})",
StringUtils.underscoreToLowerCamelCase(nameExtractor.name()))),
paramsBuilder()
.variable(field.name())
.variable(timeZone().getID()).build(),
@ -56,29 +52,11 @@ abstract class NamedDateTimeFunction extends BaseDateTimeFunction {
@Override
protected final Pipe makePipe() {
return new UnaryPipe(location(), this, Expressions.pipe(field()),
new NamedDateTimeProcessor(nameExtractor(), timeZone()));
return new UnaryPipe(location(), this, Expressions.pipe(field()), new NamedDateTimeProcessor(nameExtractor, timeZone()));
}
protected abstract NameExtractor nameExtractor();
@Override
public DataType dataType() {
return DataType.KEYWORD;
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
NamedDateTimeFunction other = (NamedDateTimeFunction) obj;
return Objects.equals(other.field(), field())
&& Objects.equals(other.timeZone(), timeZone());
}
@Override
public int hashCode() {
return Objects.hash(field(), timeZone());
}
}

View File

@ -16,36 +16,35 @@ import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;
import java.util.function.BiFunction;
import java.util.function.Function;
public class NamedDateTimeProcessor extends BaseDateTimeProcessor {
public enum NameExtractor {
// for the moment we'll use no specific Locale, but we might consider introducing a Locale parameter, just like the timeZone one
DAY_NAME((Long millis, String tzId) -> {
ZonedDateTime time = ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId));
return time.format(DateTimeFormatter.ofPattern(DAY_NAME_FORMAT, Locale.ROOT));
}),
MONTH_NAME((Long millis, String tzId) -> {
ZonedDateTime time = ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId));
return time.format(DateTimeFormatter.ofPattern(MONTH_NAME_FORMAT, Locale.ROOT));
});
DAY_NAME(time -> time.format(DAY_NAME_FORMATTER)),
MONTH_NAME(time -> time.format(MONTH_NAME_FORMATTER));
private final BiFunction<Long,String,String> apply;
NameExtractor(BiFunction<Long,String,String> apply) {
private final Function<ZonedDateTime, String> apply;
NameExtractor(Function<ZonedDateTime, String> apply) {
this.apply = apply;
}
public final String extract(Long millis, String tzId) {
return apply.apply(millis, tzId);
return extract(ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId)), tzId);
}
public final String extract(ZonedDateTime millis, String tzId) {
return apply.apply(millis.withZoneSameInstant(ZoneId.of(tzId)));
}
}
public static final String NAME = "ndt";
private static final String MONTH_NAME_FORMAT = "MMMM";
private static final String DAY_NAME_FORMAT = "EEEE";
private static final DateTimeFormatter DAY_NAME_FORMATTER = DateTimeFormatter.ofPattern("EEEE", Locale.ROOT);
private static final DateTimeFormatter MONTH_NAME_FORMATTER = DateTimeFormatter.ofPattern("MMMM", Locale.ROOT);
private final NameExtractor extractor;
public NamedDateTimeProcessor(NameExtractor extractor, TimeZone timeZone) {
@ -97,4 +96,4 @@ public class NamedDateTimeProcessor extends BaseDateTimeProcessor {
public String toString() {
return extractor.toString();
}
}
}

View File

@ -15,9 +15,7 @@ import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2;
import org.elasticsearch.xpack.sql.type.DataType;
import org.joda.time.DateTime;
import java.util.Objects;
import java.util.TimeZone;
import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.QuarterProcessor.quarter;
@ -25,25 +23,18 @@ import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.pa
public class Quarter extends BaseDateTimeFunction {
protected static final String QUARTER_FORMAT = "q";
public Quarter(Location location, Expression field, TimeZone timeZone) {
super(location, field, timeZone);
}
@Override
public Object fold() {
DateTime folded = (DateTime) field().fold();
if (folded == null) {
return null;
}
return quarter(folded.getMillis(), timeZone().getID());
protected Object doFold(long millis, String tzId) {
return quarter(millis, tzId);
}
@Override
public ScriptTemplate scriptWithField(FieldAttribute field) {
return new ScriptTemplate(formatTemplate("{sql}.quarter(doc[{}].value.millis, {})"),
return new ScriptTemplate(formatTemplate("{sql}.quarter(doc[{}].value, {})"),
paramsBuilder()
.variable(field.name())
.variable(timeZone().getID())
@ -70,19 +61,4 @@ public class Quarter extends BaseDateTimeFunction {
public DataType dataType() {
return DataType.INTEGER;
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
BaseDateTimeFunction other = (BaseDateTimeFunction) obj;
return Objects.equals(other.field(), field())
&& Objects.equals(other.timeZone(), timeZone());
}
@Override
public int hashCode() {
return Objects.hash(field(), timeZone());
}
}

View File

@ -28,6 +28,7 @@ public class QuarterProcessor extends BaseDateTimeProcessor {
}
public static final String NAME = "q";
private static final DateTimeFormatter QUARTER_FORMAT = DateTimeFormatter.ofPattern("q", Locale.ROOT);
@Override
public String getWriteableName() {
@ -40,8 +41,12 @@ public class QuarterProcessor extends BaseDateTimeProcessor {
}
public static Integer quarter(long millis, String tzId) {
ZonedDateTime time = ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId));
return Integer.valueOf(time.format(DateTimeFormatter.ofPattern(Quarter.QUARTER_FORMAT, Locale.ROOT)));
return quarter(ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId)), tzId);
}
public static Integer quarter(ZonedDateTime zdt, String tzId) {
ZonedDateTime time = zdt.withZoneSameInstant(ZoneId.of(tzId));
return Integer.valueOf(time.format(QUARTER_FORMAT));
}
@Override

View File

@ -21,8 +21,7 @@ public class BinaryMathPipe extends BinaryPipe {
private final BinaryMathOperation operation;
public BinaryMathPipe(Location location, Expression expression, Pipe left,
Pipe right, BinaryMathOperation operation) {
public BinaryMathPipe(Location location, Expression expression, Pipe left, Pipe right, BinaryMathOperation operation) {
super(location, expression, left, right);
this.operation = operation;
}

View File

@ -6,10 +6,10 @@
package org.elasticsearch.xpack.sql.expression.function.scalar.math;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation;
import org.elasticsearch.xpack.sql.expression.gen.processor.FunctionalBinaryProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Arithmetics;
@ -19,7 +19,7 @@ import java.util.function.BiFunction;
/**
* Binary math operations. Sister class to {@link MathOperation}.
*/
public class BinaryMathProcessor extends BinaryNumericProcessor<BinaryMathOperation> {
public class BinaryMathProcessor extends FunctionalBinaryProcessor<Number, Number, Number, BinaryMathOperation> {
public enum BinaryMathOperation implements BiFunction<Number, Number, Number> {
@ -27,12 +27,6 @@ public class BinaryMathProcessor extends BinaryNumericProcessor<BinaryMathOperat
MOD(Arithmetics::mod),
POWER((l, r) -> Math.pow(l.doubleValue(), r.doubleValue())),
ROUND((l, r) -> {
if (l == null) {
return null;
}
if (r == null) {
return l;
}
if (r instanceof Float || r instanceof Double) {
throw new SqlIllegalArgumentException("An integer number is required; received [{}] as second parameter", r);
}
@ -43,12 +37,6 @@ public class BinaryMathProcessor extends BinaryNumericProcessor<BinaryMathOperat
return Math.round(Math.abs(middleResult)) / tenAtScale * sign;
}),
TRUNCATE((l, r) -> {
if (l == null) {
return null;
}
if (r == null) {
return l;
}
if (r instanceof Float || r instanceof Double) {
throw new SqlIllegalArgumentException("An integer number is required; received [{}] as second parameter", r);
}
@ -66,6 +54,9 @@ public class BinaryMathProcessor extends BinaryNumericProcessor<BinaryMathOperat
@Override
public final Number apply(Number left, Number right) {
if (left == null || right == null) {
return null;
}
return process.apply(left, right);
}
}
@ -80,13 +71,15 @@ public class BinaryMathProcessor extends BinaryNumericProcessor<BinaryMathOperat
super(in, i -> i.readEnum(BinaryMathOperation.class));
}
@Override
protected void doWrite(StreamOutput out) throws IOException {
out.writeEnum(operation());
}
@Override
public String getWriteableName() {
return NAME;
}
@Override
protected void checkParameter(Object param) {
if (!(param instanceof Number)) {
throw new SqlIllegalArgumentException("A number is required; received {}", param);
}
}
}

View File

@ -10,16 +10,11 @@ import org.elasticsearch.xpack.sql.expression.Expressions;
import org.elasticsearch.xpack.sql.expression.function.scalar.BinaryScalarFunction;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.Locale;
import java.util.Objects;
import static java.lang.String.format;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
public abstract class BinaryNumericFunction extends BinaryScalarFunction {
private final BinaryMathOperation operation;
@ -51,7 +46,7 @@ public abstract class BinaryNumericFunction extends BinaryScalarFunction {
protected TypeResolution resolveInputType(DataType inputType) {
return inputType.isNumeric() ?
TypeResolution.TYPE_RESOLVED :
new TypeResolution("'%s' requires a numeric type, received %s", mathFunction(), inputType.esType);
new TypeResolution("'%s' requires a numeric type, received %s", scriptMethodName(), inputType.esType);
}
@Override
@ -64,18 +59,6 @@ public abstract class BinaryNumericFunction extends BinaryScalarFunction {
return new BinaryMathPipe(location(), this, Expressions.pipe(left()), Expressions.pipe(right()), operation);
}
@Override
protected ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript) {
return new ScriptTemplate(format(Locale.ROOT, "Math.%s(%s,%s)", mathFunction(), leftScript.template(), rightScript.template()),
paramsBuilder()
.script(leftScript.params()).script(rightScript.params())
.build(), dataType());
}
protected String mathFunction() {
return getClass().getSimpleName().toLowerCase(Locale.ROOT);
}
@Override
public int hashCode() {
return Objects.hash(left(), right(), operation);

View File

@ -1,76 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.expression.function.scalar.math;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.gen.processor.BinaryProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import java.io.IOException;
import java.util.Locale;
import java.util.Objects;
import java.util.function.BiFunction;
public abstract class BinaryNumericProcessor<O extends Enum<?> & BiFunction<Number, Number, Number>> extends BinaryProcessor {
private final O operation;
protected BinaryNumericProcessor(Processor left, Processor right, O operation) {
super(left, right);
this.operation = operation;
}
protected BinaryNumericProcessor(StreamInput in, Reader<O> reader) throws IOException {
super(in);
operation = reader.read(in);
}
protected O operation() {
return operation;
}
@Override
protected Object doProcess(Object left, Object right) {
if (left == null || right == null) {
return null;
}
if (!(left instanceof Number)) {
throw new SqlIllegalArgumentException("A number is required; received {}", left);
}
if (!(right instanceof Number)) {
throw new SqlIllegalArgumentException("A number is required; received {}", right);
}
return operation.apply((Number) left, (Number) right);
}
@Override
public int hashCode() {
return Objects.hash(operation);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
BinaryNumericProcessor<?> other = (BinaryNumericProcessor<?>) obj;
return Objects.equals(operation, other.operation)
&& Objects.equals(left(), other.left())
&& Objects.equals(right(), other.right());
}
@Override
public String toString() {
return String.format(Locale.ROOT, "(%s %s %s)", left(), operation, right());
}
}

View File

@ -10,10 +10,6 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import java.util.Locale;
import static java.lang.String.format;
/**
* <a href="https://en.wikipedia.org/wiki/Trigonometric_functions#Cosecant,_secant,_and_cotangent">Cotangent</a>
* function.
@ -33,12 +29,6 @@ public class Cot extends MathFunction {
return new Cot(location(), newChild);
}
@Override
public String processScript(String template) {
// FIXME: needs to be null aware
return super.processScript(format(Locale.ROOT, "1.0 / Math.tan(%s)", template));
}
@Override
protected MathOperation operation() {
return MathOperation.COT;

View File

@ -29,11 +29,6 @@ public class Degrees extends MathFunction {
return new Degrees(location(), newChild);
}
@Override
protected String mathFunction() {
return "toDegrees";
}
@Override
protected MathOperation operation() {
return MathOperation.DEGREES;

View File

@ -41,7 +41,7 @@ public abstract class MathFunction extends UnaryScalarFunction {
@Override
public String processScript(String template) {
return super.processScript(format(Locale.ROOT, "Math.%s(%s)", mathFunction(), template));
return super.processScript(format(Locale.ROOT, "{sql}.%s(%s)", mathFunction(), template));
}
protected String mathFunction() {

View File

@ -22,14 +22,14 @@ public class MathProcessor implements Processor {
public enum MathOperation {
ABS((Object l) -> {
if (l instanceof Float) {
return Math.abs(((Float) l).floatValue());
return Double.valueOf(Math.abs(((Float) l).floatValue()));
}
if (l instanceof Double) {
return Math.abs(((Double) l).doubleValue());
}
long lo = ((Number) l).longValue();
//handles the corner-case of Long.MIN_VALUE
return lo >= 0 ? lo : lo == Long.MIN_VALUE ? Long.MAX_VALUE : -lo;
return lo >= 0 ? lo : lo == Long.MIN_VALUE ? Double.valueOf(Long.MAX_VALUE) : -lo;
}),
ACOS(Math::acos),
@ -52,15 +52,15 @@ public class MathProcessor implements Processor {
RANDOM((Object l) -> l != null ?
new Random(((Number) l).longValue()).nextDouble() :
Randomness.get().nextDouble(), true),
SIGN((DoubleFunction<Object>) Math::signum),
SIGN((DoubleFunction<Double>) Math::signum),
SIN(Math::sin),
SINH(Math::sinh),
SQRT(Math::sqrt),
TAN(Math::tan);
private final Function<Object, Object> apply;
private final Function<Object, Double> apply;
MathOperation(Function<Object, Object> apply) {
MathOperation(Function<Object, Double> apply) {
this(apply, false);
}
@ -69,7 +69,7 @@ public class MathProcessor implements Processor {
* If true, nulls are passed through, otherwise the function is short-circuited
* and null returned.
*/
MathOperation(Function<Object, Object> apply, boolean nullAware) {
MathOperation(Function<Object, Double> apply, boolean nullAware) {
if (nullAware) {
this.apply = apply;
} else {
@ -77,7 +77,7 @@ public class MathProcessor implements Processor {
}
}
MathOperation(DoubleFunction<Object> apply) {
MathOperation(DoubleFunction<Double> apply) {
this.apply = (Object l) -> l == null ? null : apply.apply(((Number) l).doubleValue());
}
@ -85,7 +85,7 @@ public class MathProcessor implements Processor {
this.apply = l -> supplier.get();
}
public final Object apply(Object l) {
public final Double apply(Object l) {
return apply.apply(l);
}
}

View File

@ -25,9 +25,4 @@ public class Power extends BinaryNumericFunction {
protected Power replaceChildren(Expression newLeft, Expression newRight) {
return new Power(location(), newLeft, newRight);
}
@Override
protected String mathFunction() {
return "pow";
}
}

View File

@ -10,10 +10,6 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import java.util.Locale;
import static java.lang.String.format;
/**
* Returns a random double (using the given seed).
*/
@ -33,13 +29,6 @@ public class Random extends MathFunction {
return new Random(location(), newChild);
}
@Override
public String processScript(String template) {
//TODO: Painless script uses Random since Randomness is not whitelisted
return super.processScript(
format(Locale.ROOT, "%s != null ? new Random((long) %s).nextDouble() : Math.random()", template, template));
}
@Override
protected MathOperation operation() {
return MathOperation.RANDOM;

View File

@ -8,16 +8,10 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.Literal;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.Locale;
import static java.lang.String.format;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
/**
* Function that takes two parameters: one is the field/value itself, the other is a non-floating point numeric
* which indicates how the rounding should behave. If positive, it will round the number till that parameter
@ -39,17 +33,6 @@ public class Round extends BinaryNumericFunction {
protected Round replaceChildren(Expression newLeft, Expression newRight) {
return new Round(location(), newLeft, newRight);
}
@Override
protected ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript) {
return new ScriptTemplate(format(Locale.ROOT, formatTemplate("{sql}.%s(%s,%s)"),
mathFunction(),
leftScript.template(),
rightScript.template()),
paramsBuilder()
.script(leftScript.params()).script(rightScript.params())
.build(), dataType());
}
@Override
public DataType dataType() {

View File

@ -8,16 +8,10 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.Literal;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.Locale;
import static java.lang.String.format;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
/**
* Function that takes two parameters: one is the field/value itself, the other is a non-floating point numeric
* which indicates how the truncation should behave. If positive, it will truncate the number till that
@ -40,17 +34,6 @@ public class Truncate extends BinaryNumericFunction {
return new Truncate(location(), newLeft, newRight);
}
@Override
protected ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript) {
return new ScriptTemplate(format(Locale.ROOT, formatTemplate("{sql}.%s(%s,%s)"),
mathFunction(),
leftScript.template(),
rightScript.template()),
paramsBuilder()
.script(leftScript.params()).script(rightScript.params())
.build(), dataType());
}
@Override
public DataType dataType() {
return left().dataType();

View File

@ -16,7 +16,6 @@ import java.util.Locale;
import java.util.Objects;
import java.util.function.BiFunction;
import static java.lang.String.format;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
/**
@ -59,17 +58,10 @@ public abstract class BinaryStringFunction<T,R> extends BinaryScalarFunction {
}
@Override
public ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript) {
// basically, transform the script to InternalSqlScriptUtils.[function_name](function_or_field1, function_or_field2)
return new ScriptTemplate(format(Locale.ROOT, formatTemplate("{sql}.%s(%s,%s)"),
operation().toString().toLowerCase(Locale.ROOT),
leftScript.template(),
rightScript.template()),
paramsBuilder()
.script(leftScript.params()).script(rightScript.params())
.build(), dataType());
protected String scriptMethodName() {
return operation().toString().toLowerCase(Locale.ROOT);
}
@Override
public ScriptTemplate scriptWithField(FieldAttribute field) {
return new ScriptTemplate(processScript("doc[{}].value"),

View File

@ -21,8 +21,8 @@ public class BinaryStringNumericPipe extends BinaryPipe {
private final BinaryStringNumericOperation operation;
public BinaryStringNumericPipe(Location location, Expression expression, Pipe left,
Pipe right, BinaryStringNumericOperation operation) {
public BinaryStringNumericPipe(Location location, Expression expression, Pipe left, Pipe right,
BinaryStringNumericOperation operation) {
super(location, expression, left, right);
this.operation = operation;
}

View File

@ -6,9 +6,9 @@
package org.elasticsearch.xpack.sql.expression.function.scalar.string;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.BinaryStringNumericProcessor.BinaryStringNumericOperation;
import org.elasticsearch.xpack.sql.expression.gen.processor.FunctionalBinaryProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import java.io.IOException;
@ -18,32 +18,28 @@ import java.util.function.BiFunction;
* Processor class covering string manipulating functions that have the first parameter as string,
* second parameter as numeric and a string result.
*/
public class BinaryStringNumericProcessor extends BinaryStringProcessor<BinaryStringNumericOperation, Number, String> {
public class BinaryStringNumericProcessor extends FunctionalBinaryProcessor<String, Number, String, BinaryStringNumericOperation> {
public static final String NAME = "sn";
public BinaryStringNumericProcessor(StreamInput in) throws IOException {
super(in, i -> i.readEnum(BinaryStringNumericOperation.class));
}
public BinaryStringNumericProcessor(Processor left, Processor right, BinaryStringNumericOperation operation) {
super(left, right, operation);
}
public enum BinaryStringNumericOperation implements BiFunction<String, Number, String> {
LEFT((s,c) -> {
int i = c.intValue();
if (i < 0) return "";
if (i < 0) {
return "";
}
return i > s.length() ? s : s.substring(0, i);
}),
RIGHT((s,c) -> {
int i = c.intValue();
if (i < 0) return "";
if (i < 0) {
return "";
}
return i > s.length() ? s : s.substring(s.length() - i);
}),
REPEAT((s,c) -> {
int i = c.intValue();
if (i <= 0) return null;
if (i <= 0) {
return null;
}
StringBuilder sb = new StringBuilder(s.length() * i);
for (int j = 0; j < i; j++) {
@ -57,31 +53,24 @@ public class BinaryStringNumericProcessor extends BinaryStringProcessor<BinarySt
}
private final BiFunction<String, Number, String> op;
@Override
public String apply(String stringExp, Number count) {
if (stringExp == null || count == null) {
return null;
}
return op.apply(stringExp, count);
}
}
@Override
protected void doWrite(StreamOutput out) throws IOException {
out.writeEnum(operation());
public static final String NAME = "sn";
public BinaryStringNumericProcessor(Processor left, Processor right, BinaryStringNumericOperation operation) {
super(left, right, operation);
}
@Override
protected Object doProcess(Object left, Object right) {
if (left == null || right == null) {
return null;
}
if (!(left instanceof String || left instanceof Character)) {
throw new SqlIllegalArgumentException("A string/char is required; received [{}]", left);
}
if (!(right instanceof Number)) {
throw new SqlIllegalArgumentException("A number is required; received [{}]", right);
}
return operation().apply(left instanceof Character ? left.toString() : (String) left, (Number) right);
public BinaryStringNumericProcessor(StreamInput in) throws IOException {
super(in, i -> i.readEnum(BinaryStringNumericOperation.class));
}
@Override
@ -89,4 +78,15 @@ public class BinaryStringNumericProcessor extends BinaryStringProcessor<BinarySt
return NAME;
}
}
@Override
protected Object doProcess(Object left, Object right) {
if (!(left instanceof String || left instanceof Character)) {
throw new SqlIllegalArgumentException("A string/char is required; received [{}]", left);
}
if (!(right instanceof Number)) {
throw new SqlIllegalArgumentException("A number is required; received [{}]", right);
}
return super.doProcess(left.toString(), right);
}
}

View File

@ -1,59 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.expression.function.scalar.string;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.xpack.sql.expression.gen.processor.BinaryProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import java.io.IOException;
import java.util.Objects;
import java.util.function.BiFunction;
public abstract class BinaryStringProcessor<O extends Enum<?> & BiFunction<String, T, R>, T, R> extends BinaryProcessor {
private final O operation;
public BinaryStringProcessor(Processor left, Processor right, O operation) {
super(left, right);
this.operation = operation;
}
public BinaryStringProcessor(StreamInput in, Reader<O> reader) throws IOException {
super(in);
operation = reader.read(in);
}
protected O operation() {
return operation;
}
@Override
public int hashCode() {
return Objects.hash(operation);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
BinaryStringProcessor<?,?,?> other = (BinaryStringProcessor<?,?,?>) obj;
return Objects.equals(operation, other.operation)
&& Objects.equals(left(), other.left())
&& Objects.equals(right(), other.right());
}
@Override
public String toString() {
return operation.toString();
}
}

View File

@ -21,8 +21,7 @@ public class BinaryStringStringPipe extends BinaryPipe {
private final BinaryStringStringOperation operation;
public BinaryStringStringPipe(Location location, Expression expression, Pipe left,
Pipe right, BinaryStringStringOperation operation) {
public BinaryStringStringPipe(Location location, Expression expression, Pipe left, Pipe right, BinaryStringStringOperation operation) {
super(location, expression, left, right);
this.operation = operation;
}

View File

@ -6,9 +6,9 @@
package org.elasticsearch.xpack.sql.expression.function.scalar.string;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.BinaryStringStringProcessor.BinaryStringStringOperation;
import org.elasticsearch.xpack.sql.expression.gen.processor.FunctionalBinaryProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import java.io.IOException;
@ -17,21 +17,10 @@ import java.util.function.BiFunction;
/**
* Processor class covering string manipulating functions that have two string parameters and a numeric result.
*/
public class BinaryStringStringProcessor extends BinaryStringProcessor<BinaryStringStringOperation, String, Number> {
public class BinaryStringStringProcessor extends FunctionalBinaryProcessor<String, String, Number, BinaryStringStringOperation> {
public static final String NAME = "ss";
public BinaryStringStringProcessor(StreamInput in) throws IOException {
super(in, i -> i.readEnum(BinaryStringStringOperation.class));
}
public BinaryStringStringProcessor(Processor left, Processor right, BinaryStringStringOperation operation) {
super(left, right, operation);
}
public enum BinaryStringStringOperation implements BiFunction<String, String, Number> {
POSITION((sub,str) -> {
if (sub == null || str == null) return null;
int pos = str.indexOf(sub);
return pos < 0 ? 0 : pos+1;
});
@ -43,30 +32,22 @@ public class BinaryStringStringProcessor extends BinaryStringProcessor<BinaryStr
private final BiFunction<String, String, Number> op;
@Override
public Number apply(String stringExpLeft, String stringExpRight) {
return op.apply(stringExpLeft, stringExpRight);
public Number apply(String left, String right) {
if (left == null || right == null) {
return null;
}
return op.apply(left, right);
}
}
@Override
protected void doWrite(StreamOutput out) throws IOException {
out.writeEnum(operation());
public static final String NAME = "ss";
public BinaryStringStringProcessor(StreamInput in) throws IOException {
super(in, i -> i.readEnum(BinaryStringStringOperation.class));
}
@Override
protected Object doProcess(Object left, Object right) {
if (left == null || right == null) {
return null;
}
if (!(left instanceof String || left instanceof Character)) {
throw new SqlIllegalArgumentException("A string/char is required; received [{}]", left);
}
if (!(right instanceof String || right instanceof Character)) {
throw new SqlIllegalArgumentException("A string/char is required; received [{}]", right);
}
return operation().apply(left instanceof Character ? left.toString() : (String) left,
right instanceof Character ? right.toString() : (String) right);
public BinaryStringStringProcessor(Processor left, Processor right, BinaryStringStringOperation operation) {
super(left, right, operation);
}
@Override
@ -74,4 +55,15 @@ public class BinaryStringStringProcessor extends BinaryStringProcessor<BinaryStr
return NAME;
}
@Override
protected Object doProcess(Object left, Object right) {
if (!(left instanceof String || left instanceof Character)) {
throw new SqlIllegalArgumentException("A string/char is required; received [{}]", left);
}
if (!(right instanceof String || right instanceof Character)) {
throw new SqlIllegalArgumentException("A string/char is required; received [{}]", right);
}
return super.doProcess(left.toString(), right.toString());
}
}

View File

@ -15,10 +15,7 @@ import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.Locale;
import static java.lang.String.format;
import static org.elasticsearch.xpack.sql.expression.function.scalar.string.ConcatFunctionProcessor.doProcessInScripts;
import static org.elasticsearch.xpack.sql.expression.function.scalar.string.ConcatFunctionProcessor.process;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
/**
@ -58,7 +55,7 @@ public class Concat extends BinaryScalarFunction {
@Override
public Object fold() {
return doProcessInScripts(left().fold(), right().fold());
return process(left().fold(), right().fold());
}
@Override
@ -71,18 +68,6 @@ public class Concat extends BinaryScalarFunction {
return NodeInfo.create(this, Concat::new, left(), right());
}
@Override
protected ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript) {
// basically, transform the script to InternalSqlScriptUtils.[function_name](function_or_field1, function_or_field2)
return new ScriptTemplate(format(Locale.ROOT, formatTemplate("{sql}.%s(%s,%s)"),
"concat",
leftScript.template(),
rightScript.template()),
paramsBuilder()
.script(leftScript.params()).script(rightScript.params())
.build(), dataType());
}
@Override
public ScriptTemplate scriptWithField(FieldAttribute field) {
return new ScriptTemplate(processScript("doc[{}].value"),

View File

@ -15,8 +15,7 @@ import java.util.Objects;
public class ConcatFunctionPipe extends BinaryPipe {
public ConcatFunctionPipe(Location location, Expression expression, Pipe left,
Pipe right) {
public ConcatFunctionPipe(Location location, Expression expression, Pipe left, Pipe right) {
super(location, expression, left, right);
}

View File

@ -10,6 +10,7 @@ import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.gen.processor.BinaryProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import org.elasticsearch.xpack.sql.util.StringUtils;
import java.io.IOException;
import java.util.Objects;
@ -27,14 +28,26 @@ public class ConcatFunctionProcessor extends BinaryProcessor {
}
@Override
protected Object doProcess(Object source1, Object source2) {
return doProcessInScripts(source1, source2);
public Object process(Object input) {
Object l = left().process(input);
checkParameter(l);
Object r = right().process(input);
checkParameter(r);
return doProcess(l, r);
}
@Override
protected Object doProcess(Object left, Object right) {
return process(left, right);
}
/**
* Used in Painless scripting
*/
public static Object doProcessInScripts(Object source1, Object source2) {
public static Object process(Object source1, Object source2) {
if (source1 == null && source2 == null) {
return StringUtils.EMPTY;
}
if (source1 == null) {
return source2;
}
@ -79,4 +92,4 @@ public class ConcatFunctionProcessor extends BinaryProcessor {
@Override
protected void doWrite(StreamOutput out) throws IOException {
}
}
}

View File

@ -42,7 +42,7 @@ public class LocateFunctionProcessor implements Processor {
return doProcess(pattern().process(input), source().process(input), start() == null ? null : start().process(input));
}
public static Object doProcess(Object pattern, Object source, Object start) {
public static Integer doProcess(Object pattern, Object source, Object start) {
if (source == null) {
return null;
}
@ -63,9 +63,9 @@ public class LocateFunctionProcessor implements Processor {
String stringSource = source instanceof Character ? source.toString() : (String) source;
String stringPattern = pattern instanceof Character ? pattern.toString() : (String) pattern;
return 1 + (start != null ?
return Integer.valueOf(1 + (start != null ?
stringSource.indexOf(stringPattern, ((Number) start).intValue() - 1)
: stringSource.indexOf(stringPattern));
: stringSource.indexOf(stringPattern)));
}
@Override

View File

@ -75,24 +75,7 @@ public class StringProcessor implements Processor {
}
StringOperation(NumericFunction<Object> apply) {
this.apply = l -> l == null ? null : apply.apply((l));
}
StringOperation(Function<Object, Object> apply) {
this(apply, false);
}
/**
* Wrapper for nulls around the given function.
* If true, nulls are passed through, otherwise the function is short-circuited
* and null returned.
*/
StringOperation(Function<Object, Object> apply, boolean nullAware) {
if (nullAware) {
this.apply = apply;
} else {
this.apply = l -> l == null ? null : apply.apply(l);
}
this.apply = l -> l == null ? null : apply.apply(l);
}
public final Object apply(Object l) {

View File

@ -5,10 +5,14 @@
*/
package org.elasticsearch.xpack.sql.expression.function.scalar.whitelist;
import org.elasticsearch.index.fielddata.ScriptDocValues;
import org.elasticsearch.script.JodaCompatibleZonedDateTime;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeFunction;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NamedDateTimeProcessor.NameExtractor;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.QuarterProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.BinaryStringNumericProcessor.BinaryStringNumericOperation;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.BinaryStringStringProcessor.BinaryStringStringOperation;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.ConcatFunctionProcessor;
@ -17,6 +21,14 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.string.LocateFunct
import org.elasticsearch.xpack.sql.expression.function.scalar.string.ReplaceFunctionProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.StringProcessor.StringOperation;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.SubstringFunctionProcessor;
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
import org.elasticsearch.xpack.sql.expression.predicate.regex.RegexProcessor.RegexOperation;
import org.elasticsearch.xpack.sql.util.StringUtils;
import java.time.ZonedDateTime;
import java.util.Map;
/**
* Whitelisted class for SQL scripts.
@ -27,30 +39,244 @@ public final class InternalSqlScriptUtils {
private InternalSqlScriptUtils() {}
public static Integer dateTimeChrono(long millis, String tzId, String chronoName) {
return DateTimeFunction.dateTimeChrono(millis, tzId, chronoName);
//
// Utilities
//
// safe missing mapping/value extractor
public static <T> Object docValue(Map<String, ScriptDocValues<T>> doc, String fieldName) {
if (doc.containsKey(fieldName)) {
ScriptDocValues<T> docValues = doc.get(fieldName);
if (docValues.size() > 0) {
return docValues.get(0);
}
}
return null;
}
public static String dayName(long millis, String tzId) {
return NameExtractor.DAY_NAME.extract(millis, tzId);
public static boolean nullSafeFilter(Boolean filter) {
return filter == null ? false : filter.booleanValue();
}
public static double nullSafeSortNumeric(Number sort) {
return sort == null ? 0.0d : sort.doubleValue();
}
public static String nullSafeSortString(Object sort) {
return sort == null ? StringUtils.EMPTY : sort.toString();
}
//
// Operators
//
//
// Logical
//
public static Boolean eq(Object left, Object right) {
return BinaryComparisonOperation.EQ.apply(left, right);
}
public static Boolean lt(Object left, Object right) {
return BinaryComparisonOperation.LT.apply(left, right);
}
public static String monthName(long millis, String tzId) {
return NameExtractor.MONTH_NAME.extract(millis, tzId);
public static Boolean lte(Object left, Object right) {
return BinaryComparisonOperation.LTE.apply(left, right);
}
public static Boolean gt(Object left, Object right) {
return BinaryComparisonOperation.GT.apply(left, right);
}
public static Boolean gte(Object left, Object right) {
return BinaryComparisonOperation.GTE.apply(left, right);
}
public static Boolean and(Boolean left, Boolean right) {
return BinaryLogicOperation.AND.apply(left, right);
}
public static Integer quarter(long millis, String tzId) {
return QuarterProcessor.quarter(millis, tzId);
public static Boolean or(Boolean left, Boolean right) {
return BinaryLogicOperation.OR.apply(left, right);
}
//
// Regex
//
public static Boolean regex(String value, String pattern) {
return RegexOperation.match(value, pattern);
}
//
// Math
//
public static Number add(Number left, Number right) {
return BinaryArithmeticOperation.ADD.apply(left, right);
}
public static Number sub(Number left, Number right) {
return BinaryArithmeticOperation.SUB.apply(left, right);
}
public static Number mul(Number left, Number right) {
return BinaryArithmeticOperation.MUL.apply(left, right);
}
public static Number div(Number left, Number right) {
return BinaryArithmeticOperation.DIV.apply(left, right);
}
public static Number mod(Number left, Number right) {
return BinaryArithmeticOperation.MOD.apply(left, right);
}
public static Number round(Number v, Number s) {
return BinaryMathOperation.ROUND.apply(v, s);
}
public static Number truncate(Number v, Number s) {
return BinaryMathOperation.TRUNCATE.apply(v, s);
}
public static Double abs(Number value) {
return MathOperation.ABS.apply(value);
}
public static Double acos(Number value) {
return MathOperation.ACOS.apply(value);
}
public static Double asin(Number value) {
return MathOperation.ASIN.apply(value);
}
public static Double atan(Number value) {
return MathOperation.ATAN.apply(value);
}
public static Double cbrt(Number value) {
return MathOperation.CBRT.apply(value);
}
public static Double ceil(Number value) {
return MathOperation.CEIL.apply(value);
}
public static Double cos(Number value) {
return MathOperation.COS.apply(value);
}
public static Double cosh(Number value) {
return MathOperation.COSH.apply(value);
}
public static Double cot(Number value) {
return MathOperation.COT.apply(value);
}
public static Double degrees(Number value) {
return MathOperation.DEGREES.apply(value);
}
public static Double e(Number value) {
return MathOperation.E.apply(value);
}
public static Double exp(Number value) {
return MathOperation.EXP.apply(value);
}
public static Double expm1(Number value) {
return MathOperation.EXPM1.apply(value);
}
public static Double floor(Number value) {
return MathOperation.FLOOR.apply(value);
}
public static Double log(Number value) {
return MathOperation.LOG.apply(value);
}
public static Double log10(Number value) {
return MathOperation.LOG10.apply(value);
}
public static Double pi(Number value) {
return MathOperation.PI.apply(value);
}
public static Double radians(Number value) {
return MathOperation.RADIANS.apply(value);
}
public static Double random(Number value) {
return MathOperation.RANDOM.apply(value);
}
public static Double sign(Number value) {
return MathOperation.SIGN.apply(value);
}
public static Double sin(Number value) {
return MathOperation.SIN.apply(value);
}
public static Double sinh(Number value) {
return MathOperation.SINH.apply(value);
}
public static Double sqrt(Number value) {
return MathOperation.SQRT.apply(value);
}
public static Double tan(Number value) {
return MathOperation.TAN.apply(value);
}
//
// Date/Time functions
//
public static Integer dateTimeChrono(Object dateTime, String tzId, String chronoName) {
if (dateTime == null || tzId == null || chronoName == null) {
return null;
}
return DateTimeFunction.dateTimeChrono(asDateTime(dateTime), tzId, chronoName);
}
public static String dayName(Object dateTime, String tzId) {
if (dateTime == null || tzId == null) {
return null;
}
return NameExtractor.DAY_NAME.extract(asDateTime(dateTime), tzId);
}
public static String monthName(Object dateTime, String tzId) {
if (dateTime == null || tzId == null) {
return null;
}
return NameExtractor.MONTH_NAME.extract(asDateTime(dateTime), tzId);
}
public static Integer quarter(Object dateTime, String tzId) {
if (dateTime == null || tzId == null) {
return null;
}
return QuarterProcessor.quarter(asDateTime(dateTime), tzId);
}
private static ZonedDateTime asDateTime(Object dateTime) {
if (dateTime instanceof JodaCompatibleZonedDateTime) {
return ((JodaCompatibleZonedDateTime) dateTime).getZonedDateTime();
}
throw new SqlIllegalArgumentException("Invalid date encountered [{}]", dateTime);
}
//
// String functions
//
public static Integer ascii(String s) {
return (Integer) StringOperation.ASCII.apply(s);
}
@ -58,43 +284,43 @@ public final class InternalSqlScriptUtils {
public static Integer bitLength(String s) {
return (Integer) StringOperation.BIT_LENGTH.apply(s);
}
public static String character(Number n) {
return (String) StringOperation.CHAR.apply(n);
}
public static Integer charLength(String s) {
return (Integer) StringOperation.CHAR_LENGTH.apply(s);
}
public static String concat(String s1, String s2) {
return ConcatFunctionProcessor.doProcessInScripts(s1, s2).toString();
return (String) ConcatFunctionProcessor.process(s1, s2);
}
public static String insert(String s, int start, int length, String r) {
return InsertFunctionProcessor.doProcess(s, start, length, r).toString();
public static String insert(String s, Number start, Number length, String r) {
return (String) InsertFunctionProcessor.doProcess(s, start, length, r);
}
public static String lcase(String s) {
return (String) StringOperation.LCASE.apply(s);
}
public static String left(String s, int count) {
public static String left(String s, Number count) {
return BinaryStringNumericOperation.LEFT.apply(s, count);
}
public static Integer length(String s) {
return (Integer) StringOperation.LENGTH.apply(s);
}
public static Integer locate(String s1, String s2) {
return locate(s1, s2, null);
}
public static Integer locate(String s1, String s2, Integer pos) {
return (Integer) LocateFunctionProcessor.doProcess(s1, s2, pos);
public static Integer locate(String s1, String s2, Number pos) {
return LocateFunctionProcessor.doProcess(s1, s2, pos);
}
public static String ltrim(String s) {
return (String) StringOperation.LTRIM.apply(s);
}
@ -102,35 +328,35 @@ public final class InternalSqlScriptUtils {
public static Integer octetLength(String s) {
return (Integer) StringOperation.OCTET_LENGTH.apply(s);
}
public static Integer position(String s1, String s2) {
return (Integer) BinaryStringStringOperation.POSITION.apply(s1, s2);
}
public static String repeat(String s, int count) {
public static String repeat(String s, Number count) {
return BinaryStringNumericOperation.REPEAT.apply(s, count);
}
public static String replace(String s1, String s2, String s3) {
return ReplaceFunctionProcessor.doProcess(s1, s2, s3).toString();
return (String) ReplaceFunctionProcessor.doProcess(s1, s2, s3);
}
public static String right(String s, int count) {
public static String right(String s, Number count) {
return BinaryStringNumericOperation.RIGHT.apply(s, count);
}
public static String rtrim(String s) {
return (String) StringOperation.RTRIM.apply(s);
}
public static String space(Number n) {
return (String) StringOperation.SPACE.apply(n);
}
public static String substring(String s, int start, int length) {
return SubstringFunctionProcessor.doProcess(s, start, length).toString();
public static String substring(String s, Number start, Number length) {
return (String) SubstringFunctionProcessor.doProcess(s, start, length);
}
public static String ucase(String s) {
return (String) StringOperation.UCASE.apply(s);
}

View File

@ -35,7 +35,26 @@ public abstract class BinaryProcessor implements Processor {
@Override
public Object process(Object input) {
return doProcess(left.process(input), right.process(input));
Object l = left.process(input);
if (l == null) {
return null;
}
checkParameter(l);
Object r = right.process(input);
if (r == null) {
return null;
}
checkParameter(r);
return doProcess(l, r);
}
/**
* Checks the parameter (typically for its type) if the value is not null.
*/
protected void checkParameter(Object param) {
//no-op
}
protected Processor left() {

View File

@ -0,0 +1,68 @@
/*
* 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.gen.processor;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import java.io.IOException;
import java.util.Objects;
import java.util.function.BiFunction;
/**
* Base class for definition binary processors based on functions (for applying) defined as enums (for serialization purposes).
*/
public abstract class FunctionalBinaryProcessor<T, U, R, F extends Enum<F> & BiFunction<T, U, R>> extends BinaryProcessor {
private final F function;
protected FunctionalBinaryProcessor(Processor left, Processor right, F function) {
super(left, right);
this.function = function;
}
protected FunctionalBinaryProcessor(StreamInput in, Reader<F> reader) throws IOException {
super(in);
this.function = reader.read(in);
}
public F function() {
return function;
}
@Override
protected void doWrite(StreamOutput out) throws IOException {
out.writeEnum(function());
}
@SuppressWarnings("unchecked")
@Override
protected Object doProcess(Object left, Object right) {
return function.apply((T) left, (U) right);
}
@Override
public int hashCode() {
return Objects.hash(left(), right(), function());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
FunctionalBinaryProcessor<?, ?, ?, ?> other = (FunctionalBinaryProcessor<?, ?, ?, ?>) obj;
return Objects.equals(function(), other.function())
&& Objects.equals(left(), other.left())
&& Objects.equals(right(), other.right());
}
}

View File

@ -13,7 +13,6 @@ import org.elasticsearch.xpack.sql.expression.Expressions;
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
import org.elasticsearch.xpack.sql.expression.function.aggregate.AggregateFunctionAttribute;
import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunctionAttribute;
import org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalSqlScriptUtils;
import org.elasticsearch.xpack.sql.type.DataType;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
@ -75,6 +74,6 @@ public interface ScriptWeaver {
}
default String formatTemplate(String template) {
return template.replace("{sql}", InternalSqlScriptUtils.class.getSimpleName()).replace("{}", "params.%s");
return Scripts.formatTemplate(template);
}
}

View File

@ -0,0 +1,86 @@
/*
* 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.gen.script;
import org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalSqlScriptUtils;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.AbstractMap.SimpleEntry;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import static java.lang.String.format;
import static java.util.stream.Collectors.toMap;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
public final class Scripts {
private Scripts() {}
private static final Map<Pattern, String> FORMATTING_PATTERNS = Collections.unmodifiableMap(Stream.of(
new SimpleEntry<>("doc[{}].value", "{sql}.docValue(doc,{})"),
new SimpleEntry<>("{sql}", InternalSqlScriptUtils.class.getSimpleName()),
new SimpleEntry<>("{}", "params.%s"))
.collect(toMap(e -> Pattern.compile(e.getKey(), Pattern.LITERAL), Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new)));
/**
* Expands common tokens inside the script:
*
* <pre>
* {sql} -&gt; InternalSqlScriptUtils
* doc[{}].value -&gt; InternalSqlScriptUtils.docValue(doc, {})
* {} -&gt; params.%s
* </pre>
*/
public static String formatTemplate(String template) {
for (Entry<Pattern, String> entry : FORMATTING_PATTERNS.entrySet()) {
template = entry.getKey().matcher(template).replaceAll(entry.getValue());
}
return template;
}
public static ScriptTemplate nullSafeFilter(ScriptTemplate script) {
return new ScriptTemplate(formatTemplate(
format(Locale.ROOT, "{sql}.nullSafeFilter(%s)", script.template())),
script.params(),
DataType.BOOLEAN);
}
public static ScriptTemplate nullSafeSort(ScriptTemplate script) {
String methodName = script.outputType().isNumeric() ? "nullSafeSortNumeric" : "nullSafeSortString";
return new ScriptTemplate(formatTemplate(
format(Locale.ROOT, "{sql}.%s(%s)", methodName, script.template())),
script.params(),
script.outputType());
}
public static ScriptTemplate and(ScriptTemplate left, ScriptTemplate right) {
return binaryMethod("and", left, right, DataType.BOOLEAN);
}
public static ScriptTemplate or(ScriptTemplate left, ScriptTemplate right) {
return binaryMethod("or", left, right, DataType.BOOLEAN);
}
public static ScriptTemplate binaryMethod(String methodName, ScriptTemplate leftScript, ScriptTemplate rightScript,
DataType dataType) {
return new ScriptTemplate(format(Locale.ROOT, formatTemplate("{sql}.%s(%s,%s)"),
methodName,
leftScript.template(),
rightScript.template()),
paramsBuilder()
.script(leftScript.params())
.script(rightScript.params())
.build(),
dataType);
}
}

View File

@ -13,19 +13,19 @@ import org.elasticsearch.xpack.sql.type.DataType;
* Operator is a specialized binary predicate where both sides have the compatible types
* (it's up to the analyzer to do any conversion if needed).
*/
public abstract class BinaryOperator extends BinaryPredicate {
public abstract class BinaryOperator<T, U, R, F extends PredicateBiFunction<T, U, R>> extends BinaryPredicate<T, U, R, F> {
public interface Negateable {
BinaryOperator negate();
BinaryOperator<?, ?, ?, ?> negate();
}
protected BinaryOperator(Location location, Expression left, Expression right, String symbol) {
super(location, left, right, symbol);
protected BinaryOperator(Location location, Expression left, Expression right, F function) {
super(location, left, right, function);
}
protected abstract TypeResolution resolveInputType(DataType inputType);
public abstract BinaryOperator swapLeftAndRight();
public abstract BinaryOperator<T, U, R, F> swapLeftAndRight();
@Override
protected TypeResolution resolveType() {

View File

@ -15,24 +15,35 @@ import java.util.Objects;
/**
* Binary operator. Operators act as _special_ functions in that they have a symbol
* instead of a name and do not use parathensis.
* instead of a name and do not use parentheses.
* Further more they are not registered as the rest of the functions as are implicit
* to the language.
*/
public abstract class BinaryPredicate extends BinaryScalarFunction {
public abstract class BinaryPredicate<T, U, R, F extends PredicateBiFunction<T, U, R>> extends BinaryScalarFunction {
private final String symbol;
private final String name;
private final F function;
protected BinaryPredicate(Location location, Expression left, Expression right, String symbol) {
protected BinaryPredicate(Location location, Expression left, Expression right, F function) {
super(location, left, right);
this.name = name(left, right, symbol);
this.symbol = symbol;
this.name = name(left, right, function.symbol());
this.function = function;
}
@SuppressWarnings("unchecked")
@Override
public R fold() {
return function().apply((T) left().fold(), (U) right().fold());
}
@Override
protected String scriptMethodName() {
return function.scriptMethodName();
}
@Override
public int hashCode() {
return Objects.hash(left(), right());
return Objects.hash(left(), right(), function.symbol());
}
@Override
@ -47,13 +58,26 @@ public abstract class BinaryPredicate extends BinaryScalarFunction {
return false;
}
BinaryPredicate other = (BinaryPredicate) obj;
BinaryPredicate<?, ?, ?, ?> other = (BinaryPredicate<?, ?, ?, ?>) obj;
return Objects.equals(symbol, other.symbol)
return Objects.equals(symbol(), other.symbol())
&& Objects.equals(left(), other.left())
&& Objects.equals(right(), other.right());
}
@Override
public String name() {
return name;
}
public String symbol() {
return function.symbol();
}
public F function() {
return function;
}
private static String name(Expression left, Expression right, String symbol) {
StringBuilder sb = new StringBuilder();
sb.append(Expressions.name(left));
@ -72,13 +96,4 @@ public abstract class BinaryPredicate extends BinaryScalarFunction {
}
return sb.toString();
}
@Override
public String name() {
return name;
}
public final String symbol() {
return symbol;
}
}

View File

@ -0,0 +1,32 @@
/*
* 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;
import java.util.Locale;
import java.util.function.BiFunction;
public interface PredicateBiFunction<T, U, R> extends BiFunction<T, U, R> {
String name();
String symbol();
@Override
default R apply(T t, U u) {
if (t == null || u == null) {
return null;
}
return doApply(t, u);
}
R doApply(T t, U u);
default String scriptMethodName() {
return name().toLowerCase(Locale.ROOT);
}
}

View File

@ -6,6 +6,8 @@
package org.elasticsearch.xpack.sql.expression.predicate;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.predicate.logical.And;
import org.elasticsearch.xpack.sql.expression.predicate.logical.Or;
import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan;
import java.util.ArrayList;

View File

@ -5,56 +5,56 @@
*/
package org.elasticsearch.xpack.sql.expression.predicate;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.Attribute;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
import org.elasticsearch.xpack.sql.expression.Foldables;
import org.elasticsearch.xpack.sql.expression.NamedExpression;
import org.elasticsearch.xpack.sql.expression.Expressions;
import org.elasticsearch.xpack.sql.expression.Literal;
import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunction;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
import org.elasticsearch.xpack.sql.expression.gen.script.Params;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptWeaver;
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicPipe;
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparison;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonPipe;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.type.EsField;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import static java.lang.String.format;
import static java.util.Collections.emptyMap;
import static java.util.Arrays.asList;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
// BETWEEN or range - is a mix of gt(e) AND lt(e)
public class Range extends NamedExpression implements ScriptWeaver {
public class Range extends ScalarFunction {
private final String name;
private final Expression value, lower, upper;
private final boolean includeLower, includeUpper;
public Range(Location location, Expression value, Expression lower, boolean includeLower, Expression upper, boolean includeUpper) {
this(location, null, value, lower, includeLower, upper, includeUpper);
}
public Range(Location location, String name, Expression value, Expression lower, boolean includeLower, Expression upper,
boolean includeUpper) {
super(location, name == null ? defaultName(value, lower, upper, includeLower, includeUpper) : name,
Arrays.asList(value, lower, upper), null);
super(location, asList(value, lower, upper));
this.value = value;
this.lower = lower;
this.upper = upper;
this.includeLower = includeLower;
this.includeUpper = includeUpper;
this.name = name(value, lower, upper, includeLower, includeUpper);
}
@Override
public String name() {
return name;
}
@Override
protected NodeInfo<Range> info() {
return NodeInfo.create(this, Range::new, name(), value, lower, includeLower, upper, includeUpper);
return NodeInfo.create(this, Range::new, value, lower, includeLower, upper, includeUpper);
}
@Override
@ -130,18 +130,25 @@ public class Range extends NamedExpression implements ScriptWeaver {
@Override
public ScriptTemplate asScript() {
ScriptTemplate scriptTemplate = asScript(value);
ScriptTemplate valueScript = asScript(value);
ScriptTemplate lowerScript = asScript(lower);
ScriptTemplate upperScript = asScript(upper);
String template = formatTemplate(format(Locale.ROOT, "({} %s %s) && (%s %s {})",
includeLower() ? "<=" : "<",
scriptTemplate.template(),
scriptTemplate.template(),
includeUpper() ? "<=" : "<"));
String template = formatTemplate(format(Locale.ROOT, "{sql}.and({sql}.%s(%s, %s), {sql}.%s(%s, %s))",
includeLower() ? "gte" : "gt",
valueScript.template(),
lowerScript.template(),
includeUpper() ? "lte" : "lt",
valueScript.template(),
upperScript.template()
));
Params params = paramsBuilder().variable(Foldables.valueOf(lower))
.script(scriptTemplate.params())
.script(scriptTemplate.params())
.variable(Foldables.valueOf(upper))
Params params = paramsBuilder()
.script(valueScript.params())
.script(lowerScript.params())
.script(valueScript.params())
.script(upperScript.params())
.build();
return new ScriptTemplate(template, params, DataType.BOOLEAN);
@ -149,13 +156,12 @@ public class Range extends NamedExpression implements ScriptWeaver {
@Override
protected Pipe makePipe() {
throw new SqlIllegalArgumentException("Not supported yet");
}
@Override
public Attribute toAttribute() {
return new FieldAttribute(location(), "not yet implemented",
new EsField("not yet implemented", DataType.UNSUPPORTED, emptyMap(), false));
BinaryComparisonPipe lowerPipe = new BinaryComparisonPipe(location(), this, Expressions.pipe(value()), Expressions.pipe(lower()),
includeLower() ? BinaryComparisonOperation.GTE : BinaryComparisonOperation.GT);
BinaryComparisonPipe upperPipe = new BinaryComparisonPipe(location(), this, Expressions.pipe(value()), Expressions.pipe(upper()),
includeUpper() ? BinaryComparisonOperation.LTE : BinaryComparisonOperation.LT);
BinaryLogicPipe and = new BinaryLogicPipe(location(), this, lowerPipe, upperPipe, BinaryLogicOperation.AND);
return and;
}
@Override
@ -181,13 +187,28 @@ public class Range extends NamedExpression implements ScriptWeaver {
&& Objects.equals(upper, other.upper);
}
private static String defaultName(Expression value, Expression lower, Expression upper, boolean includeLower, boolean includeUpper) {
private static String name(Expression value, Expression lower, Expression upper, boolean includeLower, boolean includeUpper) {
StringBuilder sb = new StringBuilder();
sb.append(lower);
sb.append(Expressions.name(lower));
if (!(lower instanceof Literal)) {
sb.insert(0, "(");
sb.append(")");
}
sb.append(includeLower ? " <= " : " < ");
sb.append(value);
int pos = sb.length();
sb.append(Expressions.name(value));
if (!(value instanceof Literal)) {
sb.insert(pos, "(");
sb.append(")");
}
sb.append(includeUpper ? " <= " : " < ");
sb.append(upper);
pos = sb.length();
sb.append(Expressions.name(upper));
if (!(upper instanceof Literal)) {
sb.insert(pos, "(");
sb.append(")");
}
return sb.toString();
}

View File

@ -3,19 +3,18 @@
* 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;
package org.elasticsearch.xpack.sql.expression.predicate.logical;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator.Negateable;
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import java.util.Objects;
public class And extends BinaryLogic implements Negateable {
public And(Location location, Expression left, Expression right) {
super(location, left, right, "&&");
super(location, left, right, BinaryLogicOperation.AND);
}
@Override
@ -24,22 +23,17 @@ public class And extends BinaryLogic implements Negateable {
}
@Override
protected BinaryOperator replaceChildren(Expression newLeft, Expression newRight) {
protected And replaceChildren(Expression newLeft, Expression newRight) {
return new And(location(), newLeft, newRight);
}
@Override
public Object fold() {
return Objects.equals(left().fold(), Boolean.TRUE) && Objects.equals(right().fold(), Boolean.TRUE);
}
@Override
public Or negate() {
return new Or(location(), new Not(location(), left()), new Not(location(), right()));
}
@Override
public And swapLeftAndRight() {
return new And(location(), right(), left());
}
@Override
public Or negate() {
return new Or(location(), new Not(location(), left()), new Not(location(), right()));
}
}

View File

@ -3,19 +3,20 @@
* 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;
package org.elasticsearch.xpack.sql.expression.predicate.logical;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.AggNameInput;
import org.elasticsearch.xpack.sql.expression.Expressions;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType;
public abstract class BinaryLogic extends BinaryOperator {
public abstract class BinaryLogic extends BinaryOperator<Boolean, Boolean, Boolean, BinaryLogicOperation> {
protected BinaryLogic(Location location, Expression left, Expression right, String symbol) {
super(location, left, right, symbol);
protected BinaryLogic(Location location, Expression left, Expression right, BinaryLogicOperation operation) {
super(location, left, right, operation);
}
@Override
@ -29,13 +30,8 @@ public abstract class BinaryLogic extends BinaryOperator {
"'%s' requires type %s not %s", symbol(), DataType.BOOLEAN.sqlName(), inputType.sqlName());
}
@Override
protected ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript) {
return new ScriptTemplate("<not yet supported");
}
@Override
protected Pipe makePipe() {
return new AggNameInput(location(), this, "<not yet supported>");
return new BinaryLogicPipe(location(), this, Expressions.pipe(left()), Expressions.pipe(right()), function());
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.logical;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.BinaryPipe;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import java.util.Objects;
public class BinaryLogicPipe extends BinaryPipe {
private final BinaryLogicOperation operation;
public BinaryLogicPipe(Location location, Expression expression, Pipe left, Pipe right, BinaryLogicOperation operation) {
super(location, expression, left, right);
this.operation = operation;
}
@Override
protected NodeInfo<BinaryLogicPipe> info() {
return NodeInfo.create(this, BinaryLogicPipe::new, expression(), left(), right(), operation);
}
@Override
protected BinaryPipe replaceChildren(Pipe left, Pipe right) {
return new BinaryLogicPipe(location(), expression(), left, right, operation);
}
@Override
public BinaryLogicProcessor asProcessor() {
return new BinaryLogicProcessor(left().asProcessor(), right().asProcessor(), operation);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), operation);
}
@Override
public boolean equals(Object obj) {
if (super.equals(obj)) {
BinaryLogicPipe other = (BinaryLogicPipe) obj;
return Objects.equals(operation, other.operation);
}
return false;
}
}

View File

@ -0,0 +1,91 @@
/*
* 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.logical;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.gen.processor.FunctionalBinaryProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import org.elasticsearch.xpack.sql.expression.predicate.PredicateBiFunction;
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
import java.io.IOException;
import java.util.function.BiFunction;
public class BinaryLogicProcessor extends FunctionalBinaryProcessor<Boolean, Boolean, Boolean, BinaryLogicOperation> {
public enum BinaryLogicOperation implements PredicateBiFunction<Boolean, Boolean, Boolean> {
AND((l, r) -> {
if (Boolean.FALSE.equals(l) || Boolean.FALSE.equals(r)) {
return Boolean.FALSE;
}
if (l == null || r == null) {
return null;
}
return l.booleanValue() && r.booleanValue();
}, "&&"),
OR((l, r) -> {
if (Boolean.TRUE.equals(l) || Boolean.TRUE.equals(r)) {
return Boolean.TRUE;
}
if (l == null || r == null) {
return null;
}
return l.booleanValue() || r.booleanValue();
}, "||");
private final BiFunction<Boolean, Boolean, Boolean> process;
private final String symbol;
BinaryLogicOperation(BiFunction<Boolean, Boolean, Boolean> process, String symbol) {
this.process = process;
this.symbol = symbol;
}
@Override
public String symbol() {
return symbol;
}
@Override
public Boolean apply(Boolean left, Boolean right) {
return process.apply(left, right);
}
@Override
public final Boolean doApply(Boolean left, Boolean right) {
return null;
}
@Override
public String toString() {
return symbol;
}
}
public static final String NAME = "lb";
public BinaryLogicProcessor(Processor left, Processor right, BinaryLogicOperation operation) {
super(left, right, operation);
}
public BinaryLogicProcessor(StreamInput in) throws IOException {
super(in, i -> i.readEnum(BinaryLogicOperation.class));
}
@Override
public String getWriteableName() {
return NAME;
}
@Override
protected void checkParameter(Object param) {
if (!(param instanceof Boolean)) {
throw new SqlIllegalArgumentException("A boolean is required; received {}", param);
}
}
}

View File

@ -3,7 +3,7 @@
* 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;
package org.elasticsearch.xpack.sql.expression.predicate.logical;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.Expression;

View File

@ -3,19 +3,18 @@
* 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;
package org.elasticsearch.xpack.sql.expression.predicate.logical;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator.Negateable;
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import java.util.Objects;
public class Or extends BinaryLogic implements Negateable {
public Or(Location location, Expression left, Expression right) {
super(location, left, right, "||");
super(location, left, right, BinaryLogicOperation.OR);
}
@Override
@ -24,15 +23,10 @@ public class Or extends BinaryLogic implements Negateable {
}
@Override
protected BinaryOperator replaceChildren(Expression newLeft, Expression newRight) {
protected Or replaceChildren(Expression newLeft, Expression newRight) {
return new Or(location(), newLeft, newRight);
}
@Override
public Object fold() {
return Objects.equals(left().fold(), Boolean.TRUE) || Objects.equals(right().fold(), Boolean.TRUE);
}
@Override
public Or swapLeftAndRight() {
return new Or(location(), right(), left());

View File

@ -8,25 +8,16 @@ package org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic;
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.ScriptTemplate;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.type.DataTypeConversion;
import java.util.Locale;
public abstract class ArithmeticOperation extends BinaryOperator<Number, Number, Number, BinaryArithmeticOperation> {
import static java.lang.String.format;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
public abstract class ArithmeticOperation extends BinaryOperator {
private final BinaryArithmeticOperation operation;
ArithmeticOperation(Location location, Expression left, Expression right, BinaryArithmeticOperation operation) {
super(location, left, right, operation.symbol());
this.operation = operation;
protected ArithmeticOperation(Location location, Expression left, Expression right, BinaryArithmeticOperation operation) {
super(location, left, right, operation);
}
@Override
@ -46,26 +37,8 @@ public abstract class ArithmeticOperation extends BinaryOperator {
return DataTypeConversion.commonType(left().dataType(), right().dataType());
}
@Override
public Object fold() {
return operation.apply((Number) left().fold(), (Number) right().fold());
}
@Override
protected ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript) {
String op = operation.symbol();
// escape %
if (operation == BinaryArithmeticOperation.MOD) {
op = "%" + op;
}
return new ScriptTemplate(format(Locale.ROOT, "(%s) %s (%s)", leftScript.template(), op, rightScript.template()),
paramsBuilder()
.script(leftScript.params()).script(rightScript.params())
.build(), dataType());
}
@Override
protected Pipe makePipe() {
return new BinaryArithmeticPipe(location(), this, Expressions.pipe(left()), Expressions.pipe(right()), operation);
return new BinaryArithmeticPipe(location(), this, Expressions.pipe(left()), Expressions.pipe(right()), function());
}
}

View File

@ -18,16 +18,14 @@ public class BinaryArithmeticPipe extends BinaryPipe {
private final BinaryArithmeticOperation operation;
public BinaryArithmeticPipe(Location location, Expression expression, Pipe left,
Pipe right, BinaryArithmeticOperation operation) {
public BinaryArithmeticPipe(Location location, Expression expression, Pipe left, Pipe right, BinaryArithmeticOperation operation) {
super(location, expression, left, right);
this.operation = operation;
}
@Override
protected NodeInfo<BinaryArithmeticPipe> info() {
return NodeInfo.create(this, BinaryArithmeticPipe::new,
expression(), left(), right(), operation);
return NodeInfo.create(this, BinaryArithmeticPipe::new, expression(), left(), right(), operation);
}
@Override

View File

@ -6,18 +6,18 @@
package org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryNumericProcessor;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.gen.processor.FunctionalBinaryProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import org.elasticsearch.xpack.sql.expression.predicate.PredicateBiFunction;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation;
import java.io.IOException;
import java.util.function.BiFunction;
public class BinaryArithmeticProcessor extends BinaryNumericProcessor<BinaryArithmeticOperation> {
public class BinaryArithmeticProcessor extends FunctionalBinaryProcessor<Number, Number, Number, BinaryArithmeticOperation> {
public enum BinaryArithmeticOperation implements BiFunction<Number, Number, Number> {
public enum BinaryArithmeticOperation implements PredicateBiFunction<Number, Number, Number> {
ADD(Arithmetics::add, "+"),
SUB(Arithmetics::sub, "-"),
MUL(Arithmetics::mul, "*"),
@ -32,12 +32,13 @@ public class BinaryArithmeticProcessor extends BinaryNumericProcessor<BinaryArit
this.symbol = symbol;
}
@Override
public String symbol() {
return symbol;
}
@Override
public final Number apply(Number left, Number right) {
public final Number doApply(Number left, Number right) {
return process.apply(left, right);
}
@ -57,13 +58,15 @@ public class BinaryArithmeticProcessor extends BinaryNumericProcessor<BinaryArit
super(in, i -> i.readEnum(BinaryArithmeticOperation.class));
}
@Override
protected void doWrite(StreamOutput out) throws IOException {
out.writeEnum(operation());
}
@Override
public String getWriteableName() {
return NAME;
}
@Override
protected void checkParameter(Object param) {
if (!(param instanceof Number)) {
throw new SqlIllegalArgumentException("A number is required; received {}", param);
}
}
}

View File

@ -8,25 +8,16 @@ package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
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.ScriptTemplate;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.Locale;
import static java.lang.String.format;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
// marker class to indicate operations that rely on values
public abstract class BinaryComparison extends BinaryOperator {
public abstract class BinaryComparison extends BinaryOperator<Object, Object, Boolean, BinaryComparisonOperation> {
private final BinaryComparisonOperation operation;
public BinaryComparison(Location location, Expression left, Expression right, BinaryComparisonOperation operation) {
super(location, left, right, operation.symbol());
this.operation = operation;
protected BinaryComparison(Location location, Expression left, Expression right, BinaryComparisonOperation operation) {
super(location, left, right, operation);
}
@Override
@ -34,25 +25,6 @@ public abstract class BinaryComparison extends BinaryOperator {
return TypeResolution.TYPE_RESOLVED;
}
@Override
protected ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript) {
String op = operation.symbol();
return new ScriptTemplate(format(Locale.ROOT, "(%s) %s (%s)", leftScript.template(), op, rightScript.template()),
paramsBuilder()
.script(leftScript.params()).script(rightScript.params())
.build(), dataType());
}
@Override
protected Pipe makePipe() {
return new BinaryComparisonPipe(location(), this, Expressions.pipe(left()), Expressions.pipe(right()), operation);
}
@Override
public Object fold() {
return operation.apply(left().fold(), right().fold());
}
@Override
protected Expression canonicalize() {
return left().hashCode() > right().hashCode() ? swapLeftAndRight() : this;
@ -63,8 +35,9 @@ public abstract class BinaryComparison extends BinaryOperator {
return DataType.BOOLEAN;
}
public static Integer compare(Object left, Object right) {
return Comparisons.compare(left, right);
@Override
protected Pipe makePipe() {
return new BinaryComparisonPipe(location(), this, Expressions.pipe(left()), Expressions.pipe(right()), function());
}
@Override
@ -77,4 +50,8 @@ public abstract class BinaryComparison extends BinaryOperator {
sb.append(right());
return sb.toString();
}
public static Integer compare(Object left, Object right) {
return Comparisons.compare(left, right);
}
}

View File

@ -18,8 +18,7 @@ public class BinaryComparisonPipe extends BinaryPipe {
private final BinaryComparisonOperation operation;
public BinaryComparisonPipe(Location location, Expression expression, Pipe left,
Pipe right, BinaryComparisonOperation operation) {
public BinaryComparisonPipe(Location location, Expression expression, Pipe left, Pipe right, BinaryComparisonOperation operation) {
super(location, expression, left, right);
this.operation = operation;
}

View File

@ -6,16 +6,17 @@
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xpack.sql.expression.gen.processor.FunctionalBinaryProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import org.elasticsearch.xpack.sql.expression.predicate.PredicateBiFunction;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
import java.io.IOException;
import java.util.function.BiFunction;
public class BinaryComparisonProcessor extends BinaryOperatorProcessor<BinaryComparisonOperation> {
public class BinaryComparisonProcessor extends FunctionalBinaryProcessor<Object, Object, Boolean, BinaryComparisonOperation> {
public enum BinaryComparisonOperation implements BiFunction<Object, Object, Boolean> {
public enum BinaryComparisonOperation implements PredicateBiFunction<Object, Object, Boolean> {
EQ(Comparisons::eq, "=="),
GT(Comparisons::gt, ">"),
@ -31,12 +32,13 @@ public class BinaryComparisonProcessor extends BinaryOperatorProcessor<BinaryCom
this.symbol = symbol;
}
@Override
public String symbol() {
return symbol;
}
@Override
public final Boolean apply(Object left, Object right) {
public final Boolean doApply(Object left, Object right) {
return process.apply(left, right);
}
@ -56,11 +58,6 @@ public class BinaryComparisonProcessor extends BinaryOperatorProcessor<BinaryCom
super(in, i -> i.readEnum(BinaryComparisonOperation.class));
}
@Override
protected void doWrite(StreamOutput out) throws IOException {
out.writeEnum(operation());
}
@Override
public String getWriteableName() {
return NAME;

View File

@ -1,76 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.xpack.sql.expression.gen.processor.BinaryProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import java.io.IOException;
import java.util.Locale;
import java.util.Objects;
import java.util.function.BiFunction;
public abstract class BinaryOperatorProcessor<O extends Enum<?> & BiFunction<Object, Object, Boolean>> extends BinaryProcessor {
private final O operation;
protected BinaryOperatorProcessor(Processor left, Processor right, O operation) {
super(left, right);
this.operation = operation;
}
protected BinaryOperatorProcessor(StreamInput in, Reader<O> reader) throws IOException {
super(in);
operation = reader.read(in);
}
protected O operation() {
return operation;
}
@Override
protected Object doProcess(Object left, Object right) {
if (left == null || right == null) {
return null;
}
checkParameter(left);
checkParameter(right);
return operation.apply(left, right);
}
protected void checkParameter(Object param) {
//no-op
}
@Override
public int hashCode() {
return Objects.hash(operation);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
BinaryOperatorProcessor<?> other = (BinaryOperatorProcessor<?>) obj;
return Objects.equals(operation, other.operation)
&& Objects.equals(left(), other.left())
&& Objects.equals(right(), other.right());
}
@Override
public String toString() {
return String.format(Locale.ROOT, "(%s %s %s)", left(), operation, right());
}
}

View File

@ -5,62 +5,32 @@
*/
package org.elasticsearch.xpack.sql.expression.predicate.regex;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryPredicate;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.regex.Pattern;
public class Like extends BinaryPredicate {
public class Like extends RegexMatch {
public Like(Location location, Expression left, LikePattern right) {
super(location, left, right, "LIKE");
super(location, left, right);
}
@Override
protected NodeInfo<Like> info() {
return NodeInfo.create(this, Like::new, left(), right());
return NodeInfo.create(this, Like::new, left(), pattern());
}
public LikePattern pattern() {
return (LikePattern) right();
}
@Override
protected BinaryPredicate replaceChildren(Expression newLeft, Expression newRight) {
protected Like replaceChildren(Expression newLeft, Expression newRight) {
return new Like(location(), newLeft, (LikePattern) newRight);
}
@Override
public LikePattern right() {
return (LikePattern) super.right();
}
@Override
public boolean foldable() {
// right() is not directly foldable in any context but Like can fold it.
return left().foldable();
}
@Override
public Object fold() {
Pattern p = Pattern.compile(right().asJavaRegex());
return p.matcher(left().fold().toString()).matches();
}
@Override
public DataType dataType() {
return DataType.BOOLEAN;
}
@Override
protected ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript) {
throw new SqlIllegalArgumentException("Not supported yet");
}
@Override
protected Pipe makePipe() {
throw new SqlIllegalArgumentException("Not supported yet");
protected String asString(Expression pattern) {
return ((LikePattern) pattern).asJavaRegex();
}
}

View File

@ -5,57 +5,29 @@
*/
package org.elasticsearch.xpack.sql.expression.predicate.regex;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.Literal;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryPredicate;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.regex.Pattern;
public class RLike extends BinaryPredicate {
public class RLike extends RegexMatch {
public RLike(Location location, Expression left, Literal right) {
super(location, left, right, "RLIKE");
super(location, left, right);
}
@Override
protected NodeInfo<RLike> info() {
return NodeInfo.create(this, RLike::new, left(), right());
return NodeInfo.create(this, RLike::new, left(), (Literal) right());
}
@Override
protected BinaryPredicate replaceChildren(Expression newLeft, Expression newRight) {
protected RLike replaceChildren(Expression newLeft, Expression newRight) {
return new RLike(location(), newLeft, (Literal) newRight);
}
@Override
public Literal right() {
return (Literal) super.right();
}
@Override
public Object fold() {
Pattern p = Pattern.compile(right().fold().toString());
return p.matcher(left().fold().toString()).matches();
}
@Override
public DataType dataType() {
return DataType.BOOLEAN;
}
@Override
protected ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript) {
throw new SqlIllegalArgumentException("Not supported yet");
}
@Override
protected Pipe makePipe() {
throw new SqlIllegalArgumentException("Not supported yet");
protected String asString(Expression pattern) {
return pattern.fold().toString();
}
}

View File

@ -0,0 +1,40 @@
/*
* 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.regex;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryPredicate;
import org.elasticsearch.xpack.sql.expression.predicate.regex.RegexProcessor.RegexOperation;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType;
public abstract class RegexMatch extends BinaryPredicate<String, String, Boolean, RegexOperation> {
protected RegexMatch(Location location, Expression value, Expression pattern) {
super(location, value, pattern, RegexOperation.INSTANCE);
}
@Override
public DataType dataType() {
return DataType.BOOLEAN;
}
@Override
public boolean foldable() {
// right() is not directly foldable in any context but Like can fold it.
return left().foldable();
}
@Override
public Boolean fold() {
Object val = left().fold();
val = val != null ? val.toString() : val;
return function().apply((String) val, asString(right()));
}
protected abstract String asString(Expression pattern);
}

View File

@ -0,0 +1,34 @@
/*
* 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.regex;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.BinaryPipe;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
public class RegexPipe extends BinaryPipe {
public RegexPipe(Location location, Expression expression, Pipe left, Pipe right) {
super(location, expression, left, right);
}
@Override
protected NodeInfo<RegexPipe> info() {
return NodeInfo.create(this, RegexPipe::new, expression(), left(), right());
}
@Override
protected BinaryPipe replaceChildren(Pipe left, Pipe right) {
return new RegexPipe(location(), expression(), left, right);
}
@Override
public RegexProcessor asProcessor() {
return new RegexProcessor(left().asProcessor(), right().asProcessor());
}
}

View File

@ -0,0 +1,98 @@
/*
* 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.regex;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.gen.processor.BinaryProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import org.elasticsearch.xpack.sql.expression.predicate.PredicateBiFunction;
import java.io.IOException;
import java.util.Objects;
import java.util.regex.Pattern;
public class RegexProcessor extends BinaryProcessor {
public static class RegexOperation implements PredicateBiFunction<String, String, Boolean> {
public static final RegexOperation INSTANCE = new RegexOperation();
@Override
public String name() {
return symbol();
}
@Override
public String symbol() {
return "REGEX";
}
@Override
public Boolean doApply(String value, String pattern) {
return match(value, pattern);
}
public static Boolean match(Object value, Object pattern) {
if (value == null || pattern == null) {
return null;
}
Pattern p = Pattern.compile(pattern.toString());
return p.matcher(value.toString()).matches();
}
}
public static final String NAME = "rgx";
public RegexProcessor(Processor value, Processor pattern) {
super(value, pattern);
}
public RegexProcessor(StreamInput in) throws IOException {
super(in);
}
@Override
protected Boolean doProcess(Object value, Object pattern) {
return RegexOperation.match(value, pattern);
}
@Override
protected void checkParameter(Object param) {
if (!(param instanceof String || param instanceof Character)) {
throw new SqlIllegalArgumentException("A string/char is required; received [{}]", param);
}
}
@Override
public String getWriteableName() {
return NAME;
}
@Override
protected void doWrite(StreamOutput out) throws IOException {}
@Override
public int hashCode() {
return Objects.hash(left(), right());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
RegexProcessor other = (RegexProcessor) obj;
return Objects.equals(left(), other.left()) && Objects.equals(right(), other.right());
}
}

View File

@ -36,14 +36,14 @@ import org.elasticsearch.xpack.sql.expression.function.aggregate.Stats;
import org.elasticsearch.xpack.sql.expression.function.scalar.Cast;
import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunction;
import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunctionAttribute;
import org.elasticsearch.xpack.sql.expression.predicate.And;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator.Negateable;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryPredicate;
import org.elasticsearch.xpack.sql.expression.predicate.Not;
import org.elasticsearch.xpack.sql.expression.predicate.Or;
import org.elasticsearch.xpack.sql.expression.predicate.Predicates;
import org.elasticsearch.xpack.sql.expression.predicate.Range;
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;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparison;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.Equals;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.GreaterThan;
@ -1138,7 +1138,7 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
@Override
protected Expression rule(Expression e) {
if (e instanceof BinaryPredicate) {
return simplifyAndOr((BinaryPredicate) e);
return simplifyAndOr((BinaryPredicate<?, ?, ?, ?>) e);
}
if (e instanceof Not) {
return simplifyNot((Not) e);
@ -1147,7 +1147,7 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
return e;
}
private Expression simplifyAndOr(BinaryPredicate bc) {
private Expression simplifyAndOr(BinaryPredicate<?, ?, ?, ?> bc) {
Expression l = bc.left();
Expression r = bc.right();
@ -1293,10 +1293,10 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
@Override
protected Expression rule(Expression e) {
return e instanceof BinaryOperator ? literalToTheRight((BinaryOperator) e) : e;
return e instanceof BinaryOperator ? literalToTheRight((BinaryOperator<?, ?, ?, ?>) e) : e;
}
private Expression literalToTheRight(BinaryOperator be) {
private Expression literalToTheRight(BinaryOperator<?, ?, ?, ?> be) {
return be.left() instanceof Literal && !(be.right() instanceof Literal) ? be.swapLeftAndRight() : be;
}
}

View File

@ -17,21 +17,22 @@ import org.elasticsearch.xpack.sql.expression.Exists;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.Literal;
import org.elasticsearch.xpack.sql.expression.Order;
import org.elasticsearch.xpack.sql.expression.Order.NullsPosition;
import org.elasticsearch.xpack.sql.expression.ScalarSubquery;
import org.elasticsearch.xpack.sql.expression.UnresolvedAttribute;
import org.elasticsearch.xpack.sql.expression.UnresolvedStar;
import org.elasticsearch.xpack.sql.expression.function.Function;
import org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction;
import org.elasticsearch.xpack.sql.expression.function.scalar.Cast;
import org.elasticsearch.xpack.sql.expression.predicate.And;
import org.elasticsearch.xpack.sql.expression.predicate.In;
import org.elasticsearch.xpack.sql.expression.predicate.IsNotNull;
import org.elasticsearch.xpack.sql.expression.predicate.Not;
import org.elasticsearch.xpack.sql.expression.predicate.Or;
import org.elasticsearch.xpack.sql.expression.predicate.Range;
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;
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;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Add;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Div;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Mod;
@ -349,7 +350,8 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
@Override
public Order visitOrderBy(OrderByContext ctx) {
return new Order(source(ctx), expression(ctx.expression()),
ctx.DESC() != null ? Order.OrderDirection.DESC : Order.OrderDirection.ASC);
ctx.DESC() != null ? Order.OrderDirection.DESC : Order.OrderDirection.ASC,
ctx.NULLS() != null ? (ctx.FIRST() != null ? NullsPosition.FIRST : NullsPosition.LAST) : null);
}
@Override

View File

@ -20,19 +20,19 @@ class SqlBaseLexer extends Lexer {
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,
DEBUG=18, DESC=19, DESCRIBE=20, DISTINCT=21, ESCAPE=22, EXECUTABLE=23,
EXISTS=24, EXPLAIN=25, EXTRACT=26, FALSE=27, FORMAT=28, FROM=29, FULL=30,
FUNCTIONS=31, GRAPHVIZ=32, GROUP=33, HAVING=34, IN=35, INNER=36, IS=37,
JOIN=38, LEFT=39, LIKE=40, LIMIT=41, MAPPED=42, MATCH=43, NATURAL=44,
NOT=45, NULL=46, ON=47, OPTIMIZED=48, OR=49, ORDER=50, OUTER=51, PARSED=52,
PHYSICAL=53, PLAN=54, RIGHT=55, RLIKE=56, QUERY=57, SCHEMAS=58, SELECT=59,
SHOW=60, SYS=61, TABLE=62, TABLES=63, TEXT=64, TRUE=65, TYPE=66, TYPES=67,
USING=68, VERIFY=69, WHERE=70, WITH=71, ESCAPE_ESC=72, FUNCTION_ESC=73,
LIMIT_ESC=74, DATE_ESC=75, TIME_ESC=76, TIMESTAMP_ESC=77, GUID_ESC=78,
ESC_END=79, EQ=80, NEQ=81, LT=82, LTE=83, GT=84, GTE=85, PLUS=86, MINUS=87,
ASTERISK=88, SLASH=89, PERCENT=90, CONCAT=91, DOT=92, PARAM=93, STRING=94,
INTEGER_VALUE=95, DECIMAL_VALUE=96, IDENTIFIER=97, DIGIT_IDENTIFIER=98,
TABLE_IDENTIFIER=99, QUOTED_IDENTIFIER=100, BACKQUOTED_IDENTIFIER=101,
SIMPLE_COMMENT=102, BRACKETED_COMMENT=103, WS=104, UNRECOGNIZED=105;
EXISTS=24, EXPLAIN=25, EXTRACT=26, FALSE=27, FIRST=28, FORMAT=29, FROM=30,
FULL=31, FUNCTIONS=32, GRAPHVIZ=33, GROUP=34, HAVING=35, IN=36, INNER=37,
IS=38, JOIN=39, LAST=40, LEFT=41, LIKE=42, LIMIT=43, MAPPED=44, MATCH=45,
NATURAL=46, NOT=47, NULL=48, NULLS=49, ON=50, OPTIMIZED=51, OR=52, ORDER=53,
OUTER=54, PARSED=55, PHYSICAL=56, PLAN=57, RIGHT=58, RLIKE=59, QUERY=60,
SCHEMAS=61, SELECT=62, SHOW=63, SYS=64, TABLE=65, TABLES=66, TEXT=67,
TRUE=68, TYPE=69, TYPES=70, USING=71, VERIFY=72, WHERE=73, WITH=74, ESCAPE_ESC=75,
FUNCTION_ESC=76, LIMIT_ESC=77, DATE_ESC=78, TIME_ESC=79, TIMESTAMP_ESC=80,
GUID_ESC=81, ESC_END=82, EQ=83, NEQ=84, LT=85, LTE=86, GT=87, GTE=88,
PLUS=89, MINUS=90, ASTERISK=91, SLASH=92, PERCENT=93, CONCAT=94, DOT=95,
PARAM=96, STRING=97, INTEGER_VALUE=98, DECIMAL_VALUE=99, IDENTIFIER=100,
DIGIT_IDENTIFIER=101, TABLE_IDENTIFIER=102, QUOTED_IDENTIFIER=103, BACKQUOTED_IDENTIFIER=104,
SIMPLE_COMMENT=105, BRACKETED_COMMENT=106, WS=107, UNRECOGNIZED=108;
public static String[] modeNames = {
"DEFAULT_MODE"
};
@ -41,18 +41,18 @@ class SqlBaseLexer extends Lexer {
"T__0", "T__1", "T__2", "T__3", "ALL", "ANALYZE", "ANALYZED", "AND", "ANY",
"AS", "ASC", "BETWEEN", "BY", "CAST", "CATALOG", "CATALOGS", "COLUMNS",
"DEBUG", "DESC", "DESCRIBE", "DISTINCT", "ESCAPE", "EXECUTABLE", "EXISTS",
"EXPLAIN", "EXTRACT", "FALSE", "FORMAT", "FROM", "FULL", "FUNCTIONS",
"GRAPHVIZ", "GROUP", "HAVING", "IN", "INNER", "IS", "JOIN", "LEFT", "LIKE",
"LIMIT", "MAPPED", "MATCH", "NATURAL", "NOT", "NULL", "ON", "OPTIMIZED",
"OR", "ORDER", "OUTER", "PARSED", "PHYSICAL", "PLAN", "RIGHT", "RLIKE",
"QUERY", "SCHEMAS", "SELECT", "SHOW", "SYS", "TABLE", "TABLES", "TEXT",
"TRUE", "TYPE", "TYPES", "USING", "VERIFY", "WHERE", "WITH", "ESCAPE_ESC",
"FUNCTION_ESC", "LIMIT_ESC", "DATE_ESC", "TIME_ESC", "TIMESTAMP_ESC",
"GUID_ESC", "ESC_END", "EQ", "NEQ", "LT", "LTE", "GT", "GTE", "PLUS",
"MINUS", "ASTERISK", "SLASH", "PERCENT", "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"
"EXPLAIN", "EXTRACT", "FALSE", "FIRST", "FORMAT", "FROM", "FULL", "FUNCTIONS",
"GRAPHVIZ", "GROUP", "HAVING", "IN", "INNER", "IS", "JOIN", "LAST", "LEFT",
"LIKE", "LIMIT", "MAPPED", "MATCH", "NATURAL", "NOT", "NULL", "NULLS",
"ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED", "PHYSICAL", "PLAN",
"RIGHT", "RLIKE", "QUERY", "SCHEMAS", "SELECT", "SHOW", "SYS", "TABLE",
"TABLES", "TEXT", "TRUE", "TYPE", "TYPES", "USING", "VERIFY", "WHERE",
"WITH", "ESCAPE_ESC", "FUNCTION_ESC", "LIMIT_ESC", "DATE_ESC", "TIME_ESC",
"TIMESTAMP_ESC", "GUID_ESC", "ESC_END", "EQ", "NEQ", "LT", "LTE", "GT",
"GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "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 = {
@ -60,32 +60,33 @@ class SqlBaseLexer extends Lexer {
"'AND'", "'ANY'", "'AS'", "'ASC'", "'BETWEEN'", "'BY'", "'CAST'", "'CATALOG'",
"'CATALOGS'", "'COLUMNS'", "'DEBUG'", "'DESC'", "'DESCRIBE'", "'DISTINCT'",
"'ESCAPE'", "'EXECUTABLE'", "'EXISTS'", "'EXPLAIN'", "'EXTRACT'", "'FALSE'",
"'FORMAT'", "'FROM'", "'FULL'", "'FUNCTIONS'", "'GRAPHVIZ'", "'GROUP'",
"'HAVING'", "'IN'", "'INNER'", "'IS'", "'JOIN'", "'LEFT'", "'LIKE'", "'LIMIT'",
"'MAPPED'", "'MATCH'", "'NATURAL'", "'NOT'", "'NULL'", "'ON'", "'OPTIMIZED'",
"'OR'", "'ORDER'", "'OUTER'", "'PARSED'", "'PHYSICAL'", "'PLAN'", "'RIGHT'",
"'RLIKE'", "'QUERY'", "'SCHEMAS'", "'SELECT'", "'SHOW'", "'SYS'", "'TABLE'",
"'TABLES'", "'TEXT'", "'TRUE'", "'TYPE'", "'TYPES'", "'USING'", "'VERIFY'",
"'WHERE'", "'WITH'", "'{ESCAPE'", "'{FN'", "'{LIMIT'", "'{D'", "'{T'",
"'{TS'", "'{GUID'", "'}'", "'='", null, "'<'", "'<='", "'>'", "'>='",
"'+'", "'-'", "'*'", "'/'", "'%'", "'||'", "'.'", "'?'"
"'FIRST'", "'FORMAT'", "'FROM'", "'FULL'", "'FUNCTIONS'", "'GRAPHVIZ'",
"'GROUP'", "'HAVING'", "'IN'", "'INNER'", "'IS'", "'JOIN'", "'LAST'",
"'LEFT'", "'LIKE'", "'LIMIT'", "'MAPPED'", "'MATCH'", "'NATURAL'", "'NOT'",
"'NULL'", "'NULLS'", "'ON'", "'OPTIMIZED'", "'OR'", "'ORDER'", "'OUTER'",
"'PARSED'", "'PHYSICAL'", "'PLAN'", "'RIGHT'", "'RLIKE'", "'QUERY'", "'SCHEMAS'",
"'SELECT'", "'SHOW'", "'SYS'", "'TABLE'", "'TABLES'", "'TEXT'", "'TRUE'",
"'TYPE'", "'TYPES'", "'USING'", "'VERIFY'", "'WHERE'", "'WITH'", "'{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",
"DEBUG", "DESC", "DESCRIBE", "DISTINCT", "ESCAPE", "EXECUTABLE", "EXISTS",
"EXPLAIN", "EXTRACT", "FALSE", "FORMAT", "FROM", "FULL", "FUNCTIONS",
"GRAPHVIZ", "GROUP", "HAVING", "IN", "INNER", "IS", "JOIN", "LEFT", "LIKE",
"LIMIT", "MAPPED", "MATCH", "NATURAL", "NOT", "NULL", "ON", "OPTIMIZED",
"OR", "ORDER", "OUTER", "PARSED", "PHYSICAL", "PLAN", "RIGHT", "RLIKE",
"QUERY", "SCHEMAS", "SELECT", "SHOW", "SYS", "TABLE", "TABLES", "TEXT",
"TRUE", "TYPE", "TYPES", "USING", "VERIFY", "WHERE", "WITH", "ESCAPE_ESC",
"FUNCTION_ESC", "LIMIT_ESC", "DATE_ESC", "TIME_ESC", "TIMESTAMP_ESC",
"GUID_ESC", "ESC_END", "EQ", "NEQ", "LT", "LTE", "GT", "GTE", "PLUS",
"MINUS", "ASTERISK", "SLASH", "PERCENT", "CONCAT", "DOT", "PARAM", "STRING",
"INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER", "DIGIT_IDENTIFIER", "TABLE_IDENTIFIER",
"QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", "SIMPLE_COMMENT", "BRACKETED_COMMENT",
"WS", "UNRECOGNIZED"
"EXPLAIN", "EXTRACT", "FALSE", "FIRST", "FORMAT", "FROM", "FULL", "FUNCTIONS",
"GRAPHVIZ", "GROUP", "HAVING", "IN", "INNER", "IS", "JOIN", "LAST", "LEFT",
"LIKE", "LIMIT", "MAPPED", "MATCH", "NATURAL", "NOT", "NULL", "NULLS",
"ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED", "PHYSICAL", "PLAN",
"RIGHT", "RLIKE", "QUERY", "SCHEMAS", "SELECT", "SHOW", "SYS", "TABLE",
"TABLES", "TEXT", "TRUE", "TYPE", "TYPES", "USING", "VERIFY", "WHERE",
"WITH", "ESCAPE_ESC", "FUNCTION_ESC", "LIMIT_ESC", "DATE_ESC", "TIME_ESC",
"TIMESTAMP_ESC", "GUID_ESC", "ESC_END", "EQ", "NEQ", "LT", "LTE", "GT",
"GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "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);
@ -142,7 +143,7 @@ class SqlBaseLexer extends Lexer {
public ATN getATN() { return _ATN; }
public static final String _serializedATN =
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2k\u036f\b\1\4\2\t"+
"\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2n\u0386\b\1\4\2\t"+
"\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"+
"\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+
@ -154,54 +155,55 @@ class SqlBaseLexer extends Lexer {
"\tI\4J\tJ\4K\tK\4L\tL\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\tT"+
"\4U\tU\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\4]\t]\4^\t^\4_\t_\4"+
"`\t`\4a\ta\4b\tb\4c\tc\4d\td\4e\te\4f\tf\4g\tg\4h\th\4i\ti\4j\tj\4k\t"+
"k\4l\tl\4m\tm\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\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\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\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\31"+
"\3\31\3\31\3\31\3\31\3\31\3\31\3\32\3\32\3\32\3\32\3\32\3\32\3\32\3\32"+
"\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\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\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\60\3\60\3\60\3\61\3\61\3\61\3\61"+
"\3\61\3\61\3\61\3\61\3\61\3\61\3\62\3\62\3\62\3\63\3\63\3\63\3\63\3\63"+
"\3\63\3\64\3\64\3\64\3\64\3\64\3\64\3\65\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\66\3\66\3\67\3\67\3\67\3\67\3\67"+
"\38\38\38\38\38\38\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@\3A\3A\3A\3A\3A\3B\3B\3B\3B\3B\3C\3C\3C\3C"+
"\3C\3D\3D\3D\3D\3D\3D\3E\3E\3E\3E\3E\3E\3F\3F\3F\3F\3F\3F\3F\3G\3G\3G"+
"\3G\3G\3G\3H\3H\3H\3H\3H\3I\3I\3I\3I\3I\3I\3I\3I\3J\3J\3J\3J\3K\3K\3K"+
"\3K\3K\3K\3K\3L\3L\3L\3M\3M\3M\3N\3N\3N\3N\3O\3O\3O\3O\3O\3O\3P\3P\3Q"+
"\3Q\3R\3R\3R\3R\3R\3R\3R\5R\u02b0\nR\3S\3S\3T\3T\3T\3U\3U\3V\3V\3V\3W"+
"\3W\3X\3X\3Y\3Y\3Z\3Z\3[\3[\3\\\3\\\3\\\3]\3]\3^\3^\3_\3_\3_\3_\7_\u02d1"+
"\n_\f_\16_\u02d4\13_\3_\3_\3`\6`\u02d9\n`\r`\16`\u02da\3a\6a\u02de\na"+
"\ra\16a\u02df\3a\3a\7a\u02e4\na\fa\16a\u02e7\13a\3a\3a\6a\u02eb\na\ra"+
"\16a\u02ec\3a\6a\u02f0\na\ra\16a\u02f1\3a\3a\7a\u02f6\na\fa\16a\u02f9"+
"\13a\5a\u02fb\na\3a\3a\3a\3a\6a\u0301\na\ra\16a\u0302\3a\3a\5a\u0307\n"+
"a\3b\3b\5b\u030b\nb\3b\3b\3b\7b\u0310\nb\fb\16b\u0313\13b\3c\3c\3c\3c"+
"\6c\u0319\nc\rc\16c\u031a\3d\3d\3d\6d\u0320\nd\rd\16d\u0321\3e\3e\3e\3"+
"e\7e\u0328\ne\fe\16e\u032b\13e\3e\3e\3f\3f\3f\3f\7f\u0333\nf\ff\16f\u0336"+
"\13f\3f\3f\3g\3g\5g\u033c\ng\3g\6g\u033f\ng\rg\16g\u0340\3h\3h\3i\3i\3"+
"j\3j\3j\3j\7j\u034b\nj\fj\16j\u034e\13j\3j\5j\u0351\nj\3j\5j\u0354\nj"+
"\3j\3j\3k\3k\3k\3k\3k\7k\u035d\nk\fk\16k\u0360\13k\3k\3k\3k\3k\3k\3l\6"+
"l\u0368\nl\rl\16l\u0369\3l\3l\3m\3m\3\u035e\2n\3\3\5\4\7\5\t\6\13\7\r"+
"\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17\35\20\37\21!\22#\23%\24\'\25"+
")\26+\27-\30/\31\61\32\63\33\65\34\67\359\36;\37= ?!A\"C#E$G%I&K\'M(O"+
")Q*S+U,W-Y.[/]\60_\61a\62c\63e\64g\65i\66k\67m8o9q:s;u<w=y>{?}@\177A\u0081"+
"B\u0083C\u0085D\u0087E\u0089F\u008bG\u008dH\u008fI\u0091J\u0093K\u0095"+
"L\u0097M\u0099N\u009bO\u009dP\u009fQ\u00a1R\u00a3S\u00a5T\u00a7U\u00a9"+
"V\u00abW\u00adX\u00afY\u00b1Z\u00b3[\u00b5\\\u00b7]\u00b9^\u00bb_\u00bd"+
"`\u00bfa\u00c1b\u00c3c\u00c5d\u00c7e\u00c9f\u00cbg\u00cd\2\u00cf\2\u00d1"+
"\2\u00d3h\u00d5i\u00d7j\u00d9k\3\2\f\3\2))\4\2BBaa\5\2<<BBaa\3\2$$\3\2"+
"bb\4\2--//\3\2\62;\3\2C\\\4\2\f\f\17\17\5\2\13\f\17\17\"\"\u0390\2\3\3"+
"k\4l\tl\4m\tm\4n\tn\4o\to\4p\tp\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\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\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\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30"+
"\3\30\3\30\3\30\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\32\3\32\3\32\3\32"+
"\3\32\3\32\3\32\3\32\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\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\36\3\36\3\36\3\36"+
"\3\36\3\36\3\36\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\60"+
"\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\62"+
"\3\63\3\63\3\63\3\64\3\64\3\64\3\64\3\64\3\64\3\64\3\64\3\64\3\64\3\65"+
"\3\65\3\65\3\66\3\66\3\66\3\66\3\66\3\66\3\67\3\67\3\67\3\67\3\67\3\67"+
"\38\38\38\38\38\38\38\39\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@\3@\3@\3@\3@\3A\3A\3A\3A\3B\3B\3B\3B\3B\3B"+
"\3C\3C\3C\3C\3C\3C\3C\3D\3D\3D\3D\3D\3E\3E\3E\3E\3E\3F\3F\3F\3F\3F\3G"+
"\3G\3G\3G\3G\3G\3H\3H\3H\3H\3H\3H\3I\3I\3I\3I\3I\3I\3I\3J\3J\3J\3J\3J"+
"\3J\3K\3K\3K\3K\3K\3L\3L\3L\3L\3L\3L\3L\3L\3M\3M\3M\3M\3N\3N\3N\3N\3N"+
"\3N\3N\3O\3O\3O\3P\3P\3P\3Q\3Q\3Q\3Q\3R\3R\3R\3R\3R\3R\3S\3S\3T\3T\3U"+
"\3U\3U\3U\3U\3U\3U\5U\u02c7\nU\3V\3V\3W\3W\3W\3X\3X\3Y\3Y\3Y\3Z\3Z\3["+
"\3[\3\\\3\\\3]\3]\3^\3^\3_\3_\3_\3`\3`\3a\3a\3b\3b\3b\3b\7b\u02e8\nb\f"+
"b\16b\u02eb\13b\3b\3b\3c\6c\u02f0\nc\rc\16c\u02f1\3d\6d\u02f5\nd\rd\16"+
"d\u02f6\3d\3d\7d\u02fb\nd\fd\16d\u02fe\13d\3d\3d\6d\u0302\nd\rd\16d\u0303"+
"\3d\6d\u0307\nd\rd\16d\u0308\3d\3d\7d\u030d\nd\fd\16d\u0310\13d\5d\u0312"+
"\nd\3d\3d\3d\3d\6d\u0318\nd\rd\16d\u0319\3d\3d\5d\u031e\nd\3e\3e\5e\u0322"+
"\ne\3e\3e\3e\7e\u0327\ne\fe\16e\u032a\13e\3f\3f\3f\3f\6f\u0330\nf\rf\16"+
"f\u0331\3g\3g\3g\6g\u0337\ng\rg\16g\u0338\3h\3h\3h\3h\7h\u033f\nh\fh\16"+
"h\u0342\13h\3h\3h\3i\3i\3i\3i\7i\u034a\ni\fi\16i\u034d\13i\3i\3i\3j\3"+
"j\5j\u0353\nj\3j\6j\u0356\nj\rj\16j\u0357\3k\3k\3l\3l\3m\3m\3m\3m\7m\u0362"+
"\nm\fm\16m\u0365\13m\3m\5m\u0368\nm\3m\5m\u036b\nm\3m\3m\3n\3n\3n\3n\3"+
"n\7n\u0374\nn\fn\16n\u0377\13n\3n\3n\3n\3n\3n\3o\6o\u037f\no\ro\16o\u0380"+
"\3o\3o\3p\3p\3\u0375\2q\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\62"+
"c\63e\64g\65i\66k\67m8o9q:s;u<w=y>{?}@\177A\u0081B\u0083C\u0085D\u0087"+
"E\u0089F\u008bG\u008dH\u008fI\u0091J\u0093K\u0095L\u0097M\u0099N\u009b"+
"O\u009dP\u009fQ\u00a1R\u00a3S\u00a5T\u00a7U\u00a9V\u00abW\u00adX\u00af"+
"Y\u00b1Z\u00b3[\u00b5\\\u00b7]\u00b9^\u00bb_\u00bd`\u00bfa\u00c1b\u00c3"+
"c\u00c5d\u00c7e\u00c9f\u00cbg\u00cdh\u00cfi\u00d1j\u00d3\2\u00d5\2\u00d7"+
"\2\u00d9k\u00dbl\u00ddm\u00dfn\3\2\f\3\2))\4\2BBaa\5\2<<BBaa\3\2$$\3\2"+
"bb\4\2--//\3\2\62;\3\2C\\\4\2\f\f\17\17\5\2\13\f\17\17\"\"\u03a7\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"+
@ -220,225 +222,231 @@ class SqlBaseLexer extends Lexer {
"\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\u00d3"+
"\3\2\2\2\2\u00d5\3\2\2\2\2\u00d7\3\2\2\2\2\u00d9\3\2\2\2\3\u00db\3\2\2"+
"\2\5\u00dd\3\2\2\2\7\u00df\3\2\2\2\t\u00e1\3\2\2\2\13\u00e3\3\2\2\2\r"+
"\u00e7\3\2\2\2\17\u00ef\3\2\2\2\21\u00f8\3\2\2\2\23\u00fc\3\2\2\2\25\u0100"+
"\3\2\2\2\27\u0103\3\2\2\2\31\u0107\3\2\2\2\33\u010f\3\2\2\2\35\u0112\3"+
"\2\2\2\37\u0117\3\2\2\2!\u011f\3\2\2\2#\u0128\3\2\2\2%\u0130\3\2\2\2\'"+
"\u0136\3\2\2\2)\u013b\3\2\2\2+\u0144\3\2\2\2-\u014d\3\2\2\2/\u0154\3\2"+
"\2\2\61\u015f\3\2\2\2\63\u0166\3\2\2\2\65\u016e\3\2\2\2\67\u0176\3\2\2"+
"\29\u017c\3\2\2\2;\u0183\3\2\2\2=\u0188\3\2\2\2?\u018d\3\2\2\2A\u0197"+
"\3\2\2\2C\u01a0\3\2\2\2E\u01a6\3\2\2\2G\u01ad\3\2\2\2I\u01b0\3\2\2\2K"+
"\u01b6\3\2\2\2M\u01b9\3\2\2\2O\u01be\3\2\2\2Q\u01c3\3\2\2\2S\u01c8\3\2"+
"\2\2U\u01ce\3\2\2\2W\u01d5\3\2\2\2Y\u01db\3\2\2\2[\u01e3\3\2\2\2]\u01e7"+
"\3\2\2\2_\u01ec\3\2\2\2a\u01ef\3\2\2\2c\u01f9\3\2\2\2e\u01fc\3\2\2\2g"+
"\u0202\3\2\2\2i\u0208\3\2\2\2k\u020f\3\2\2\2m\u0218\3\2\2\2o\u021d\3\2"+
"\2\2q\u0223\3\2\2\2s\u0229\3\2\2\2u\u022f\3\2\2\2w\u0237\3\2\2\2y\u023e"+
"\3\2\2\2{\u0243\3\2\2\2}\u0247\3\2\2\2\177\u024d\3\2\2\2\u0081\u0254\3"+
"\2\2\2\u0083\u0259\3\2\2\2\u0085\u025e\3\2\2\2\u0087\u0263\3\2\2\2\u0089"+
"\u0269\3\2\2\2\u008b\u026f\3\2\2\2\u008d\u0276\3\2\2\2\u008f\u027c\3\2"+
"\2\2\u0091\u0281\3\2\2\2\u0093\u0289\3\2\2\2\u0095\u028d\3\2\2\2\u0097"+
"\u0294\3\2\2\2\u0099\u0297\3\2\2\2\u009b\u029a\3\2\2\2\u009d\u029e\3\2"+
"\2\2\u009f\u02a4\3\2\2\2\u00a1\u02a6\3\2\2\2\u00a3\u02af\3\2\2\2\u00a5"+
"\u02b1\3\2\2\2\u00a7\u02b3\3\2\2\2\u00a9\u02b6\3\2\2\2\u00ab\u02b8\3\2"+
"\2\2\u00ad\u02bb\3\2\2\2\u00af\u02bd\3\2\2\2\u00b1\u02bf\3\2\2\2\u00b3"+
"\u02c1\3\2\2\2\u00b5\u02c3\3\2\2\2\u00b7\u02c5\3\2\2\2\u00b9\u02c8\3\2"+
"\2\2\u00bb\u02ca\3\2\2\2\u00bd\u02cc\3\2\2\2\u00bf\u02d8\3\2\2\2\u00c1"+
"\u0306\3\2\2\2\u00c3\u030a\3\2\2\2\u00c5\u0314\3\2\2\2\u00c7\u031f\3\2"+
"\2\2\u00c9\u0323\3\2\2\2\u00cb\u032e\3\2\2\2\u00cd\u0339\3\2\2\2\u00cf"+
"\u0342\3\2\2\2\u00d1\u0344\3\2\2\2\u00d3\u0346\3\2\2\2\u00d5\u0357\3\2"+
"\2\2\u00d7\u0367\3\2\2\2\u00d9\u036d\3\2\2\2\u00db\u00dc\7*\2\2\u00dc"+
"\4\3\2\2\2\u00dd\u00de\7+\2\2\u00de\6\3\2\2\2\u00df\u00e0\7.\2\2\u00e0"+
"\b\3\2\2\2\u00e1\u00e2\7<\2\2\u00e2\n\3\2\2\2\u00e3\u00e4\7C\2\2\u00e4"+
"\u00e5\7N\2\2\u00e5\u00e6\7N\2\2\u00e6\f\3\2\2\2\u00e7\u00e8\7C\2\2\u00e8"+
"\u00e9\7P\2\2\u00e9\u00ea\7C\2\2\u00ea\u00eb\7N\2\2\u00eb\u00ec\7[\2\2"+
"\u00ec\u00ed\7\\\2\2\u00ed\u00ee\7G\2\2\u00ee\16\3\2\2\2\u00ef\u00f0\7"+
"C\2\2\u00f0\u00f1\7P\2\2\u00f1\u00f2\7C\2\2\u00f2\u00f3\7N\2\2\u00f3\u00f4"+
"\7[\2\2\u00f4\u00f5\7\\\2\2\u00f5\u00f6\7G\2\2\u00f6\u00f7\7F\2\2\u00f7"+
"\20\3\2\2\2\u00f8\u00f9\7C\2\2\u00f9\u00fa\7P\2\2\u00fa\u00fb\7F\2\2\u00fb"+
"\22\3\2\2\2\u00fc\u00fd\7C\2\2\u00fd\u00fe\7P\2\2\u00fe\u00ff\7[\2\2\u00ff"+
"\24\3\2\2\2\u0100\u0101\7C\2\2\u0101\u0102\7U\2\2\u0102\26\3\2\2\2\u0103"+
"\u0104\7C\2\2\u0104\u0105\7U\2\2\u0105\u0106\7E\2\2\u0106\30\3\2\2\2\u0107"+
"\u0108\7D\2\2\u0108\u0109\7G\2\2\u0109\u010a\7V\2\2\u010a\u010b\7Y\2\2"+
"\u010b\u010c\7G\2\2\u010c\u010d\7G\2\2\u010d\u010e\7P\2\2\u010e\32\3\2"+
"\2\2\u010f\u0110\7D\2\2\u0110\u0111\7[\2\2\u0111\34\3\2\2\2\u0112\u0113"+
"\7E\2\2\u0113\u0114\7C\2\2\u0114\u0115\7U\2\2\u0115\u0116\7V\2\2\u0116"+
"\36\3\2\2\2\u0117\u0118\7E\2\2\u0118\u0119\7C\2\2\u0119\u011a\7V\2\2\u011a"+
"\u011b\7C\2\2\u011b\u011c\7N\2\2\u011c\u011d\7Q\2\2\u011d\u011e\7I\2\2"+
"\u011e \3\2\2\2\u011f\u0120\7E\2\2\u0120\u0121\7C\2\2\u0121\u0122\7V\2"+
"\2\u0122\u0123\7C\2\2\u0123\u0124\7N\2\2\u0124\u0125\7Q\2\2\u0125\u0126"+
"\7I\2\2\u0126\u0127\7U\2\2\u0127\"\3\2\2\2\u0128\u0129\7E\2\2\u0129\u012a"+
"\7Q\2\2\u012a\u012b\7N\2\2\u012b\u012c\7W\2\2\u012c\u012d\7O\2\2\u012d"+
"\u012e\7P\2\2\u012e\u012f\7U\2\2\u012f$\3\2\2\2\u0130\u0131\7F\2\2\u0131"+
"\u0132\7G\2\2\u0132\u0133\7D\2\2\u0133\u0134\7W\2\2\u0134\u0135\7I\2\2"+
"\u0135&\3\2\2\2\u0136\u0137\7F\2\2\u0137\u0138\7G\2\2\u0138\u0139\7U\2"+
"\2\u0139\u013a\7E\2\2\u013a(\3\2\2\2\u013b\u013c\7F\2\2\u013c\u013d\7"+
"G\2\2\u013d\u013e\7U\2\2\u013e\u013f\7E\2\2\u013f\u0140\7T\2\2\u0140\u0141"+
"\7K\2\2\u0141\u0142\7D\2\2\u0142\u0143\7G\2\2\u0143*\3\2\2\2\u0144\u0145"+
"\7F\2\2\u0145\u0146\7K\2\2\u0146\u0147\7U\2\2\u0147\u0148\7V\2\2\u0148"+
"\u0149\7K\2\2\u0149\u014a\7P\2\2\u014a\u014b\7E\2\2\u014b\u014c\7V\2\2"+
"\u014c,\3\2\2\2\u014d\u014e\7G\2\2\u014e\u014f\7U\2\2\u014f\u0150\7E\2"+
"\2\u0150\u0151\7C\2\2\u0151\u0152\7R\2\2\u0152\u0153\7G\2\2\u0153.\3\2"+
"\2\2\u0154\u0155\7G\2\2\u0155\u0156\7Z\2\2\u0156\u0157\7G\2\2\u0157\u0158"+
"\7E\2\2\u0158\u0159\7W\2\2\u0159\u015a\7V\2\2\u015a\u015b\7C\2\2\u015b"+
"\u015c\7D\2\2\u015c\u015d\7N\2\2\u015d\u015e\7G\2\2\u015e\60\3\2\2\2\u015f"+
"\u0160\7G\2\2\u0160\u0161\7Z\2\2\u0161\u0162\7K\2\2\u0162\u0163\7U\2\2"+
"\u0163\u0164\7V\2\2\u0164\u0165\7U\2\2\u0165\62\3\2\2\2\u0166\u0167\7"+
"G\2\2\u0167\u0168\7Z\2\2\u0168\u0169\7R\2\2\u0169\u016a\7N\2\2\u016a\u016b"+
"\7C\2\2\u016b\u016c\7K\2\2\u016c\u016d\7P\2\2\u016d\64\3\2\2\2\u016e\u016f"+
"\7G\2\2\u016f\u0170\7Z\2\2\u0170\u0171\7V\2\2\u0171\u0172\7T\2\2\u0172"+
"\u0173\7C\2\2\u0173\u0174\7E\2\2\u0174\u0175\7V\2\2\u0175\66\3\2\2\2\u0176"+
"\u0177\7H\2\2\u0177\u0178\7C\2\2\u0178\u0179\7N\2\2\u0179\u017a\7U\2\2"+
"\u017a\u017b\7G\2\2\u017b8\3\2\2\2\u017c\u017d\7H\2\2\u017d\u017e\7Q\2"+
"\2\u017e\u017f\7T\2\2\u017f\u0180\7O\2\2\u0180\u0181\7C\2\2\u0181\u0182"+
"\7V\2\2\u0182:\3\2\2\2\u0183\u0184\7H\2\2\u0184\u0185\7T\2\2\u0185\u0186"+
"\7Q\2\2\u0186\u0187\7O\2\2\u0187<\3\2\2\2\u0188\u0189\7H\2\2\u0189\u018a"+
"\7W\2\2\u018a\u018b\7N\2\2\u018b\u018c\7N\2\2\u018c>\3\2\2\2\u018d\u018e"+
"\7H\2\2\u018e\u018f\7W\2\2\u018f\u0190\7P\2\2\u0190\u0191\7E\2\2\u0191"+
"\u0192\7V\2\2\u0192\u0193\7K\2\2\u0193\u0194\7Q\2\2\u0194\u0195\7P\2\2"+
"\u0195\u0196\7U\2\2\u0196@\3\2\2\2\u0197\u0198\7I\2\2\u0198\u0199\7T\2"+
"\2\u0199\u019a\7C\2\2\u019a\u019b\7R\2\2\u019b\u019c\7J\2\2\u019c\u019d"+
"\7X\2\2\u019d\u019e\7K\2\2\u019e\u019f\7\\\2\2\u019fB\3\2\2\2\u01a0\u01a1"+
"\7I\2\2\u01a1\u01a2\7T\2\2\u01a2\u01a3\7Q\2\2\u01a3\u01a4\7W\2\2\u01a4"+
"\u01a5\7R\2\2\u01a5D\3\2\2\2\u01a6\u01a7\7J\2\2\u01a7\u01a8\7C\2\2\u01a8"+
"\u01a9\7X\2\2\u01a9\u01aa\7K\2\2\u01aa\u01ab\7P\2\2\u01ab\u01ac\7I\2\2"+
"\u01acF\3\2\2\2\u01ad\u01ae\7K\2\2\u01ae\u01af\7P\2\2\u01afH\3\2\2\2\u01b0"+
"\u01b1\7K\2\2\u01b1\u01b2\7P\2\2\u01b2\u01b3\7P\2\2\u01b3\u01b4\7G\2\2"+
"\u01b4\u01b5\7T\2\2\u01b5J\3\2\2\2\u01b6\u01b7\7K\2\2\u01b7\u01b8\7U\2"+
"\2\u01b8L\3\2\2\2\u01b9\u01ba\7L\2\2\u01ba\u01bb\7Q\2\2\u01bb\u01bc\7"+
"K\2\2\u01bc\u01bd\7P\2\2\u01bdN\3\2\2\2\u01be\u01bf\7N\2\2\u01bf\u01c0"+
"\7G\2\2\u01c0\u01c1\7H\2\2\u01c1\u01c2\7V\2\2\u01c2P\3\2\2\2\u01c3\u01c4"+
"\7N\2\2\u01c4\u01c5\7K\2\2\u01c5\u01c6\7M\2\2\u01c6\u01c7\7G\2\2\u01c7"+
"R\3\2\2\2\u01c8\u01c9\7N\2\2\u01c9\u01ca\7K\2\2\u01ca\u01cb\7O\2\2\u01cb"+
"\u01cc\7K\2\2\u01cc\u01cd\7V\2\2\u01cdT\3\2\2\2\u01ce\u01cf\7O\2\2\u01cf"+
"\u01d0\7C\2\2\u01d0\u01d1\7R\2\2\u01d1\u01d2\7R\2\2\u01d2\u01d3\7G\2\2"+
"\u01d3\u01d4\7F\2\2\u01d4V\3\2\2\2\u01d5\u01d6\7O\2\2\u01d6\u01d7\7C\2"+
"\2\u01d7\u01d8\7V\2\2\u01d8\u01d9\7E\2\2\u01d9\u01da\7J\2\2\u01daX\3\2"+
"\2\2\u01db\u01dc\7P\2\2\u01dc\u01dd\7C\2\2\u01dd\u01de\7V\2\2\u01de\u01df"+
"\7W\2\2\u01df\u01e0\7T\2\2\u01e0\u01e1\7C\2\2\u01e1\u01e2\7N\2\2\u01e2"+
"Z\3\2\2\2\u01e3\u01e4\7P\2\2\u01e4\u01e5\7Q\2\2\u01e5\u01e6\7V\2\2\u01e6"+
"\\\3\2\2\2\u01e7\u01e8\7P\2\2\u01e8\u01e9\7W\2\2\u01e9\u01ea\7N\2\2\u01ea"+
"\u01eb\7N\2\2\u01eb^\3\2\2\2\u01ec\u01ed\7Q\2\2\u01ed\u01ee\7P\2\2\u01ee"+
"`\3\2\2\2\u01ef\u01f0\7Q\2\2\u01f0\u01f1\7R\2\2\u01f1\u01f2\7V\2\2\u01f2"+
"\u01f3\7K\2\2\u01f3\u01f4\7O\2\2\u01f4\u01f5\7K\2\2\u01f5\u01f6\7\\\2"+
"\2\u01f6\u01f7\7G\2\2\u01f7\u01f8\7F\2\2\u01f8b\3\2\2\2\u01f9\u01fa\7"+
"Q\2\2\u01fa\u01fb\7T\2\2\u01fbd\3\2\2\2\u01fc\u01fd\7Q\2\2\u01fd\u01fe"+
"\7T\2\2\u01fe\u01ff\7F\2\2\u01ff\u0200\7G\2\2\u0200\u0201\7T\2\2\u0201"+
"f\3\2\2\2\u0202\u0203\7Q\2\2\u0203\u0204\7W\2\2\u0204\u0205\7V\2\2\u0205"+
"\u0206\7G\2\2\u0206\u0207\7T\2\2\u0207h\3\2\2\2\u0208\u0209\7R\2\2\u0209"+
"\u020a\7C\2\2\u020a\u020b\7T\2\2\u020b\u020c\7U\2\2\u020c\u020d\7G\2\2"+
"\u020d\u020e\7F\2\2\u020ej\3\2\2\2\u020f\u0210\7R\2\2\u0210\u0211\7J\2"+
"\2\u0211\u0212\7[\2\2\u0212\u0213\7U\2\2\u0213\u0214\7K\2\2\u0214\u0215"+
"\7E\2\2\u0215\u0216\7C\2\2\u0216\u0217\7N\2\2\u0217l\3\2\2\2\u0218\u0219"+
"\7R\2\2\u0219\u021a\7N\2\2\u021a\u021b\7C\2\2\u021b\u021c\7P\2\2\u021c"+
"n\3\2\2\2\u021d\u021e\7T\2\2\u021e\u021f\7K\2\2\u021f\u0220\7I\2\2\u0220"+
"\u0221\7J\2\2\u0221\u0222\7V\2\2\u0222p\3\2\2\2\u0223\u0224\7T\2\2\u0224"+
"\u0225\7N\2\2\u0225\u0226\7K\2\2\u0226\u0227\7M\2\2\u0227\u0228\7G\2\2"+
"\u0228r\3\2\2\2\u0229\u022a\7S\2\2\u022a\u022b\7W\2\2\u022b\u022c\7G\2"+
"\2\u022c\u022d\7T\2\2\u022d\u022e\7[\2\2\u022et\3\2\2\2\u022f\u0230\7"+
"U\2\2\u0230\u0231\7E\2\2\u0231\u0232\7J\2\2\u0232\u0233\7G\2\2\u0233\u0234"+
"\7O\2\2\u0234\u0235\7C\2\2\u0235\u0236\7U\2\2\u0236v\3\2\2\2\u0237\u0238"+
"\7U\2\2\u0238\u0239\7G\2\2\u0239\u023a\7N\2\2\u023a\u023b\7G\2\2\u023b"+
"\u023c\7E\2\2\u023c\u023d\7V\2\2\u023dx\3\2\2\2\u023e\u023f\7U\2\2\u023f"+
"\u0240\7J\2\2\u0240\u0241\7Q\2\2\u0241\u0242\7Y\2\2\u0242z\3\2\2\2\u0243"+
"\u0244\7U\2\2\u0244\u0245\7[\2\2\u0245\u0246\7U\2\2\u0246|\3\2\2\2\u0247"+
"\u0248\7V\2\2\u0248\u0249\7C\2\2\u0249\u024a\7D\2\2\u024a\u024b\7N\2\2"+
"\u024b\u024c\7G\2\2\u024c~\3\2\2\2\u024d\u024e\7V\2\2\u024e\u024f\7C\2"+
"\2\u024f\u0250\7D\2\2\u0250\u0251\7N\2\2\u0251\u0252\7G\2\2\u0252\u0253"+
"\7U\2\2\u0253\u0080\3\2\2\2\u0254\u0255\7V\2\2\u0255\u0256\7G\2\2\u0256"+
"\u0257\7Z\2\2\u0257\u0258\7V\2\2\u0258\u0082\3\2\2\2\u0259\u025a\7V\2"+
"\2\u025a\u025b\7T\2\2\u025b\u025c\7W\2\2\u025c\u025d\7G\2\2\u025d\u0084"+
"\3\2\2\2\u025e\u025f\7V\2\2\u025f\u0260\7[\2\2\u0260\u0261\7R\2\2\u0261"+
"\u0262\7G\2\2\u0262\u0086\3\2\2\2\u0263\u0264\7V\2\2\u0264\u0265\7[\2"+
"\2\u0265\u0266\7R\2\2\u0266\u0267\7G\2\2\u0267\u0268\7U\2\2\u0268\u0088"+
"\3\2\2\2\u0269\u026a\7W\2\2\u026a\u026b\7U\2\2\u026b\u026c\7K\2\2\u026c"+
"\u026d\7P\2\2\u026d\u026e\7I\2\2\u026e\u008a\3\2\2\2\u026f\u0270\7X\2"+
"\2\u0270\u0271\7G\2\2\u0271\u0272\7T\2\2\u0272\u0273\7K\2\2\u0273\u0274"+
"\7H\2\2\u0274\u0275\7[\2\2\u0275\u008c\3\2\2\2\u0276\u0277\7Y\2\2\u0277"+
"\u0278\7J\2\2\u0278\u0279\7G\2\2\u0279\u027a\7T\2\2\u027a\u027b\7G\2\2"+
"\u027b\u008e\3\2\2\2\u027c\u027d\7Y\2\2\u027d\u027e\7K\2\2\u027e\u027f"+
"\7V\2\2\u027f\u0280\7J\2\2\u0280\u0090\3\2\2\2\u0281\u0282\7}\2\2\u0282"+
"\u0283\7G\2\2\u0283\u0284\7U\2\2\u0284\u0285\7E\2\2\u0285\u0286\7C\2\2"+
"\u0286\u0287\7R\2\2\u0287\u0288\7G\2\2\u0288\u0092\3\2\2\2\u0289\u028a"+
"\7}\2\2\u028a\u028b\7H\2\2\u028b\u028c\7P\2\2\u028c\u0094\3\2\2\2\u028d"+
"\u028e\7}\2\2\u028e\u028f\7N\2\2\u028f\u0290\7K\2\2\u0290\u0291\7O\2\2"+
"\u0291\u0292\7K\2\2\u0292\u0293\7V\2\2\u0293\u0096\3\2\2\2\u0294\u0295"+
"\7}\2\2\u0295\u0296\7F\2\2\u0296\u0098\3\2\2\2\u0297\u0298\7}\2\2\u0298"+
"\u0299\7V\2\2\u0299\u009a\3\2\2\2\u029a\u029b\7}\2\2\u029b\u029c\7V\2"+
"\2\u029c\u029d\7U\2\2\u029d\u009c\3\2\2\2\u029e\u029f\7}\2\2\u029f\u02a0"+
"\7I\2\2\u02a0\u02a1\7W\2\2\u02a1\u02a2\7K\2\2\u02a2\u02a3\7F\2\2\u02a3"+
"\u009e\3\2\2\2\u02a4\u02a5\7\177\2\2\u02a5\u00a0\3\2\2\2\u02a6\u02a7\7"+
"?\2\2\u02a7\u00a2\3\2\2\2\u02a8\u02a9\7>\2\2\u02a9\u02b0\7@\2\2\u02aa"+
"\u02ab\7#\2\2\u02ab\u02b0\7?\2\2\u02ac\u02ad\7>\2\2\u02ad\u02ae\7?\2\2"+
"\u02ae\u02b0\7@\2\2\u02af\u02a8\3\2\2\2\u02af\u02aa\3\2\2\2\u02af\u02ac"+
"\3\2\2\2\u02b0\u00a4\3\2\2\2\u02b1\u02b2\7>\2\2\u02b2\u00a6\3\2\2\2\u02b3"+
"\u02b4\7>\2\2\u02b4\u02b5\7?\2\2\u02b5\u00a8\3\2\2\2\u02b6\u02b7\7@\2"+
"\2\u02b7\u00aa\3\2\2\2\u02b8\u02b9\7@\2\2\u02b9\u02ba\7?\2\2\u02ba\u00ac"+
"\3\2\2\2\u02bb\u02bc\7-\2\2\u02bc\u00ae\3\2\2\2\u02bd\u02be\7/\2\2\u02be"+
"\u00b0\3\2\2\2\u02bf\u02c0\7,\2\2\u02c0\u00b2\3\2\2\2\u02c1\u02c2\7\61"+
"\2\2\u02c2\u00b4\3\2\2\2\u02c3\u02c4\7\'\2\2\u02c4\u00b6\3\2\2\2\u02c5"+
"\u02c6\7~\2\2\u02c6\u02c7\7~\2\2\u02c7\u00b8\3\2\2\2\u02c8\u02c9\7\60"+
"\2\2\u02c9\u00ba\3\2\2\2\u02ca\u02cb\7A\2\2\u02cb\u00bc\3\2\2\2\u02cc"+
"\u02d2\7)\2\2\u02cd\u02d1\n\2\2\2\u02ce\u02cf\7)\2\2\u02cf\u02d1\7)\2"+
"\2\u02d0\u02cd\3\2\2\2\u02d0\u02ce\3\2\2\2\u02d1\u02d4\3\2\2\2\u02d2\u02d0"+
"\3\2\2\2\u02d2\u02d3\3\2\2\2\u02d3\u02d5\3\2\2\2\u02d4\u02d2\3\2\2\2\u02d5"+
"\u02d6\7)\2\2\u02d6\u00be\3\2\2\2\u02d7\u02d9\5\u00cfh\2\u02d8\u02d7\3"+
"\2\2\2\u02d9\u02da\3\2\2\2\u02da\u02d8\3\2\2\2\u02da\u02db\3\2\2\2\u02db"+
"\u00c0\3\2\2\2\u02dc\u02de\5\u00cfh\2\u02dd\u02dc\3\2\2\2\u02de\u02df"+
"\3\2\2\2\u02df\u02dd\3\2\2\2\u02df\u02e0\3\2\2\2\u02e0\u02e1\3\2\2\2\u02e1"+
"\u02e5\5\u00b9]\2\u02e2\u02e4\5\u00cfh\2\u02e3\u02e2\3\2\2\2\u02e4\u02e7"+
"\3\2\2\2\u02e5\u02e3\3\2\2\2\u02e5\u02e6\3\2\2\2\u02e6\u0307\3\2\2\2\u02e7"+
"\u02e5\3\2\2\2\u02e8\u02ea\5\u00b9]\2\u02e9\u02eb\5\u00cfh\2\u02ea\u02e9"+
"\3\2\2\2\u02eb\u02ec\3\2\2\2\u02ec\u02ea\3\2\2\2\u02ec\u02ed\3\2\2\2\u02ed"+
"\u0307\3\2\2\2\u02ee\u02f0\5\u00cfh\2\u02ef\u02ee\3\2\2\2\u02f0\u02f1"+
"\3\2\2\2\u02f1\u02ef\3\2\2\2\u02f1\u02f2\3\2\2\2\u02f2\u02fa\3\2\2\2\u02f3"+
"\u02f7\5\u00b9]\2\u02f4\u02f6\5\u00cfh\2\u02f5\u02f4\3\2\2\2\u02f6\u02f9"+
"\3\2\2\2\u02f7\u02f5\3\2\2\2\u02f7\u02f8\3\2\2\2\u02f8\u02fb\3\2\2\2\u02f9"+
"\u02f7\3\2\2\2\u02fa\u02f3\3\2\2\2\u02fa\u02fb\3\2\2\2\u02fb\u02fc\3\2"+
"\2\2\u02fc\u02fd\5\u00cdg\2\u02fd\u0307\3\2\2\2\u02fe\u0300\5\u00b9]\2"+
"\u02ff\u0301\5\u00cfh\2\u0300\u02ff\3\2\2\2\u0301\u0302\3\2\2\2\u0302"+
"\u0300\3\2\2\2\u0302\u0303\3\2\2\2\u0303\u0304\3\2\2\2\u0304\u0305\5\u00cd"+
"g\2\u0305\u0307\3\2\2\2\u0306\u02dd\3\2\2\2\u0306\u02e8\3\2\2\2\u0306"+
"\u02ef\3\2\2\2\u0306\u02fe\3\2\2\2\u0307\u00c2\3\2\2\2\u0308\u030b\5\u00d1"+
"i\2\u0309\u030b\7a\2\2\u030a\u0308\3\2\2\2\u030a\u0309\3\2\2\2\u030b\u0311"+
"\3\2\2\2\u030c\u0310\5\u00d1i\2\u030d\u0310\5\u00cfh\2\u030e\u0310\t\3"+
"\2\2\u030f\u030c\3\2\2\2\u030f\u030d\3\2\2\2\u030f\u030e\3\2\2\2\u0310"+
"\u0313\3\2\2\2\u0311\u030f\3\2\2\2\u0311\u0312\3\2\2\2\u0312\u00c4\3\2"+
"\2\2\u0313\u0311\3\2\2\2\u0314\u0318\5\u00cfh\2\u0315\u0319\5\u00d1i\2"+
"\u0316\u0319\5\u00cfh\2\u0317\u0319\t\4\2\2\u0318\u0315\3\2\2\2\u0318"+
"\u0316\3\2\2\2\u0318\u0317\3\2\2\2\u0319\u031a\3\2\2\2\u031a\u0318\3\2"+
"\2\2\u031a\u031b\3\2\2\2\u031b\u00c6\3\2\2\2\u031c\u0320\5\u00d1i\2\u031d"+
"\u0320\5\u00cfh\2\u031e\u0320\7a\2\2\u031f\u031c\3\2\2\2\u031f\u031d\3"+
"\2\2\2\u031f\u031e\3\2\2\2\u0320\u0321\3\2\2\2\u0321\u031f\3\2\2\2\u0321"+
"\u0322\3\2\2\2\u0322\u00c8\3\2\2\2\u0323\u0329\7$\2\2\u0324\u0328\n\5"+
"\2\2\u0325\u0326\7$\2\2\u0326\u0328\7$\2\2\u0327\u0324\3\2\2\2\u0327\u0325"+
"\3\2\2\2\u0328\u032b\3\2\2\2\u0329\u0327\3\2\2\2\u0329\u032a\3\2\2\2\u032a"+
"\u032c\3\2\2\2\u032b\u0329\3\2\2\2\u032c\u032d\7$\2\2\u032d\u00ca\3\2"+
"\2\2\u032e\u0334\7b\2\2\u032f\u0333\n\6\2\2\u0330\u0331\7b\2\2\u0331\u0333"+
"\7b\2\2\u0332\u032f\3\2\2\2\u0332\u0330\3\2\2\2\u0333\u0336\3\2\2\2\u0334"+
"\u0332\3\2\2\2\u0334\u0335\3\2\2\2\u0335\u0337\3\2\2\2\u0336\u0334\3\2"+
"\2\2\u0337\u0338\7b\2\2\u0338\u00cc\3\2\2\2\u0339\u033b\7G\2\2\u033a\u033c"+
"\t\7\2\2\u033b\u033a\3\2\2\2\u033b\u033c\3\2\2\2\u033c\u033e\3\2\2\2\u033d"+
"\u033f\5\u00cfh\2\u033e\u033d\3\2\2\2\u033f\u0340\3\2\2\2\u0340\u033e"+
"\3\2\2\2\u0340\u0341\3\2\2\2\u0341\u00ce\3\2\2\2\u0342\u0343\t\b\2\2\u0343"+
"\u00d0\3\2\2\2\u0344\u0345\t\t\2\2\u0345\u00d2\3\2\2\2\u0346\u0347\7/"+
"\2\2\u0347\u0348\7/\2\2\u0348\u034c\3\2\2\2\u0349\u034b\n\n\2\2\u034a"+
"\u0349\3\2\2\2\u034b\u034e\3\2\2\2\u034c\u034a\3\2\2\2\u034c\u034d\3\2"+
"\2\2\u034d\u0350\3\2\2\2\u034e\u034c\3\2\2\2\u034f\u0351\7\17\2\2\u0350"+
"\u034f\3\2\2\2\u0350\u0351\3\2\2\2\u0351\u0353\3\2\2\2\u0352\u0354\7\f"+
"\2\2\u0353\u0352\3\2\2\2\u0353\u0354\3\2\2\2\u0354\u0355\3\2\2\2\u0355"+
"\u0356\bj\2\2\u0356\u00d4\3\2\2\2\u0357\u0358\7\61\2\2\u0358\u0359\7,"+
"\2\2\u0359\u035e\3\2\2\2\u035a\u035d\5\u00d5k\2\u035b\u035d\13\2\2\2\u035c"+
"\u035a\3\2\2\2\u035c\u035b\3\2\2\2\u035d\u0360\3\2\2\2\u035e\u035f\3\2"+
"\2\2\u035e\u035c\3\2\2\2\u035f\u0361\3\2\2\2\u0360\u035e\3\2\2\2\u0361"+
"\u0362\7,\2\2\u0362\u0363\7\61\2\2\u0363\u0364\3\2\2\2\u0364\u0365\bk"+
"\2\2\u0365\u00d6\3\2\2\2\u0366\u0368\t\13\2\2\u0367\u0366\3\2\2\2\u0368"+
"\u0369\3\2\2\2\u0369\u0367\3\2\2\2\u0369\u036a\3\2\2\2\u036a\u036b\3\2"+
"\2\2\u036b\u036c\bl\2\2\u036c\u00d8\3\2\2\2\u036d\u036e\13\2\2\2\u036e"+
"\u00da\3\2\2\2\"\2\u02af\u02d0\u02d2\u02da\u02df\u02e5\u02ec\u02f1\u02f7"+
"\u02fa\u0302\u0306\u030a\u030f\u0311\u0318\u031a\u031f\u0321\u0327\u0329"+
"\u0332\u0334\u033b\u0340\u034c\u0350\u0353\u035c\u035e\u0369\3\2\3\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\u00d9\3\2\2\2\2\u00db\3\2\2"+
"\2\2\u00dd\3\2\2\2\2\u00df\3\2\2\2\3\u00e1\3\2\2\2\5\u00e3\3\2\2\2\7\u00e5"+
"\3\2\2\2\t\u00e7\3\2\2\2\13\u00e9\3\2\2\2\r\u00ed\3\2\2\2\17\u00f5\3\2"+
"\2\2\21\u00fe\3\2\2\2\23\u0102\3\2\2\2\25\u0106\3\2\2\2\27\u0109\3\2\2"+
"\2\31\u010d\3\2\2\2\33\u0115\3\2\2\2\35\u0118\3\2\2\2\37\u011d\3\2\2\2"+
"!\u0125\3\2\2\2#\u012e\3\2\2\2%\u0136\3\2\2\2\'\u013c\3\2\2\2)\u0141\3"+
"\2\2\2+\u014a\3\2\2\2-\u0153\3\2\2\2/\u015a\3\2\2\2\61\u0165\3\2\2\2\63"+
"\u016c\3\2\2\2\65\u0174\3\2\2\2\67\u017c\3\2\2\29\u0182\3\2\2\2;\u0188"+
"\3\2\2\2=\u018f\3\2\2\2?\u0194\3\2\2\2A\u0199\3\2\2\2C\u01a3\3\2\2\2E"+
"\u01ac\3\2\2\2G\u01b2\3\2\2\2I\u01b9\3\2\2\2K\u01bc\3\2\2\2M\u01c2\3\2"+
"\2\2O\u01c5\3\2\2\2Q\u01ca\3\2\2\2S\u01cf\3\2\2\2U\u01d4\3\2\2\2W\u01d9"+
"\3\2\2\2Y\u01df\3\2\2\2[\u01e6\3\2\2\2]\u01ec\3\2\2\2_\u01f4\3\2\2\2a"+
"\u01f8\3\2\2\2c\u01fd\3\2\2\2e\u0203\3\2\2\2g\u0206\3\2\2\2i\u0210\3\2"+
"\2\2k\u0213\3\2\2\2m\u0219\3\2\2\2o\u021f\3\2\2\2q\u0226\3\2\2\2s\u022f"+
"\3\2\2\2u\u0234\3\2\2\2w\u023a\3\2\2\2y\u0240\3\2\2\2{\u0246\3\2\2\2}"+
"\u024e\3\2\2\2\177\u0255\3\2\2\2\u0081\u025a\3\2\2\2\u0083\u025e\3\2\2"+
"\2\u0085\u0264\3\2\2\2\u0087\u026b\3\2\2\2\u0089\u0270\3\2\2\2\u008b\u0275"+
"\3\2\2\2\u008d\u027a\3\2\2\2\u008f\u0280\3\2\2\2\u0091\u0286\3\2\2\2\u0093"+
"\u028d\3\2\2\2\u0095\u0293\3\2\2\2\u0097\u0298\3\2\2\2\u0099\u02a0\3\2"+
"\2\2\u009b\u02a4\3\2\2\2\u009d\u02ab\3\2\2\2\u009f\u02ae\3\2\2\2\u00a1"+
"\u02b1\3\2\2\2\u00a3\u02b5\3\2\2\2\u00a5\u02bb\3\2\2\2\u00a7\u02bd\3\2"+
"\2\2\u00a9\u02c6\3\2\2\2\u00ab\u02c8\3\2\2\2\u00ad\u02ca\3\2\2\2\u00af"+
"\u02cd\3\2\2\2\u00b1\u02cf\3\2\2\2\u00b3\u02d2\3\2\2\2\u00b5\u02d4\3\2"+
"\2\2\u00b7\u02d6\3\2\2\2\u00b9\u02d8\3\2\2\2\u00bb\u02da\3\2\2\2\u00bd"+
"\u02dc\3\2\2\2\u00bf\u02df\3\2\2\2\u00c1\u02e1\3\2\2\2\u00c3\u02e3\3\2"+
"\2\2\u00c5\u02ef\3\2\2\2\u00c7\u031d\3\2\2\2\u00c9\u0321\3\2\2\2\u00cb"+
"\u032b\3\2\2\2\u00cd\u0336\3\2\2\2\u00cf\u033a\3\2\2\2\u00d1\u0345\3\2"+
"\2\2\u00d3\u0350\3\2\2\2\u00d5\u0359\3\2\2\2\u00d7\u035b\3\2\2\2\u00d9"+
"\u035d\3\2\2\2\u00db\u036e\3\2\2\2\u00dd\u037e\3\2\2\2\u00df\u0384\3\2"+
"\2\2\u00e1\u00e2\7*\2\2\u00e2\4\3\2\2\2\u00e3\u00e4\7+\2\2\u00e4\6\3\2"+
"\2\2\u00e5\u00e6\7.\2\2\u00e6\b\3\2\2\2\u00e7\u00e8\7<\2\2\u00e8\n\3\2"+
"\2\2\u00e9\u00ea\7C\2\2\u00ea\u00eb\7N\2\2\u00eb\u00ec\7N\2\2\u00ec\f"+
"\3\2\2\2\u00ed\u00ee\7C\2\2\u00ee\u00ef\7P\2\2\u00ef\u00f0\7C\2\2\u00f0"+
"\u00f1\7N\2\2\u00f1\u00f2\7[\2\2\u00f2\u00f3\7\\\2\2\u00f3\u00f4\7G\2"+
"\2\u00f4\16\3\2\2\2\u00f5\u00f6\7C\2\2\u00f6\u00f7\7P\2\2\u00f7\u00f8"+
"\7C\2\2\u00f8\u00f9\7N\2\2\u00f9\u00fa\7[\2\2\u00fa\u00fb\7\\\2\2\u00fb"+
"\u00fc\7G\2\2\u00fc\u00fd\7F\2\2\u00fd\20\3\2\2\2\u00fe\u00ff\7C\2\2\u00ff"+
"\u0100\7P\2\2\u0100\u0101\7F\2\2\u0101\22\3\2\2\2\u0102\u0103\7C\2\2\u0103"+
"\u0104\7P\2\2\u0104\u0105\7[\2\2\u0105\24\3\2\2\2\u0106\u0107\7C\2\2\u0107"+
"\u0108\7U\2\2\u0108\26\3\2\2\2\u0109\u010a\7C\2\2\u010a\u010b\7U\2\2\u010b"+
"\u010c\7E\2\2\u010c\30\3\2\2\2\u010d\u010e\7D\2\2\u010e\u010f\7G\2\2\u010f"+
"\u0110\7V\2\2\u0110\u0111\7Y\2\2\u0111\u0112\7G\2\2\u0112\u0113\7G\2\2"+
"\u0113\u0114\7P\2\2\u0114\32\3\2\2\2\u0115\u0116\7D\2\2\u0116\u0117\7"+
"[\2\2\u0117\34\3\2\2\2\u0118\u0119\7E\2\2\u0119\u011a\7C\2\2\u011a\u011b"+
"\7U\2\2\u011b\u011c\7V\2\2\u011c\36\3\2\2\2\u011d\u011e\7E\2\2\u011e\u011f"+
"\7C\2\2\u011f\u0120\7V\2\2\u0120\u0121\7C\2\2\u0121\u0122\7N\2\2\u0122"+
"\u0123\7Q\2\2\u0123\u0124\7I\2\2\u0124 \3\2\2\2\u0125\u0126\7E\2\2\u0126"+
"\u0127\7C\2\2\u0127\u0128\7V\2\2\u0128\u0129\7C\2\2\u0129\u012a\7N\2\2"+
"\u012a\u012b\7Q\2\2\u012b\u012c\7I\2\2\u012c\u012d\7U\2\2\u012d\"\3\2"+
"\2\2\u012e\u012f\7E\2\2\u012f\u0130\7Q\2\2\u0130\u0131\7N\2\2\u0131\u0132"+
"\7W\2\2\u0132\u0133\7O\2\2\u0133\u0134\7P\2\2\u0134\u0135\7U\2\2\u0135"+
"$\3\2\2\2\u0136\u0137\7F\2\2\u0137\u0138\7G\2\2\u0138\u0139\7D\2\2\u0139"+
"\u013a\7W\2\2\u013a\u013b\7I\2\2\u013b&\3\2\2\2\u013c\u013d\7F\2\2\u013d"+
"\u013e\7G\2\2\u013e\u013f\7U\2\2\u013f\u0140\7E\2\2\u0140(\3\2\2\2\u0141"+
"\u0142\7F\2\2\u0142\u0143\7G\2\2\u0143\u0144\7U\2\2\u0144\u0145\7E\2\2"+
"\u0145\u0146\7T\2\2\u0146\u0147\7K\2\2\u0147\u0148\7D\2\2\u0148\u0149"+
"\7G\2\2\u0149*\3\2\2\2\u014a\u014b\7F\2\2\u014b\u014c\7K\2\2\u014c\u014d"+
"\7U\2\2\u014d\u014e\7V\2\2\u014e\u014f\7K\2\2\u014f\u0150\7P\2\2\u0150"+
"\u0151\7E\2\2\u0151\u0152\7V\2\2\u0152,\3\2\2\2\u0153\u0154\7G\2\2\u0154"+
"\u0155\7U\2\2\u0155\u0156\7E\2\2\u0156\u0157\7C\2\2\u0157\u0158\7R\2\2"+
"\u0158\u0159\7G\2\2\u0159.\3\2\2\2\u015a\u015b\7G\2\2\u015b\u015c\7Z\2"+
"\2\u015c\u015d\7G\2\2\u015d\u015e\7E\2\2\u015e\u015f\7W\2\2\u015f\u0160"+
"\7V\2\2\u0160\u0161\7C\2\2\u0161\u0162\7D\2\2\u0162\u0163\7N\2\2\u0163"+
"\u0164\7G\2\2\u0164\60\3\2\2\2\u0165\u0166\7G\2\2\u0166\u0167\7Z\2\2\u0167"+
"\u0168\7K\2\2\u0168\u0169\7U\2\2\u0169\u016a\7V\2\2\u016a\u016b\7U\2\2"+
"\u016b\62\3\2\2\2\u016c\u016d\7G\2\2\u016d\u016e\7Z\2\2\u016e\u016f\7"+
"R\2\2\u016f\u0170\7N\2\2\u0170\u0171\7C\2\2\u0171\u0172\7K\2\2\u0172\u0173"+
"\7P\2\2\u0173\64\3\2\2\2\u0174\u0175\7G\2\2\u0175\u0176\7Z\2\2\u0176\u0177"+
"\7V\2\2\u0177\u0178\7T\2\2\u0178\u0179\7C\2\2\u0179\u017a\7E\2\2\u017a"+
"\u017b\7V\2\2\u017b\66\3\2\2\2\u017c\u017d\7H\2\2\u017d\u017e\7C\2\2\u017e"+
"\u017f\7N\2\2\u017f\u0180\7U\2\2\u0180\u0181\7G\2\2\u01818\3\2\2\2\u0182"+
"\u0183\7H\2\2\u0183\u0184\7K\2\2\u0184\u0185\7T\2\2\u0185\u0186\7U\2\2"+
"\u0186\u0187\7V\2\2\u0187:\3\2\2\2\u0188\u0189\7H\2\2\u0189\u018a\7Q\2"+
"\2\u018a\u018b\7T\2\2\u018b\u018c\7O\2\2\u018c\u018d\7C\2\2\u018d\u018e"+
"\7V\2\2\u018e<\3\2\2\2\u018f\u0190\7H\2\2\u0190\u0191\7T\2\2\u0191\u0192"+
"\7Q\2\2\u0192\u0193\7O\2\2\u0193>\3\2\2\2\u0194\u0195\7H\2\2\u0195\u0196"+
"\7W\2\2\u0196\u0197\7N\2\2\u0197\u0198\7N\2\2\u0198@\3\2\2\2\u0199\u019a"+
"\7H\2\2\u019a\u019b\7W\2\2\u019b\u019c\7P\2\2\u019c\u019d\7E\2\2\u019d"+
"\u019e\7V\2\2\u019e\u019f\7K\2\2\u019f\u01a0\7Q\2\2\u01a0\u01a1\7P\2\2"+
"\u01a1\u01a2\7U\2\2\u01a2B\3\2\2\2\u01a3\u01a4\7I\2\2\u01a4\u01a5\7T\2"+
"\2\u01a5\u01a6\7C\2\2\u01a6\u01a7\7R\2\2\u01a7\u01a8\7J\2\2\u01a8\u01a9"+
"\7X\2\2\u01a9\u01aa\7K\2\2\u01aa\u01ab\7\\\2\2\u01abD\3\2\2\2\u01ac\u01ad"+
"\7I\2\2\u01ad\u01ae\7T\2\2\u01ae\u01af\7Q\2\2\u01af\u01b0\7W\2\2\u01b0"+
"\u01b1\7R\2\2\u01b1F\3\2\2\2\u01b2\u01b3\7J\2\2\u01b3\u01b4\7C\2\2\u01b4"+
"\u01b5\7X\2\2\u01b5\u01b6\7K\2\2\u01b6\u01b7\7P\2\2\u01b7\u01b8\7I\2\2"+
"\u01b8H\3\2\2\2\u01b9\u01ba\7K\2\2\u01ba\u01bb\7P\2\2\u01bbJ\3\2\2\2\u01bc"+
"\u01bd\7K\2\2\u01bd\u01be\7P\2\2\u01be\u01bf\7P\2\2\u01bf\u01c0\7G\2\2"+
"\u01c0\u01c1\7T\2\2\u01c1L\3\2\2\2\u01c2\u01c3\7K\2\2\u01c3\u01c4\7U\2"+
"\2\u01c4N\3\2\2\2\u01c5\u01c6\7L\2\2\u01c6\u01c7\7Q\2\2\u01c7\u01c8\7"+
"K\2\2\u01c8\u01c9\7P\2\2\u01c9P\3\2\2\2\u01ca\u01cb\7N\2\2\u01cb\u01cc"+
"\7C\2\2\u01cc\u01cd\7U\2\2\u01cd\u01ce\7V\2\2\u01ceR\3\2\2\2\u01cf\u01d0"+
"\7N\2\2\u01d0\u01d1\7G\2\2\u01d1\u01d2\7H\2\2\u01d2\u01d3\7V\2\2\u01d3"+
"T\3\2\2\2\u01d4\u01d5\7N\2\2\u01d5\u01d6\7K\2\2\u01d6\u01d7\7M\2\2\u01d7"+
"\u01d8\7G\2\2\u01d8V\3\2\2\2\u01d9\u01da\7N\2\2\u01da\u01db\7K\2\2\u01db"+
"\u01dc\7O\2\2\u01dc\u01dd\7K\2\2\u01dd\u01de\7V\2\2\u01deX\3\2\2\2\u01df"+
"\u01e0\7O\2\2\u01e0\u01e1\7C\2\2\u01e1\u01e2\7R\2\2\u01e2\u01e3\7R\2\2"+
"\u01e3\u01e4\7G\2\2\u01e4\u01e5\7F\2\2\u01e5Z\3\2\2\2\u01e6\u01e7\7O\2"+
"\2\u01e7\u01e8\7C\2\2\u01e8\u01e9\7V\2\2\u01e9\u01ea\7E\2\2\u01ea\u01eb"+
"\7J\2\2\u01eb\\\3\2\2\2\u01ec\u01ed\7P\2\2\u01ed\u01ee\7C\2\2\u01ee\u01ef"+
"\7V\2\2\u01ef\u01f0\7W\2\2\u01f0\u01f1\7T\2\2\u01f1\u01f2\7C\2\2\u01f2"+
"\u01f3\7N\2\2\u01f3^\3\2\2\2\u01f4\u01f5\7P\2\2\u01f5\u01f6\7Q\2\2\u01f6"+
"\u01f7\7V\2\2\u01f7`\3\2\2\2\u01f8\u01f9\7P\2\2\u01f9\u01fa\7W\2\2\u01fa"+
"\u01fb\7N\2\2\u01fb\u01fc\7N\2\2\u01fcb\3\2\2\2\u01fd\u01fe\7P\2\2\u01fe"+
"\u01ff\7W\2\2\u01ff\u0200\7N\2\2\u0200\u0201\7N\2\2\u0201\u0202\7U\2\2"+
"\u0202d\3\2\2\2\u0203\u0204\7Q\2\2\u0204\u0205\7P\2\2\u0205f\3\2\2\2\u0206"+
"\u0207\7Q\2\2\u0207\u0208\7R\2\2\u0208\u0209\7V\2\2\u0209\u020a\7K\2\2"+
"\u020a\u020b\7O\2\2\u020b\u020c\7K\2\2\u020c\u020d\7\\\2\2\u020d\u020e"+
"\7G\2\2\u020e\u020f\7F\2\2\u020fh\3\2\2\2\u0210\u0211\7Q\2\2\u0211\u0212"+
"\7T\2\2\u0212j\3\2\2\2\u0213\u0214\7Q\2\2\u0214\u0215\7T\2\2\u0215\u0216"+
"\7F\2\2\u0216\u0217\7G\2\2\u0217\u0218\7T\2\2\u0218l\3\2\2\2\u0219\u021a"+
"\7Q\2\2\u021a\u021b\7W\2\2\u021b\u021c\7V\2\2\u021c\u021d\7G\2\2\u021d"+
"\u021e\7T\2\2\u021en\3\2\2\2\u021f\u0220\7R\2\2\u0220\u0221\7C\2\2\u0221"+
"\u0222\7T\2\2\u0222\u0223\7U\2\2\u0223\u0224\7G\2\2\u0224\u0225\7F\2\2"+
"\u0225p\3\2\2\2\u0226\u0227\7R\2\2\u0227\u0228\7J\2\2\u0228\u0229\7[\2"+
"\2\u0229\u022a\7U\2\2\u022a\u022b\7K\2\2\u022b\u022c\7E\2\2\u022c\u022d"+
"\7C\2\2\u022d\u022e\7N\2\2\u022er\3\2\2\2\u022f\u0230\7R\2\2\u0230\u0231"+
"\7N\2\2\u0231\u0232\7C\2\2\u0232\u0233\7P\2\2\u0233t\3\2\2\2\u0234\u0235"+
"\7T\2\2\u0235\u0236\7K\2\2\u0236\u0237\7I\2\2\u0237\u0238\7J\2\2\u0238"+
"\u0239\7V\2\2\u0239v\3\2\2\2\u023a\u023b\7T\2\2\u023b\u023c\7N\2\2\u023c"+
"\u023d\7K\2\2\u023d\u023e\7M\2\2\u023e\u023f\7G\2\2\u023fx\3\2\2\2\u0240"+
"\u0241\7S\2\2\u0241\u0242\7W\2\2\u0242\u0243\7G\2\2\u0243\u0244\7T\2\2"+
"\u0244\u0245\7[\2\2\u0245z\3\2\2\2\u0246\u0247\7U\2\2\u0247\u0248\7E\2"+
"\2\u0248\u0249\7J\2\2\u0249\u024a\7G\2\2\u024a\u024b\7O\2\2\u024b\u024c"+
"\7C\2\2\u024c\u024d\7U\2\2\u024d|\3\2\2\2\u024e\u024f\7U\2\2\u024f\u0250"+
"\7G\2\2\u0250\u0251\7N\2\2\u0251\u0252\7G\2\2\u0252\u0253\7E\2\2\u0253"+
"\u0254\7V\2\2\u0254~\3\2\2\2\u0255\u0256\7U\2\2\u0256\u0257\7J\2\2\u0257"+
"\u0258\7Q\2\2\u0258\u0259\7Y\2\2\u0259\u0080\3\2\2\2\u025a\u025b\7U\2"+
"\2\u025b\u025c\7[\2\2\u025c\u025d\7U\2\2\u025d\u0082\3\2\2\2\u025e\u025f"+
"\7V\2\2\u025f\u0260\7C\2\2\u0260\u0261\7D\2\2\u0261\u0262\7N\2\2\u0262"+
"\u0263\7G\2\2\u0263\u0084\3\2\2\2\u0264\u0265\7V\2\2\u0265\u0266\7C\2"+
"\2\u0266\u0267\7D\2\2\u0267\u0268\7N\2\2\u0268\u0269\7G\2\2\u0269\u026a"+
"\7U\2\2\u026a\u0086\3\2\2\2\u026b\u026c\7V\2\2\u026c\u026d\7G\2\2\u026d"+
"\u026e\7Z\2\2\u026e\u026f\7V\2\2\u026f\u0088\3\2\2\2\u0270\u0271\7V\2"+
"\2\u0271\u0272\7T\2\2\u0272\u0273\7W\2\2\u0273\u0274\7G\2\2\u0274\u008a"+
"\3\2\2\2\u0275\u0276\7V\2\2\u0276\u0277\7[\2\2\u0277\u0278\7R\2\2\u0278"+
"\u0279\7G\2\2\u0279\u008c\3\2\2\2\u027a\u027b\7V\2\2\u027b\u027c\7[\2"+
"\2\u027c\u027d\7R\2\2\u027d\u027e\7G\2\2\u027e\u027f\7U\2\2\u027f\u008e"+
"\3\2\2\2\u0280\u0281\7W\2\2\u0281\u0282\7U\2\2\u0282\u0283\7K\2\2\u0283"+
"\u0284\7P\2\2\u0284\u0285\7I\2\2\u0285\u0090\3\2\2\2\u0286\u0287\7X\2"+
"\2\u0287\u0288\7G\2\2\u0288\u0289\7T\2\2\u0289\u028a\7K\2\2\u028a\u028b"+
"\7H\2\2\u028b\u028c\7[\2\2\u028c\u0092\3\2\2\2\u028d\u028e\7Y\2\2\u028e"+
"\u028f\7J\2\2\u028f\u0290\7G\2\2\u0290\u0291\7T\2\2\u0291\u0292\7G\2\2"+
"\u0292\u0094\3\2\2\2\u0293\u0294\7Y\2\2\u0294\u0295\7K\2\2\u0295\u0296"+
"\7V\2\2\u0296\u0297\7J\2\2\u0297\u0096\3\2\2\2\u0298\u0299\7}\2\2\u0299"+
"\u029a\7G\2\2\u029a\u029b\7U\2\2\u029b\u029c\7E\2\2\u029c\u029d\7C\2\2"+
"\u029d\u029e\7R\2\2\u029e\u029f\7G\2\2\u029f\u0098\3\2\2\2\u02a0\u02a1"+
"\7}\2\2\u02a1\u02a2\7H\2\2\u02a2\u02a3\7P\2\2\u02a3\u009a\3\2\2\2\u02a4"+
"\u02a5\7}\2\2\u02a5\u02a6\7N\2\2\u02a6\u02a7\7K\2\2\u02a7\u02a8\7O\2\2"+
"\u02a8\u02a9\7K\2\2\u02a9\u02aa\7V\2\2\u02aa\u009c\3\2\2\2\u02ab\u02ac"+
"\7}\2\2\u02ac\u02ad\7F\2\2\u02ad\u009e\3\2\2\2\u02ae\u02af\7}\2\2\u02af"+
"\u02b0\7V\2\2\u02b0\u00a0\3\2\2\2\u02b1\u02b2\7}\2\2\u02b2\u02b3\7V\2"+
"\2\u02b3\u02b4\7U\2\2\u02b4\u00a2\3\2\2\2\u02b5\u02b6\7}\2\2\u02b6\u02b7"+
"\7I\2\2\u02b7\u02b8\7W\2\2\u02b8\u02b9\7K\2\2\u02b9\u02ba\7F\2\2\u02ba"+
"\u00a4\3\2\2\2\u02bb\u02bc\7\177\2\2\u02bc\u00a6\3\2\2\2\u02bd\u02be\7"+
"?\2\2\u02be\u00a8\3\2\2\2\u02bf\u02c0\7>\2\2\u02c0\u02c7\7@\2\2\u02c1"+
"\u02c2\7#\2\2\u02c2\u02c7\7?\2\2\u02c3\u02c4\7>\2\2\u02c4\u02c5\7?\2\2"+
"\u02c5\u02c7\7@\2\2\u02c6\u02bf\3\2\2\2\u02c6\u02c1\3\2\2\2\u02c6\u02c3"+
"\3\2\2\2\u02c7\u00aa\3\2\2\2\u02c8\u02c9\7>\2\2\u02c9\u00ac\3\2\2\2\u02ca"+
"\u02cb\7>\2\2\u02cb\u02cc\7?\2\2\u02cc\u00ae\3\2\2\2\u02cd\u02ce\7@\2"+
"\2\u02ce\u00b0\3\2\2\2\u02cf\u02d0\7@\2\2\u02d0\u02d1\7?\2\2\u02d1\u00b2"+
"\3\2\2\2\u02d2\u02d3\7-\2\2\u02d3\u00b4\3\2\2\2\u02d4\u02d5\7/\2\2\u02d5"+
"\u00b6\3\2\2\2\u02d6\u02d7\7,\2\2\u02d7\u00b8\3\2\2\2\u02d8\u02d9\7\61"+
"\2\2\u02d9\u00ba\3\2\2\2\u02da\u02db\7\'\2\2\u02db\u00bc\3\2\2\2\u02dc"+
"\u02dd\7~\2\2\u02dd\u02de\7~\2\2\u02de\u00be\3\2\2\2\u02df\u02e0\7\60"+
"\2\2\u02e0\u00c0\3\2\2\2\u02e1\u02e2\7A\2\2\u02e2\u00c2\3\2\2\2\u02e3"+
"\u02e9\7)\2\2\u02e4\u02e8\n\2\2\2\u02e5\u02e6\7)\2\2\u02e6\u02e8\7)\2"+
"\2\u02e7\u02e4\3\2\2\2\u02e7\u02e5\3\2\2\2\u02e8\u02eb\3\2\2\2\u02e9\u02e7"+
"\3\2\2\2\u02e9\u02ea\3\2\2\2\u02ea\u02ec\3\2\2\2\u02eb\u02e9\3\2\2\2\u02ec"+
"\u02ed\7)\2\2\u02ed\u00c4\3\2\2\2\u02ee\u02f0\5\u00d5k\2\u02ef\u02ee\3"+
"\2\2\2\u02f0\u02f1\3\2\2\2\u02f1\u02ef\3\2\2\2\u02f1\u02f2\3\2\2\2\u02f2"+
"\u00c6\3\2\2\2\u02f3\u02f5\5\u00d5k\2\u02f4\u02f3\3\2\2\2\u02f5\u02f6"+
"\3\2\2\2\u02f6\u02f4\3\2\2\2\u02f6\u02f7\3\2\2\2\u02f7\u02f8\3\2\2\2\u02f8"+
"\u02fc\5\u00bf`\2\u02f9\u02fb\5\u00d5k\2\u02fa\u02f9\3\2\2\2\u02fb\u02fe"+
"\3\2\2\2\u02fc\u02fa\3\2\2\2\u02fc\u02fd\3\2\2\2\u02fd\u031e\3\2\2\2\u02fe"+
"\u02fc\3\2\2\2\u02ff\u0301\5\u00bf`\2\u0300\u0302\5\u00d5k\2\u0301\u0300"+
"\3\2\2\2\u0302\u0303\3\2\2\2\u0303\u0301\3\2\2\2\u0303\u0304\3\2\2\2\u0304"+
"\u031e\3\2\2\2\u0305\u0307\5\u00d5k\2\u0306\u0305\3\2\2\2\u0307\u0308"+
"\3\2\2\2\u0308\u0306\3\2\2\2\u0308\u0309\3\2\2\2\u0309\u0311\3\2\2\2\u030a"+
"\u030e\5\u00bf`\2\u030b\u030d\5\u00d5k\2\u030c\u030b\3\2\2\2\u030d\u0310"+
"\3\2\2\2\u030e\u030c\3\2\2\2\u030e\u030f\3\2\2\2\u030f\u0312\3\2\2\2\u0310"+
"\u030e\3\2\2\2\u0311\u030a\3\2\2\2\u0311\u0312\3\2\2\2\u0312\u0313\3\2"+
"\2\2\u0313\u0314\5\u00d3j\2\u0314\u031e\3\2\2\2\u0315\u0317\5\u00bf`\2"+
"\u0316\u0318\5\u00d5k\2\u0317\u0316\3\2\2\2\u0318\u0319\3\2\2\2\u0319"+
"\u0317\3\2\2\2\u0319\u031a\3\2\2\2\u031a\u031b\3\2\2\2\u031b\u031c\5\u00d3"+
"j\2\u031c\u031e\3\2\2\2\u031d\u02f4\3\2\2\2\u031d\u02ff\3\2\2\2\u031d"+
"\u0306\3\2\2\2\u031d\u0315\3\2\2\2\u031e\u00c8\3\2\2\2\u031f\u0322\5\u00d7"+
"l\2\u0320\u0322\7a\2\2\u0321\u031f\3\2\2\2\u0321\u0320\3\2\2\2\u0322\u0328"+
"\3\2\2\2\u0323\u0327\5\u00d7l\2\u0324\u0327\5\u00d5k\2\u0325\u0327\t\3"+
"\2\2\u0326\u0323\3\2\2\2\u0326\u0324\3\2\2\2\u0326\u0325\3\2\2\2\u0327"+
"\u032a\3\2\2\2\u0328\u0326\3\2\2\2\u0328\u0329\3\2\2\2\u0329\u00ca\3\2"+
"\2\2\u032a\u0328\3\2\2\2\u032b\u032f\5\u00d5k\2\u032c\u0330\5\u00d7l\2"+
"\u032d\u0330\5\u00d5k\2\u032e\u0330\t\4\2\2\u032f\u032c\3\2\2\2\u032f"+
"\u032d\3\2\2\2\u032f\u032e\3\2\2\2\u0330\u0331\3\2\2\2\u0331\u032f\3\2"+
"\2\2\u0331\u0332\3\2\2\2\u0332\u00cc\3\2\2\2\u0333\u0337\5\u00d7l\2\u0334"+
"\u0337\5\u00d5k\2\u0335\u0337\7a\2\2\u0336\u0333\3\2\2\2\u0336\u0334\3"+
"\2\2\2\u0336\u0335\3\2\2\2\u0337\u0338\3\2\2\2\u0338\u0336\3\2\2\2\u0338"+
"\u0339\3\2\2\2\u0339\u00ce\3\2\2\2\u033a\u0340\7$\2\2\u033b\u033f\n\5"+
"\2\2\u033c\u033d\7$\2\2\u033d\u033f\7$\2\2\u033e\u033b\3\2\2\2\u033e\u033c"+
"\3\2\2\2\u033f\u0342\3\2\2\2\u0340\u033e\3\2\2\2\u0340\u0341\3\2\2\2\u0341"+
"\u0343\3\2\2\2\u0342\u0340\3\2\2\2\u0343\u0344\7$\2\2\u0344\u00d0\3\2"+
"\2\2\u0345\u034b\7b\2\2\u0346\u034a\n\6\2\2\u0347\u0348\7b\2\2\u0348\u034a"+
"\7b\2\2\u0349\u0346\3\2\2\2\u0349\u0347\3\2\2\2\u034a\u034d\3\2\2\2\u034b"+
"\u0349\3\2\2\2\u034b\u034c\3\2\2\2\u034c\u034e\3\2\2\2\u034d\u034b\3\2"+
"\2\2\u034e\u034f\7b\2\2\u034f\u00d2\3\2\2\2\u0350\u0352\7G\2\2\u0351\u0353"+
"\t\7\2\2\u0352\u0351\3\2\2\2\u0352\u0353\3\2\2\2\u0353\u0355\3\2\2\2\u0354"+
"\u0356\5\u00d5k\2\u0355\u0354\3\2\2\2\u0356\u0357\3\2\2\2\u0357\u0355"+
"\3\2\2\2\u0357\u0358\3\2\2\2\u0358\u00d4\3\2\2\2\u0359\u035a\t\b\2\2\u035a"+
"\u00d6\3\2\2\2\u035b\u035c\t\t\2\2\u035c\u00d8\3\2\2\2\u035d\u035e\7/"+
"\2\2\u035e\u035f\7/\2\2\u035f\u0363\3\2\2\2\u0360\u0362\n\n\2\2\u0361"+
"\u0360\3\2\2\2\u0362\u0365\3\2\2\2\u0363\u0361\3\2\2\2\u0363\u0364\3\2"+
"\2\2\u0364\u0367\3\2\2\2\u0365\u0363\3\2\2\2\u0366\u0368\7\17\2\2\u0367"+
"\u0366\3\2\2\2\u0367\u0368\3\2\2\2\u0368\u036a\3\2\2\2\u0369\u036b\7\f"+
"\2\2\u036a\u0369\3\2\2\2\u036a\u036b\3\2\2\2\u036b\u036c\3\2\2\2\u036c"+
"\u036d\bm\2\2\u036d\u00da\3\2\2\2\u036e\u036f\7\61\2\2\u036f\u0370\7,"+
"\2\2\u0370\u0375\3\2\2\2\u0371\u0374\5\u00dbn\2\u0372\u0374\13\2\2\2\u0373"+
"\u0371\3\2\2\2\u0373\u0372\3\2\2\2\u0374\u0377\3\2\2\2\u0375\u0376\3\2"+
"\2\2\u0375\u0373\3\2\2\2\u0376\u0378\3\2\2\2\u0377\u0375\3\2\2\2\u0378"+
"\u0379\7,\2\2\u0379\u037a\7\61\2\2\u037a\u037b\3\2\2\2\u037b\u037c\bn"+
"\2\2\u037c\u00dc\3\2\2\2\u037d\u037f\t\13\2\2\u037e\u037d\3\2\2\2\u037f"+
"\u0380\3\2\2\2\u0380\u037e\3\2\2\2\u0380\u0381\3\2\2\2\u0381\u0382\3\2"+
"\2\2\u0382\u0383\bo\2\2\u0383\u00de\3\2\2\2\u0384\u0385\13\2\2\2\u0385"+
"\u00e0\3\2\2\2\"\2\u02c6\u02e7\u02e9\u02f1\u02f6\u02fc\u0303\u0308\u030e"+
"\u0311\u0319\u031d\u0321\u0326\u0328\u032f\u0331\u0336\u0338\u033e\u0340"+
"\u0349\u034b\u0352\u0357\u0363\u0367\u036a\u0373\u0375\u0380\3\2\3\2";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {

View File

@ -53,6 +53,7 @@ import org.elasticsearch.xpack.sql.querydsl.container.QueryContainer;
import org.elasticsearch.xpack.sql.querydsl.container.ScoreSort;
import org.elasticsearch.xpack.sql.querydsl.container.ScriptSort;
import org.elasticsearch.xpack.sql.querydsl.container.Sort.Direction;
import org.elasticsearch.xpack.sql.querydsl.container.Sort.Missing;
import org.elasticsearch.xpack.sql.querydsl.query.Query;
import org.elasticsearch.xpack.sql.rule.Rule;
import org.elasticsearch.xpack.sql.rule.RuleExecutor;
@ -423,6 +424,7 @@ class QueryFolder extends RuleExecutor<PhysicalPlan> {
for (Order order : plan.order()) {
Direction direction = Direction.from(order.direction());
Missing missing = Missing.from(order.nullsPosition());
// check whether sorting is on an group (and thus nested agg) or field
Attribute attr = ((NamedExpression) order.child()).toAttribute();
@ -451,19 +453,19 @@ class QueryFolder extends RuleExecutor<PhysicalPlan> {
if (sfa.orderBy() instanceof NamedExpression) {
Attribute at = ((NamedExpression) sfa.orderBy()).toAttribute();
at = qContainer.aliases().getOrDefault(at, at);
qContainer = qContainer.sort(new AttributeSort(at, direction));
qContainer = qContainer.sort(new AttributeSort(at, direction, missing));
} else if (!sfa.orderBy().foldable()) {
// ignore constant
throw new PlanningException("does not know how to order by expression {}", sfa.orderBy());
}
} else {
// nope, use scripted sorting
qContainer = qContainer.sort(new ScriptSort(sfa.script(), direction));
qContainer = qContainer.sort(new ScriptSort(sfa.script(), direction, missing));
}
} else if (attr instanceof ScoreAttribute) {
qContainer = qContainer.sort(new ScoreSort(direction));
qContainer = qContainer.sort(new ScoreSort(direction, missing));
} else {
qContainer = qContainer.sort(new AttributeSort(attr, direction));
qContainer = qContainer.sort(new AttributeSort(attr, direction, missing));
}
}
}

View File

@ -32,15 +32,14 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunction;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeFunction;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeHistogramFunction;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.expression.predicate.And;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryPredicate;
import org.elasticsearch.xpack.sql.expression.predicate.IsNotNull;
import org.elasticsearch.xpack.sql.expression.predicate.Not;
import org.elasticsearch.xpack.sql.expression.predicate.Or;
import org.elasticsearch.xpack.sql.expression.predicate.Range;
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;
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;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparison;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.Equals;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.GreaterThan;
@ -50,6 +49,7 @@ import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.Less
import org.elasticsearch.xpack.sql.expression.predicate.regex.Like;
import org.elasticsearch.xpack.sql.expression.predicate.regex.LikePattern;
import org.elasticsearch.xpack.sql.expression.predicate.regex.RLike;
import org.elasticsearch.xpack.sql.expression.predicate.regex.RegexMatch;
import org.elasticsearch.xpack.sql.querydsl.agg.AggFilter;
import org.elasticsearch.xpack.sql.querydsl.agg.AndAggFilter;
import org.elasticsearch.xpack.sql.querydsl.agg.AvgAgg;
@ -394,10 +394,10 @@ abstract class QueryTranslator {
// TODO: need to optimize on ngram
// TODO: see whether escaping is needed
static class Likes extends ExpressionTranslator<BinaryPredicate> {
static class Likes extends ExpressionTranslator<RegexMatch> {
@Override
protected QueryTranslation asQuery(BinaryPredicate e, boolean onAggs) {
protected QueryTranslation asQuery(RegexMatch e, boolean onAggs) {
Query q = null;
boolean inexact = true;
String target = null;
@ -412,7 +412,7 @@ abstract class QueryTranslator {
}
if (e instanceof Like) {
LikePattern p = ((Like) e).right();
LikePattern p = ((Like) e).pattern();
if (inexact) {
q = new QueryStringQuery(e.location(), p.asLuceneWildcard(), target);
}
@ -459,10 +459,10 @@ abstract class QueryTranslator {
}
}
static class BinaryLogic extends ExpressionTranslator<BinaryPredicate> {
static class BinaryLogic extends ExpressionTranslator<org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogic> {
@Override
protected QueryTranslation asQuery(BinaryPredicate e, boolean onAggs) {
protected QueryTranslation asQuery(org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogic e, boolean onAggs) {
if (e instanceof And) {
return and(e.location(), toQuery(e.left(), onAggs), toQuery(e.right(), onAggs));
}

View File

@ -5,20 +5,20 @@
*/
package org.elasticsearch.xpack.sql.querydsl.agg;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.aggregations.PipelineAggregationBuilder;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.util.Check;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import static org.elasticsearch.search.aggregations.pipeline.PipelineAggregatorBuilders.bucketSelector;
public class AggFilter extends PipelineAgg {
private static final String BUCKET_SELECTOR_ID_PREFIX = "having";
private static final String BUCKET_SELECTOR_ID_PREFIX = "having.";
private final ScriptTemplate scriptTemplate;
private final Map<String, String> aggPaths;

View File

@ -5,14 +5,7 @@
*/
package org.elasticsearch.xpack.sql.querydsl.agg;
import org.elasticsearch.xpack.sql.expression.gen.script.Params;
import org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.Locale;
import static java.lang.String.format;
import org.elasticsearch.xpack.sql.expression.gen.script.Scripts;
public class AndAggFilter extends AggFilter {
@ -21,12 +14,6 @@ public class AndAggFilter extends AggFilter {
}
public AndAggFilter(String name, AggFilter left, AggFilter right) {
super(name, and(left.scriptTemplate(), right.scriptTemplate()));
}
private static ScriptTemplate and(ScriptTemplate left, ScriptTemplate right) {
String template = format(Locale.ROOT, "( %s ) && ( %s )", left.template(), right.template());
Params params = new ParamsBuilder().script(left.params()).script(right.params()).build();
return new ScriptTemplate(template, params, DataType.BOOLEAN);
super(name, Scripts.and(left.scriptTemplate(), right.scriptTemplate()));
}
}

View File

@ -5,14 +5,7 @@
*/
package org.elasticsearch.xpack.sql.querydsl.agg;
import org.elasticsearch.xpack.sql.expression.gen.script.Params;
import org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.Locale;
import static java.lang.String.format;
import org.elasticsearch.xpack.sql.expression.gen.script.Scripts;
public class OrAggFilter extends AggFilter {
@ -21,12 +14,6 @@ public class OrAggFilter extends AggFilter {
}
public OrAggFilter(String name, AggFilter left, AggFilter right) {
super(name, and(left.scriptTemplate(), right.scriptTemplate()));
}
private static ScriptTemplate and(ScriptTemplate left, ScriptTemplate right) {
String template = format(Locale.ROOT, "( %s ) || ( %s )", left.template(), right.template());
Params params = new ParamsBuilder().script(left.params()).script(right.params()).build();
return new ScriptTemplate(template, params, DataType.BOOLEAN);
super(name, Scripts.or(left.scriptTemplate(), right.scriptTemplate()));
}
}

View File

@ -5,16 +5,16 @@
*/
package org.elasticsearch.xpack.sql.querydsl.container;
import java.util.Objects;
import org.elasticsearch.xpack.sql.expression.Attribute;
import java.util.Objects;
public class AttributeSort extends Sort {
private final Attribute attribute;
public AttributeSort(Attribute attribute, Direction direction) {
super(direction);
public AttributeSort(Attribute attribute, Direction direction, Missing missing) {
super(direction, missing);
this.attribute = attribute;
}
@ -24,7 +24,7 @@ public class AttributeSort extends Sort {
@Override
public int hashCode() {
return Objects.hash(attribute, direction());
return Objects.hash(attribute, direction(), missing());
}
@Override
@ -39,6 +39,7 @@ public class AttributeSort extends Sort {
AttributeSort other = (AttributeSort) obj;
return Objects.equals(direction(), other.direction())
&& Objects.equals(missing(), other.missing())
&& Objects.equals(attribute, other.attribute);
}
}

View File

@ -8,13 +8,13 @@ package org.elasticsearch.xpack.sql.querydsl.container;
import java.util.Objects;
public class ScoreSort extends Sort {
public ScoreSort(Direction direction) {
super(direction);
public ScoreSort(Direction direction, Missing missing) {
super(direction, missing);
}
@Override
public int hashCode() {
return Objects.hash(direction());
return Objects.hash(direction(), missing());
}
@Override
@ -28,6 +28,7 @@ public class ScoreSort extends Sort {
}
ScriptSort other = (ScriptSort) obj;
return Objects.equals(direction(), other.direction());
return Objects.equals(direction(), other.direction())
&& Objects.equals(missing(), other.missing());
}
}

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.sql.querydsl.container;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.expression.gen.script.Scripts;
import java.util.Objects;
@ -13,9 +14,9 @@ public class ScriptSort extends Sort {
private final ScriptTemplate script;
public ScriptSort(ScriptTemplate script, Direction direction) {
super(direction);
this.script = script;
public ScriptSort(ScriptTemplate script, Direction direction, Missing missing) {
super(direction, missing);
this.script = Scripts.nullSafeSort(script);
}
public ScriptTemplate script() {
@ -24,7 +25,7 @@ public class ScriptSort extends Sort {
@Override
public int hashCode() {
return Objects.hash(direction(), script);
return Objects.hash(direction(), missing(), script);
}
@Override
@ -39,6 +40,7 @@ public class ScriptSort extends Sort {
ScriptSort other = (ScriptSort) obj;
return Objects.equals(direction(), other.direction())
&& Objects.equals(missing(), other.missing())
&& Objects.equals(script, other.script);
}
}

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.sql.querydsl.container;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.xpack.sql.expression.Order.NullsPosition;
import org.elasticsearch.xpack.sql.expression.Order.OrderDirection;
public class Sort {
@ -22,13 +23,37 @@ public class Sort {
}
}
private final Direction direction;
public enum Missing {
FIRST("_first"), LAST("_last");
protected Sort(Direction direction) {
private final String position;
Missing(String position) {
this.position = position;
}
public static Missing from(NullsPosition pos) {
return pos == null || pos == NullsPosition.FIRST ? FIRST : LAST;
}
public String position() {
return position;
}
}
private final Direction direction;
private final Missing missing;
protected Sort(Direction direction, Missing nulls) {
this.direction = direction;
this.missing = nulls;
}
public Direction direction() {
return direction;
}
public Missing missing() {
return missing;
}
}

View File

@ -5,12 +5,13 @@
*/
package org.elasticsearch.xpack.sql.querydsl.query;
import java.util.Objects;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.expression.gen.script.Scripts;
import org.elasticsearch.xpack.sql.tree.Location;
import java.util.Objects;
import static org.elasticsearch.index.query.QueryBuilders.scriptQuery;
public class ScriptQuery extends LeafQuery {
@ -19,7 +20,8 @@ public class ScriptQuery extends LeafQuery {
public ScriptQuery(Location location, ScriptTemplate script) {
super(location);
this.script = script;
// make script null safe
this.script = Scripts.nullSafeFilter(script);
}
public ScriptTemplate script() {

View File

@ -24,7 +24,17 @@ public class ReflectionUtils {
c);
}
return (Class<E>) typeArguments[0];
Type tp = typeArguments[0];
if (tp instanceof Class<?>) {
return (Class<E>) tp;
} else if (tp instanceof ParameterizedType) {
Type rawType = ((ParameterizedType) type).getRawType();
if (rawType instanceof Class<?>) {
return (Class<E>) rawType;
}
}
throw new SqlIllegalArgumentException("Unexpected class structure for class {}", c);
}
clazz = clazz.getSuperclass();
}

View File

@ -7,32 +7,101 @@
# This file contains a whitelist for SQL specific utilities available inside SQL scripting
class org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalSqlScriptUtils {
Integer dateTimeChrono(long, String, String)
String dayName(long, String)
String monthName(long, String)
Integer quarter(long, String)
#
# Utilities
#
def docValue(java.util.Map, String)
boolean nullSafeFilter(Boolean)
double nullSafeSortNumeric(Number)
String nullSafeSortString(Object)
#
# Comparison
#
Boolean eq(Object, Object)
Boolean lt(Object, Object)
Boolean lte(Object, Object)
Boolean gt(Object, Object)
Boolean gte(Object, Object)
#
# Logical
#
Boolean and(Boolean, Boolean)
Boolean or(Boolean, Boolean)
#
# Regex
#
Boolean regex(String, String)
#
# Math
#
Number add(Number, Number)
Number div(Number, Number)
Number mod(Number, Number)
Number mul(Number, Number)
Number sub(Number, Number)
Number round(Number, Number)
Number truncate(Number, Number)
Double abs(Number)
Double acos(Number)
Double asin(Number)
Double atan(Number)
Double cbrt(Number)
Double ceil(Number)
Double cos(Number)
Double cosh(Number)
Double cot(Number)
Double degrees(Number)
Double e(Number)
Double exp(Number)
Double expm1(Number)
Double floor(Number)
Double log(Number)
Double log10(Number)
Double pi(Number)
Double radians(Number)
Double random(Number)
Double sign(Number)
Double sin(Number)
Double sinh(Number)
Double sqrt(Number)
Double tan(Number)
#
# Date/Time functions
#
Integer dateTimeChrono(Object, String, String)
String dayName(Object, String)
String monthName(Object, String)
Integer quarter(Object, String)
#
# ASCII Functions
#
Integer ascii(String)
Integer bitLength(String)
String character(Number)
String character(Number)
Integer charLength(String)
String concat(String, String)
String insert(String, int, int, String)
String lcase(String)
String left(String, int)
String concat(String, String)
String insert(String, Number, Number, String)
String lcase(String)
String left(String, Number)
Integer length(String)
Integer locate(String, String)
Integer locate(String, String, Integer)
String ltrim(String)
Integer locate(String, String, Number)
String ltrim(String)
Integer octetLength(String)
Integer position(String, String)
String repeat(String, int)
String replace(String, String, String)
String right(String, int)
String rtrim(String)
String space(Number)
String substring(String, int, int)
String ucase(String)
String repeat(String, Number)
String replace(String, String, String)
String right(String, Number)
String rtrim(String)
String space(Number)
String substring(String, Number, Number)
String ucase(String)
}

View File

@ -10,6 +10,7 @@ import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.AggregatorFactories.Builder;
import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
@ -20,6 +21,7 @@ import org.elasticsearch.xpack.sql.querydsl.container.AttributeSort;
import org.elasticsearch.xpack.sql.querydsl.container.QueryContainer;
import org.elasticsearch.xpack.sql.querydsl.container.ScoreSort;
import org.elasticsearch.xpack.sql.querydsl.container.Sort.Direction;
import org.elasticsearch.xpack.sql.querydsl.container.Sort.Missing;
import org.elasticsearch.xpack.sql.querydsl.query.MatchQuery;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.KeywordEsField;
@ -84,21 +86,25 @@ public class SourceGeneratorTests extends ESTestCase {
public void testSortScoreSpecified() {
QueryContainer container = new QueryContainer()
.sort(new ScoreSort(Direction.DESC));
.sort(new ScoreSort(Direction.DESC, null));
SearchSourceBuilder sourceBuilder = SourceGenerator.sourceBuilder(container, null, randomIntBetween(1, 10));
assertEquals(singletonList(scoreSort()), sourceBuilder.sorts());
}
public void testSortFieldSpecified() {
FieldSortBuilder sortField = fieldSort("test").unmappedType("keyword");
QueryContainer container = new QueryContainer()
.sort(new AttributeSort(new FieldAttribute(new Location(1, 1), "test", new KeywordEsField("test")), Direction.ASC));
.sort(new AttributeSort(new FieldAttribute(new Location(1, 1), "test", new KeywordEsField("test")), Direction.ASC,
Missing.LAST));
SearchSourceBuilder sourceBuilder = SourceGenerator.sourceBuilder(container, null, randomIntBetween(1, 10));
assertEquals(singletonList(fieldSort("test").order(SortOrder.ASC)), sourceBuilder.sorts());
assertEquals(singletonList(sortField.order(SortOrder.ASC).missing("_last")), sourceBuilder.sorts());
container = new QueryContainer()
.sort(new AttributeSort(new FieldAttribute(new Location(1, 1), "test", new KeywordEsField("test")), Direction.DESC));
.sort(new AttributeSort(new FieldAttribute(new Location(1, 1), "test", new KeywordEsField("test")), Direction.DESC,
Missing.FIRST));
sourceBuilder = SourceGenerator.sourceBuilder(container, null, randomIntBetween(1, 10));
assertEquals(singletonList(fieldSort("test").order(SortOrder.DESC)), sourceBuilder.sorts());
assertEquals(singletonList(sortField.order(SortOrder.DESC).missing("_first")), sourceBuilder.sorts());
}
public void testNoSort() {

View File

@ -13,15 +13,15 @@ import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.function.scalar.Processors;
import org.elasticsearch.xpack.sql.expression.gen.processor.ConstantProcessor;
import static org.elasticsearch.xpack.sql.tree.Location.EMPTY;
import static org.elasticsearch.xpack.sql.expression.function.scalar.FunctionTestUtils.l;
import static org.elasticsearch.xpack.sql.tree.Location.EMPTY;
public class ConcatProcessorTests extends AbstractWireSerializingTestCase<ConcatFunctionProcessor> {
@Override
protected ConcatFunctionProcessor createTestInstance() {
return new ConcatFunctionProcessor(
new ConstantProcessor(randomRealisticUnicodeOfLengthBetween(0, 128)),
new ConstantProcessor(randomRealisticUnicodeOfLengthBetween(0, 128)),
new ConstantProcessor(randomRealisticUnicodeOfLengthBetween(0, 128)));
}
@ -43,7 +43,7 @@ public class ConcatProcessorTests extends AbstractWireSerializingTestCase<Concat
public void testConcatFunctionWithEdgeCases() {
assertEquals("foo", new Concat(EMPTY, l("foo"), l(null)).makePipe().asProcessor().process(null));
assertEquals("bar", new Concat(EMPTY, l(null), l("bar")).makePipe().asProcessor().process(null));
assertNull(new Concat(EMPTY, l(null), l(null)).makePipe().asProcessor().process(null));
assertEquals("", new Concat(EMPTY, l(null), l(null)).makePipe().asProcessor().process(null));
}
public void testConcatFunctionInputsValidation() {

View File

@ -29,12 +29,12 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.math.ATan;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Abs;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.E;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Floor;
import org.elasticsearch.xpack.sql.expression.predicate.And;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
import org.elasticsearch.xpack.sql.expression.predicate.IsNotNull;
import org.elasticsearch.xpack.sql.expression.predicate.Not;
import org.elasticsearch.xpack.sql.expression.predicate.Or;
import org.elasticsearch.xpack.sql.expression.predicate.Range;
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;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Add;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Div;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Mod;
@ -204,7 +204,7 @@ public class OptimizerTests extends ESTestCase {
// SELECT
p = new Project(EMPTY, p, Arrays.asList(a, b));
// ORDER BY
p = new OrderBy(EMPTY, p, singletonList(new Order(EMPTY, b, OrderDirection.ASC)));
p = new OrderBy(EMPTY, p, singletonList(new Order(EMPTY, b, OrderDirection.ASC, null)));
LogicalPlan result = new ReplaceFoldableAttributes().apply(p);
assertNotSame(p, result);
@ -319,7 +319,7 @@ public class OptimizerTests extends ESTestCase {
return ((Literal) new ConstantFolding().rule(f)).value();
}
private static Object foldOperator(BinaryOperator b) {
private static Object foldOperator(BinaryOperator<?, ?, ?, ?> b) {
return ((Literal) new ConstantFolding().rule(b)).value();
}

View File

@ -69,7 +69,7 @@ public class EscapedFunctionsTests extends ESTestCase {
private LikePattern likeEscape(String like, String character) {
Expression exp = parser.createExpression(format(Locale.ROOT, "exp LIKE '%s' {escape '%s'}", like, character));
assertThat(exp, instanceOf(Like.class));
return ((Like) exp).right();
return ((Like) exp).pattern();
}
private Function function(String name) {

View File

@ -39,7 +39,7 @@ public class LikeEscapingParsingTests extends ESTestCase {
}
assertThat(exp, instanceOf(Like.class));
Like l = (Like) exp;
return l.right();
return l.pattern();
}
public void testNoEscaping() {

View File

@ -33,3 +33,5 @@
sort:
- int:
order: asc
missing: _last
unmapped_type: long

View File

@ -74,12 +74,12 @@ public class JdbcDocCsvSpecIT extends SpecBaseIntegrationTestCase {
@Override
protected boolean logEsResultSet() {
return false;
return true;
}
@Override
protected final void doTest() throws Throwable {
try (Connection csv = csvConnection(testCase.expectedResults); Connection es = esJdbc()) {
try (Connection csv = csvConnection(testCase); Connection es = esJdbc()) {
// pass the testName as table for debugging purposes (in case the underlying reader is missing)
ResultSet expected = executeCsvQuery(csv, testName);

View File

@ -49,8 +49,7 @@ public abstract class CsvSpecTestCase extends SpecBaseIntegrationTestCase {
@Override
protected final void doTest() throws Throwable {
try (Connection csv = csvConnection(testCase.expectedResults);
Connection es = esJdbc()) {
try (Connection csv = csvConnection(testCase); Connection es = esJdbc()) {
// pass the testName as table for debugging purposes (in case the underlying reader is missing)
ResultSet expected = executeCsvQuery(csv, testName);

View File

@ -25,7 +25,9 @@ import java.util.List;
import java.util.Locale;
import java.util.Properties;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.isEmptyOrNullString;
import static org.junit.Assert.assertThat;
/**
@ -55,12 +57,12 @@ public final class CsvTestUtils {
*
* Use {@link #executeCsvQuery} to obtain ResultSet from this connection
*/
public static Connection csvConnection(String expectedResults) throws IOException, SQLException {
public static Connection csvConnection(CsvTestCase csvTest) throws IOException, SQLException {
Properties csvProperties = new Properties();
csvProperties.setProperty("charset", "UTF-8");
csvProperties.setProperty("separator", "|");
csvProperties.setProperty("trimValues", "true");
Tuple<String, String> resultsAndTypes = extractColumnTypesAndStripCli(expectedResults);
Tuple<String, String> resultsAndTypes = extractColumnTypesAndStripCli(csvTest.earlySchema, csvTest.expectedResults);
csvProperties.setProperty("columnTypes", resultsAndTypes.v2());
Reader reader = new StringReader(resultsAndTypes.v1());
TableReader tableReader = new TableReader() {
@ -78,7 +80,7 @@ public final class CsvTestUtils {
};
}
private static Tuple<String, String> extractColumnTypesAndStripCli(String expectedResults) throws IOException {
private static Tuple<String, String> extractColumnTypesAndStripCli(String schema, String expectedResults) throws IOException {
try (StringReader reader = new StringReader(expectedResults);
BufferedReader bufferedReader = new BufferedReader(reader);
StringWriter writer = new StringWriter();
@ -87,8 +89,14 @@ public final class CsvTestUtils {
String header = bufferedReader.readLine();
Tuple<String, String> headerAndTypes;
String sch = schema;
if (header.contains(":")) {
headerAndTypes = extractColumnTypesFromHeader(header);
assertThat("Cannot declare schema both individually and inside the header", sch, isEmptyOrNullString());
sch = header;
}
if (Strings.hasText(sch)) {
headerAndTypes = extractColumnTypesFromHeader(sch);
} else {
// No type information in headers, no need to parse columns - trigger auto-detection
headerAndTypes = new Tuple<>(header, "");
@ -160,6 +168,9 @@ public final class CsvTestUtils {
}
private static class CsvSpecParser implements SpecBaseIntegrationTestCase.Parser {
private static final String SCHEMA_PREFIX = "schema::";
private final StringBuilder earlySchema = new StringBuilder();
private final StringBuilder query = new StringBuilder();
private final StringBuilder data = new StringBuilder();
private CsvTestCase testCase;
@ -168,17 +179,25 @@ public final class CsvTestUtils {
public Object parse(String line) {
// read the query
if (testCase == null) {
if (line.endsWith(";")) {
if (line.startsWith(SCHEMA_PREFIX)) {
assertThat("Early schema already declared " + earlySchema, earlySchema.length(), is(0));
earlySchema.append(line.substring(SCHEMA_PREFIX.length()).trim());
}
else {
if (line.endsWith(";")) {
// pick up the query
testCase = new CsvTestCase();
query.append(line.substring(0, line.length() - 1).trim());
testCase.query = query.toString();
testCase.earlySchema = earlySchema.toString();
earlySchema.setLength(0);
query.setLength(0);
}
// keep reading the query
else {
query.append(line);
query.append("\r\n");
}
// keep reading the query
else {
query.append(line);
query.append("\r\n");
}
}
}
// read the results
@ -204,6 +223,7 @@ public final class CsvTestUtils {
public static class CsvTestCase {
public String query;
public String earlySchema;
public String expectedResults;
}
}

View File

@ -6,12 +6,12 @@
package org.elasticsearch.xpack.qa.sql.jdbc;
import org.apache.http.HttpHost;
import org.apache.logging.log4j.LogManager;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.common.CheckedBiConsumer;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.json.JsonXContent;
@ -33,7 +33,7 @@ public class DataLoader {
public static void main(String[] args) throws Exception {
try (RestClient client = RestClient.builder(new HttpHost("localhost", 9200)).build()) {
loadEmpDatasetIntoEs(client);
Loggers.getLogger(DataLoader.class).info("Data loaded");
LogManager.getLogger(DataLoader.class).info("Data loaded");
}
}
@ -44,7 +44,6 @@ public class DataLoader {
protected static void loadEmpDatasetIntoEs(RestClient client) throws Exception {
loadEmpDatasetIntoEs(client, "test_emp", "employees");
loadEmpDatasetIntoEs(client, "test_emp_copy", "employees");
loadEmpDatasetIntoEs(client, "test_emp_with_nulls", "employees_with_nulls");
makeAlias(client, "test_alias", "test_emp", "test_emp_copy");
makeAlias(client, "test_alias_emp", "test_emp", "test_emp_copy");
}
@ -134,12 +133,16 @@ public class DataLoader {
bulk.append("{\"index\":{}}\n");
bulk.append('{');
String emp_no = fields.get(1);
boolean hadLastItem = false;
for (int f = 0; f < fields.size(); f++) {
// an empty value in the csv file is treated as 'null', thus skipping it in the bulk request
if (fields.get(f).trim().length() > 0) {
if (f != 0) {
bulk.append(',');
if (hadLastItem) {
bulk.append(",");
}
hadLastItem = true;
bulk.append('"').append(titles.get(f)).append("\":\"").append(fields.get(f)).append('"');
}
}

View File

@ -54,9 +54,7 @@ public abstract class DebugCsvSpec extends SpecBaseIntegrationTestCase {
@Override
protected final void doTest() throws Throwable {
try (Connection csv = csvConnection(testCase.expectedResults);
Connection es = esJdbc()) {
try (Connection csv = csvConnection(testCase); Connection es = esJdbc()) {
// pass the testName as table for debugging purposes (in case the underlying reader is missing)
ResultSet expected = executeCsvQuery(csv, testName);
ResultSet elasticResults = executeJdbcQuery(es, testCase.query);

View File

@ -26,6 +26,7 @@ import static java.sql.Types.REAL;
import static java.sql.Types.SMALLINT;
import static java.sql.Types.TINYINT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@ -177,7 +178,12 @@ public class JdbcAssert {
// handle nulls first
if (expectedObject == null || actualObject == null) {
assertEquals(msg, expectedObject, actualObject);
// hack for JDBC CSV nulls
if (expectedObject != null && "null".equals(expectedObject.toString().toLowerCase(Locale.ROOT))) {
assertNull(msg, actualObject);
} else {
assertEquals(msg, expectedObject, actualObject);
}
}
// then timestamp
else if (type == Types.TIMESTAMP || type == Types.TIMESTAMP_WITH_TIMEZONE) {

View File

@ -16,6 +16,22 @@ import java.util.Properties;
public class LocalH2 extends ExternalResource implements CheckedSupplier<Connection, SQLException> {
/*
* The syntax on the connection string is fairly particular:
* mem:; creates an anonymous database in memory. The `;` is
* technically the separator that comes after the name.
* DATABASE_TO_UPPER=false turns *off* H2's Oracle-like habit
* of upper-casing everything that isn't quoted.
* ALIAS_COLUMN_NAME=true turn *on* returning alias names in
* result set metadata which is what most DBs do except
* for MySQL and, by default, H2. Our jdbc driver does it.
*/
// http://www.h2database.com/html/features.html#in_memory_databases
private static String memUrl(String name) {
String n = name == null ? "" : name;
return "jdbc:h2:mem:" + n + ";DATABASE_TO_UPPER=false;ALIAS_COLUMN_NAME=true";
}
static {
try {
// Initialize h2 so we can use it for testing
@ -30,7 +46,7 @@ public class LocalH2 extends ExternalResource implements CheckedSupplier<Connect
* Closing the connection will remove the db.
*/
public static Connection anonymousDb() throws SQLException {
return DriverManager.getConnection("jdbc:h2:mem:;DATABASE_TO_UPPER=false;ALIAS_COLUMN_NAME=true");
return DriverManager.getConnection(memUrl(null));
}
private static final Properties DEFAULTS = new Properties();
@ -41,19 +57,8 @@ public class LocalH2 extends ExternalResource implements CheckedSupplier<Connect
private CheckedConsumer<Connection, SQLException> initializer;
/*
* The syntax on the connection string is fairly particular:
* mem:; creates an anonymous database in memory. The `;` is
* technically the separator that comes after the name.
* DATABASE_TO_UPPER=false turns *off* H2's Oracle-like habit
* of upper-casing everything that isn't quoted.
* ALIAS_COLUMN_NAME=true turn *on* returning alias names in
* result set metadata which is what most DBs do except
* for MySQL and, by default, H2. Our jdbc driver does it.
*/
// http://www.h2database.com/html/features.html#in_memory_databases
public LocalH2(CheckedConsumer<Connection, SQLException> initializer) {
this.url = "jdbc:h2:mem:essql;DATABASE_TO_UPPER=false;ALIAS_COLUMN_NAME=true";
this.url = memUrl("essql");
this.initializer = initializer;
}
@ -76,4 +81,4 @@ public class LocalH2 extends ExternalResource implements CheckedSupplier<Connect
public Connection get() throws SQLException {
return DriverManager.getConnection(url, DEFAULTS);
}
}
}

View File

@ -27,7 +27,6 @@ public abstract class SqlSpecTestCase extends SpecBaseIntegrationTestCase {
@ClassRule
public static LocalH2 H2 = new LocalH2((c) -> {
c.createStatement().execute("RUNSCRIPT FROM 'classpath:/setup_test_emp.sql'");
c.createStatement().execute("RUNSCRIPT FROM 'classpath:/setup_test_emp_with_nulls.sql'");
});
@ParametersFactory(argumentFormatting = PARAM_FORMATTING)
@ -42,7 +41,6 @@ public abstract class SqlSpecTestCase extends SpecBaseIntegrationTestCase {
tests.addAll(readScriptSpec("/arithmetic.sql-spec", parser));
tests.addAll(readScriptSpec("/string-functions.sql-spec", parser));
tests.addAll(readScriptSpec("/case-functions.sql-spec", parser));
tests.addAll(readScriptSpec("/agg_nulls.sql-spec", parser));
return tests;
}

View File

@ -464,7 +464,7 @@ public abstract class RestSqlTestCase extends ESRestTestCase implements ErrorsTe
@SuppressWarnings("unchecked")
Map<String, Object> termsScript = (Map<String, Object>) terms.get("script");
assertEquals(3, termsScript.size());
assertEquals("Math.abs(doc[params.v0].value)", termsScript.get("source"));
assertEquals("InternalSqlScriptUtils.abs(InternalSqlScriptUtils.docValue(doc,params.v0))", termsScript.get("source"));
assertEquals("painless", termsScript.get("lang"));
@SuppressWarnings("unchecked")
@ -494,7 +494,7 @@ public abstract class RestSqlTestCase extends ESRestTestCase implements ErrorsTe
}
}
Collections.sort(aggKeys);
assertEquals("having" + aggKeys.get(1), aggFilterKey);
assertEquals("having." + aggKeys.get(1), aggFilterKey);
@SuppressWarnings("unchecked")
Map<String, Object> having = (Map<String, Object>) aggregations2.get(aggFilterKey);
@ -513,7 +513,7 @@ public abstract class RestSqlTestCase extends ESRestTestCase implements ErrorsTe
@SuppressWarnings("unchecked")
Map<String, Object> filterScript = (Map<String, Object>) bucketSelector.get("script");
assertEquals(3, filterScript.size());
assertEquals("(params.a0) > (params.v0)", filterScript.get("source"));
assertEquals("InternalSqlScriptUtils.gt(params.a0,params.v0)", filterScript.get("source"));
assertEquals("painless", filterScript.get("lang"));
@SuppressWarnings("unchecked")
Map<String, Object> filterScriptParams = (Map<String, Object>) filterScript.get("params");

View File

@ -6,64 +6,72 @@ singlePercentileWithoutComma
SELECT gender, PERCENTILE(emp_no, 97) p1 FROM test_emp GROUP BY gender;
gender:s | p1:d
F | 10099.1936
M | 10095.6112
null |10019.0
F |10099.51
M |10095.789999999999
;
singlePercentileWithComma
SELECT gender, PERCENTILE(emp_no, 97.76) p1 FROM test_emp GROUP BY gender;
gender:s | p1:d
F | 10099.1936
M | 10095.6112
null |10019.0
F |10099.51
M |10095.789999999999
;
multiplePercentilesOneWithCommaOneWithout
SELECT gender, PERCENTILE(emp_no, 92.45) p1, PERCENTILE(emp_no, 91) p2 FROM test_emp GROUP BY gender;
gender:s | p1:d | p2:d
F | 10096.826000000001 | 10094.68
M | 10090.319 | 10089.320000000002
null |10018.745 |10018.599999999999
F |10098.0085 |10096.119999999999
M |10091.393 |10090.37
;
multiplePercentilesWithoutComma
SELECT gender, PERCENTILE(emp_no, 91) p1, PERCENTILE(emp_no, 89) p2 FROM test_emp GROUP BY gender;
gender:s | p1:d | p2:d
F | 10094.68 | 10092.08
M | 10089.320000000002 | 10085.18
null |10018.599999999999 |10018.4
F |10096.119999999999 |10093.74
M |10090.37 |10086.92
;
multiplePercentilesWithComma
SELECT gender, PERCENTILE(emp_no, 85.7) p1, PERCENTILE(emp_no, 94.3) p2 FROM test_emp GROUP BY gender;
gender:s | p1:d | p2:d
F | 10088.852 | 10097.792
M | 10083.134 | 10091.932
null |10018.070000000002 |10018.929999999998
F |10091.343 |10098.619
M |10084.349 |10093.502
;
percentileRank
SELECT gender, PERCENTILE_RANK(emp_no, 10025) rank FROM test_emp GROUP BY gender;
gender:s | rank:d
F | 26.351351351351347
M | 23.41269841269841
null |100.0
F |17.424242424242426
M |15.350877192982457
;
multiplePercentileRanks
SELECT gender, PERCENTILE_RANK(emp_no, 10030.0) rank1, PERCENTILE_RANK(emp_no, 10025) rank2 FROM test_emp GROUP BY gender;
gender:s | rank1:d | rank2:d
F | 29.93762993762994 | 26.351351351351347
M | 29.365079365079367 | 23.41269841269841
null |100.0 |100.0
F |21.445221445221442 |17.424242424242426
M |21.929824561403507 |15.350877192982457
;
multiplePercentilesAndPercentileRank
SELECT gender, PERCENTILE(emp_no, 97.76) p1, PERCENTILE(emp_no, 93.3) p2, PERCENTILE_RANK(emp_no, 10025) rank FROM test_emp GROUP BY gender;
gender:s | p1:d | p2:d | rank:d
F | 10099.1936 | 10098.021 | 26.351351351351347
M | 10095.6112 | 10090.846 | 23.41269841269841
null |10019.0 |10018.83 |100.0
F |10099.7608 |10098.289 |17.424242424242426
M |10096.2232 |10092.362 |15.350877192982457
;
sum
@ -110,6 +118,7 @@ SELECT gender, KURTOSIS(salary) k, SKEWNESS(salary) s FROM test_emp GROUP BY gen
gender:s | k:d | s:d
F | 1.8427808415250482 | 0.04517149340491813
M | 2.259327644285826 | 0.40268950715550333
null |2.2215791166941923 |-0.03373126000214023
F |1.7873117044424276 |0.05504995122217512
M |2.280646181070106 |0.44302407229580243
;

Some files were not shown because too many files have changed in this diff Show More