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" : [ "sort" : [
{ {
"page_count" : { "page_count" : {
"order" : "desc" "order" : "desc",
"missing" : "_first",
"unmapped_type" : "short"
} }
} }
] ]

View File

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

View File

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

View File

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

View File

@ -487,7 +487,8 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
if (ordinal != null) { if (ordinal != null) {
changed = true; changed = true;
if (ordinal > 0 && ordinal <= max) { 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 { else {
throw new AnalysisException(order, "Invalid %d specified in OrderBy (valid range is [1, %d])", ordinal, max); 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; FieldAttribute fa = (FieldAttribute) attr;
fa = fa.isInexact() ? fa.exactAttribute() : fa; 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()) { 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 newSort = new NestedSortBuilder(fa.nestedParent().name());
NestedSortBuilder nestedSort = fieldSort.getNestedSort(); NestedSortBuilder nestedSort = fieldSort.getNestedSort();

View File

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

View File

@ -20,18 +20,24 @@ public class Order extends Expression {
ASC, DESC ASC, DESC
} }
public enum NullsPosition {
FIRST, LAST;
}
private final Expression child; private final Expression child;
private final OrderDirection direction; 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)); super(location, singletonList(child));
this.child = child; this.child = child;
this.direction = direction; this.direction = direction;
this.nulls = nulls == null ? (direction == OrderDirection.DESC ? NullsPosition.FIRST : NullsPosition.LAST) : nulls;
} }
@Override @Override
protected NodeInfo<Order> info() { protected NodeInfo<Order> info() {
return NodeInfo.create(this, Order::new, child, direction); return NodeInfo.create(this, Order::new, child, direction, nulls);
} }
@Override @Override
@ -49,7 +55,7 @@ public class Order extends Expression {
if (newChildren.size() != 1) { if (newChildren.size() != 1) {
throw new IllegalArgumentException("expected [1] child but received [" + newChildren.size() + "]"); 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() { public Expression child() {
@ -60,6 +66,10 @@ public class Order extends Expression {
return direction; return direction;
} }
public NullsPosition nullsPosition() {
return nulls;
}
@Override @Override
public boolean foldable() { public boolean foldable() {
return false; return false;
@ -67,7 +77,7 @@ public class Order extends Expression {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(child, direction); return Objects.hash(child, direction, nulls);
} }
@Override @Override
@ -82,6 +92,7 @@ public class Order extends Expression {
Order other = (Order) obj; Order other = (Order) obj;
return Objects.equals(direction, other.direction) return Objects.equals(direction, other.direction)
&& Objects.equals(nulls, other.nulls)
&& Objects.equals(child, other.child); && 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.Expression;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate; 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 org.elasticsearch.xpack.sql.tree.Location;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Locale;
public abstract class BinaryScalarFunction extends ScalarFunction { public abstract class BinaryScalarFunction extends ScalarFunction {
@ -53,5 +55,11 @@ public abstract class BinaryScalarFunction extends ScalarFunction {
return asScriptFrom(leftScript, rightScript); 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.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataType;
import org.joda.time.DateTime;
import java.util.Objects;
import java.util.TimeZone; import java.util.TimeZone;
abstract class BaseDateTimeFunction extends UnaryScalarFunction { abstract class BaseDateTimeFunction extends UnaryScalarFunction {
@ -60,4 +62,32 @@ abstract class BaseDateTimeFunction extends UnaryScalarFunction {
public boolean foldable() { public boolean foldable() {
return field().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.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataType;
import org.joda.time.DateTime;
import java.time.Instant; import java.time.Instant;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
import java.util.Objects;
import java.util.TimeZone; import java.util.TimeZone;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder; import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
@ -33,13 +31,8 @@ public abstract class DateTimeFunction extends BaseDateTimeFunction {
} }
@Override @Override
public Object fold() { protected Object doFold(long millis, String tzId) {
DateTime folded = (DateTime) field().fold(); return dateTimeChrono(millis, tzId, chronoField().name());
if (folded == null) {
return null;
}
return dateTimeChrono(folded.getMillis(), timeZone().getID(), chronoField().name());
} }
public static Integer dateTimeChrono(long millis, String tzId, String chronoName) { 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))); 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 @Override
public ScriptTemplate scriptWithField(FieldAttribute field) { public ScriptTemplate scriptWithField(FieldAttribute field) {
ParamsBuilder params = paramsBuilder(); ParamsBuilder params = paramsBuilder();
String template = null; String template = null;
template = formatTemplate("{sql}.dateTimeChrono(doc[{}].value.millis, {}, {})"); template = formatTemplate("{sql}.dateTimeChrono(doc[{}].value, {}, {})");
params.variable(field.name()) params.variable(field.name())
.variable(timeZone().getID()) .variable(timeZone().getID())
.variable(chronoField().name()); .variable(chronoField().name());
@ -79,19 +77,4 @@ public abstract class DateTimeFunction extends BaseDateTimeFunction {
// used for applying ranges // used for applying ranges
public abstract String dateTimeFormat(); 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 class DayName extends NamedDateTimeFunction {
public DayName(Location location, Expression field, TimeZone timeZone) { public DayName(Location location, Expression field, TimeZone timeZone) {
super(location, field, timeZone); super(location, field, timeZone, NameExtractor.DAY_NAME);
} }
@Override @Override
@ -30,9 +30,4 @@ public class DayName extends NamedDateTimeFunction {
protected DayName replaceChild(Expression newChild) { protected DayName replaceChild(Expression newChild) {
return new DayName(location(), newChild, timeZone()); 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 class MonthName extends NamedDateTimeFunction {
public MonthName(Location location, Expression field, TimeZone timeZone) { public MonthName(Location location, Expression field, TimeZone timeZone) {
super(location, field, timeZone); super(location, field, timeZone, NameExtractor.MONTH_NAME);
} }
@Override @Override
@ -30,10 +30,4 @@ public class MonthName extends NamedDateTimeFunction {
protected MonthName replaceChild(Expression newChild) { protected MonthName replaceChild(Expression newChild) {
return new MonthName(location(), newChild, timeZone()); 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.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.util.StringUtils; import org.elasticsearch.xpack.sql.util.StringUtils;
import org.joda.time.DateTime;
import java.util.Locale; import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone; import java.util.TimeZone;
import static java.lang.String.format; 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 { 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); super(location, field, timeZone);
this.nameExtractor = nameExtractor;
} }
@Override @Override
public Object fold() { protected Object doFold(long millis, String tzId) {
DateTime folded = (DateTime) field().fold(); return nameExtractor.extract(millis, tzId);
if (folded == null) {
return null;
}
return nameExtractor().extract(folded.getMillis(), timeZone().getID());
} }
@Override @Override
public ScriptTemplate scriptWithField(FieldAttribute field) { public ScriptTemplate scriptWithField(FieldAttribute field) {
return new ScriptTemplate( return new ScriptTemplate(
formatTemplate(format(Locale.ROOT, "{sql}.%s(doc[{}].value.millis, {})", formatTemplate(format(Locale.ROOT, "{sql}.%s(doc[{}].value, {})",
StringUtils.underscoreToLowerCamelCase(nameExtractor().name()))), StringUtils.underscoreToLowerCamelCase(nameExtractor.name()))),
paramsBuilder() paramsBuilder()
.variable(field.name()) .variable(field.name())
.variable(timeZone().getID()).build(), .variable(timeZone().getID()).build(),
@ -56,29 +52,11 @@ abstract class NamedDateTimeFunction extends BaseDateTimeFunction {
@Override @Override
protected final Pipe makePipe() { protected final Pipe makePipe() {
return new UnaryPipe(location(), this, Expressions.pipe(field()), return new UnaryPipe(location(), this, Expressions.pipe(field()), new NamedDateTimeProcessor(nameExtractor, timeZone()));
new NamedDateTimeProcessor(nameExtractor(), timeZone()));
} }
protected abstract NameExtractor nameExtractor();
@Override @Override
public DataType dataType() { public DataType dataType() {
return DataType.KEYWORD; 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.Locale;
import java.util.Objects; import java.util.Objects;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.function.BiFunction; import java.util.function.Function;
public class NamedDateTimeProcessor extends BaseDateTimeProcessor { public class NamedDateTimeProcessor extends BaseDateTimeProcessor {
public enum NameExtractor { 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 // 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) -> { DAY_NAME(time -> time.format(DAY_NAME_FORMATTER)),
ZonedDateTime time = ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId)); MONTH_NAME(time -> time.format(MONTH_NAME_FORMATTER));
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));
});
private final BiFunction<Long,String,String> apply; private final Function<ZonedDateTime, String> apply;
NameExtractor(BiFunction<Long,String,String> apply) { NameExtractor(Function<ZonedDateTime, String> apply) {
this.apply = apply; this.apply = apply;
} }
public final String extract(Long millis, String tzId) { 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"; public static final String NAME = "ndt";
private static final String MONTH_NAME_FORMAT = "MMMM"; private static final DateTimeFormatter DAY_NAME_FORMATTER = DateTimeFormatter.ofPattern("EEEE", Locale.ROOT);
private static final String DAY_NAME_FORMAT = "EEEE"; private static final DateTimeFormatter MONTH_NAME_FORMATTER = DateTimeFormatter.ofPattern("MMMM", Locale.ROOT);
private final NameExtractor extractor; private final NameExtractor extractor;
public NamedDateTimeProcessor(NameExtractor extractor, TimeZone timeZone) { public NamedDateTimeProcessor(NameExtractor extractor, TimeZone timeZone) {
@ -97,4 +96,4 @@ public class NamedDateTimeProcessor extends BaseDateTimeProcessor {
public String toString() { public String toString() {
return extractor.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.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2;
import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataType;
import org.joda.time.DateTime;
import java.util.Objects;
import java.util.TimeZone; import java.util.TimeZone;
import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.QuarterProcessor.quarter; 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 { public class Quarter extends BaseDateTimeFunction {
protected static final String QUARTER_FORMAT = "q";
public Quarter(Location location, Expression field, TimeZone timeZone) { public Quarter(Location location, Expression field, TimeZone timeZone) {
super(location, field, timeZone); super(location, field, timeZone);
} }
@Override @Override
public Object fold() { protected Object doFold(long millis, String tzId) {
DateTime folded = (DateTime) field().fold(); return quarter(millis, tzId);
if (folded == null) {
return null;
}
return quarter(folded.getMillis(), timeZone().getID());
} }
@Override @Override
public ScriptTemplate scriptWithField(FieldAttribute field) { public ScriptTemplate scriptWithField(FieldAttribute field) {
return new ScriptTemplate(formatTemplate("{sql}.quarter(doc[{}].value.millis, {})"), return new ScriptTemplate(formatTemplate("{sql}.quarter(doc[{}].value, {})"),
paramsBuilder() paramsBuilder()
.variable(field.name()) .variable(field.name())
.variable(timeZone().getID()) .variable(timeZone().getID())
@ -70,19 +61,4 @@ public class Quarter extends BaseDateTimeFunction {
public DataType dataType() { public DataType dataType() {
return DataType.INTEGER; 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"; public static final String NAME = "q";
private static final DateTimeFormatter QUARTER_FORMAT = DateTimeFormatter.ofPattern("q", Locale.ROOT);
@Override @Override
public String getWriteableName() { public String getWriteableName() {
@ -40,8 +41,12 @@ public class QuarterProcessor extends BaseDateTimeProcessor {
} }
public static Integer quarter(long millis, String tzId) { public static Integer quarter(long millis, String tzId) {
ZonedDateTime time = ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId)); return quarter(ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId)), tzId);
return Integer.valueOf(time.format(DateTimeFormatter.ofPattern(Quarter.QUARTER_FORMAT, Locale.ROOT))); }
public static Integer quarter(ZonedDateTime zdt, String tzId) {
ZonedDateTime time = zdt.withZoneSameInstant(ZoneId.of(tzId));
return Integer.valueOf(time.format(QUARTER_FORMAT));
} }
@Override @Override

View File

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

View File

@ -6,10 +6,10 @@
package org.elasticsearch.xpack.sql.expression.function.scalar.math; package org.elasticsearch.xpack.sql.expression.function.scalar.math;
import org.elasticsearch.common.io.stream.StreamInput; 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.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation; 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.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.gen.processor.Processor;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Arithmetics; 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}. * 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> { public enum BinaryMathOperation implements BiFunction<Number, Number, Number> {
@ -27,12 +27,6 @@ public class BinaryMathProcessor extends BinaryNumericProcessor<BinaryMathOperat
MOD(Arithmetics::mod), MOD(Arithmetics::mod),
POWER((l, r) -> Math.pow(l.doubleValue(), r.doubleValue())), POWER((l, r) -> Math.pow(l.doubleValue(), r.doubleValue())),
ROUND((l, r) -> { ROUND((l, r) -> {
if (l == null) {
return null;
}
if (r == null) {
return l;
}
if (r instanceof Float || r instanceof Double) { if (r instanceof Float || r instanceof Double) {
throw new SqlIllegalArgumentException("An integer number is required; received [{}] as second parameter", r); 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; return Math.round(Math.abs(middleResult)) / tenAtScale * sign;
}), }),
TRUNCATE((l, r) -> { TRUNCATE((l, r) -> {
if (l == null) {
return null;
}
if (r == null) {
return l;
}
if (r instanceof Float || r instanceof Double) { if (r instanceof Float || r instanceof Double) {
throw new SqlIllegalArgumentException("An integer number is required; received [{}] as second parameter", r); throw new SqlIllegalArgumentException("An integer number is required; received [{}] as second parameter", r);
} }
@ -66,6 +54,9 @@ public class BinaryMathProcessor extends BinaryNumericProcessor<BinaryMathOperat
@Override @Override
public final Number apply(Number left, Number right) { public final Number apply(Number left, Number right) {
if (left == null || right == null) {
return null;
}
return process.apply(left, right); return process.apply(left, right);
} }
} }
@ -80,13 +71,15 @@ public class BinaryMathProcessor extends BinaryNumericProcessor<BinaryMathOperat
super(in, i -> i.readEnum(BinaryMathOperation.class)); super(in, i -> i.readEnum(BinaryMathOperation.class));
} }
@Override
protected void doWrite(StreamOutput out) throws IOException {
out.writeEnum(operation());
}
@Override @Override
public String getWriteableName() { public String getWriteableName() {
return NAME; 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.BinaryScalarFunction;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation; 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.pipeline.Pipe;
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataType;
import java.util.Locale;
import java.util.Objects; 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 { public abstract class BinaryNumericFunction extends BinaryScalarFunction {
private final BinaryMathOperation operation; private final BinaryMathOperation operation;
@ -51,7 +46,7 @@ public abstract class BinaryNumericFunction extends BinaryScalarFunction {
protected TypeResolution resolveInputType(DataType inputType) { protected TypeResolution resolveInputType(DataType inputType) {
return inputType.isNumeric() ? return inputType.isNumeric() ?
TypeResolution.TYPE_RESOLVED : 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 @Override
@ -64,18 +59,6 @@ public abstract class BinaryNumericFunction extends BinaryScalarFunction {
return new BinaryMathPipe(location(), this, Expressions.pipe(left()), Expressions.pipe(right()), operation); 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 @Override
public int hashCode() { public int hashCode() {
return Objects.hash(left(), right(), operation); 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.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo; 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> * <a href="https://en.wikipedia.org/wiki/Trigonometric_functions#Cosecant,_secant,_and_cotangent">Cotangent</a>
* function. * function.
@ -33,12 +29,6 @@ public class Cot extends MathFunction {
return new Cot(location(), newChild); 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 @Override
protected MathOperation operation() { protected MathOperation operation() {
return MathOperation.COT; return MathOperation.COT;

View File

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

View File

@ -41,7 +41,7 @@ public abstract class MathFunction extends UnaryScalarFunction {
@Override @Override
public String processScript(String template) { 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() { protected String mathFunction() {

View File

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

View File

@ -25,9 +25,4 @@ public class Power extends BinaryNumericFunction {
protected Power replaceChildren(Expression newLeft, Expression newRight) { protected Power replaceChildren(Expression newLeft, Expression newRight) {
return new Power(location(), newLeft, 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.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo; 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). * Returns a random double (using the given seed).
*/ */
@ -33,13 +29,6 @@ public class Random extends MathFunction {
return new Random(location(), newChild); 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 @Override
protected MathOperation operation() { protected MathOperation operation() {
return MathOperation.RANDOM; 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.Expression;
import org.elasticsearch.xpack.sql.expression.Literal; import org.elasticsearch.xpack.sql.expression.Literal;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation; 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.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType; 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 * 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 * 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) { protected Round replaceChildren(Expression newLeft, Expression newRight) {
return new Round(location(), newLeft, 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 @Override
public DataType dataType() { 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.Expression;
import org.elasticsearch.xpack.sql.expression.Literal; import org.elasticsearch.xpack.sql.expression.Literal;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation; 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.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType; 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 * 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 * 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); 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 @Override
public DataType dataType() { public DataType dataType() {
return left().dataType(); return left().dataType();

View File

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

View File

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

View File

@ -6,9 +6,9 @@
package org.elasticsearch.xpack.sql.expression.function.scalar.string; package org.elasticsearch.xpack.sql.expression.function.scalar.string;
import org.elasticsearch.common.io.stream.StreamInput; 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.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.BinaryStringNumericProcessor.BinaryStringNumericOperation; 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 org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import java.io.IOException; 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, * Processor class covering string manipulating functions that have the first parameter as string,
* second parameter as numeric and a string result. * 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> { public enum BinaryStringNumericOperation implements BiFunction<String, Number, String> {
LEFT((s,c) -> { LEFT((s,c) -> {
int i = c.intValue(); int i = c.intValue();
if (i < 0) return ""; if (i < 0) {
return "";
}
return i > s.length() ? s : s.substring(0, i); return i > s.length() ? s : s.substring(0, i);
}), }),
RIGHT((s,c) -> { RIGHT((s,c) -> {
int i = c.intValue(); int i = c.intValue();
if (i < 0) return ""; if (i < 0) {
return "";
}
return i > s.length() ? s : s.substring(s.length() - i); return i > s.length() ? s : s.substring(s.length() - i);
}), }),
REPEAT((s,c) -> { REPEAT((s,c) -> {
int i = c.intValue(); int i = c.intValue();
if (i <= 0) return null; if (i <= 0) {
return null;
}
StringBuilder sb = new StringBuilder(s.length() * i); StringBuilder sb = new StringBuilder(s.length() * i);
for (int j = 0; j < i; j++) { for (int j = 0; j < i; j++) {
@ -57,31 +53,24 @@ public class BinaryStringNumericProcessor extends BinaryStringProcessor<BinarySt
} }
private final BiFunction<String, Number, String> op; private final BiFunction<String, Number, String> op;
@Override @Override
public String apply(String stringExp, Number count) { public String apply(String stringExp, Number count) {
if (stringExp == null || count == null) {
return null;
}
return op.apply(stringExp, count); return op.apply(stringExp, count);
} }
} }
@Override public static final String NAME = "sn";
protected void doWrite(StreamOutput out) throws IOException {
out.writeEnum(operation()); public BinaryStringNumericProcessor(Processor left, Processor right, BinaryStringNumericOperation operation) {
super(left, right, operation);
} }
@Override public BinaryStringNumericProcessor(StreamInput in) throws IOException {
protected Object doProcess(Object left, Object right) { super(in, i -> i.readEnum(BinaryStringNumericOperation.class));
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);
} }
@Override @Override
@ -89,4 +78,15 @@ public class BinaryStringNumericProcessor extends BinaryStringProcessor<BinarySt
return NAME; 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; private final BinaryStringStringOperation operation;
public BinaryStringStringPipe(Location location, Expression expression, Pipe left, public BinaryStringStringPipe(Location location, Expression expression, Pipe left, Pipe right, BinaryStringStringOperation operation) {
Pipe right, BinaryStringStringOperation operation) {
super(location, expression, left, right); super(location, expression, left, right);
this.operation = operation; this.operation = operation;
} }

View File

@ -6,9 +6,9 @@
package org.elasticsearch.xpack.sql.expression.function.scalar.string; package org.elasticsearch.xpack.sql.expression.function.scalar.string;
import org.elasticsearch.common.io.stream.StreamInput; 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.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.BinaryStringStringProcessor.BinaryStringStringOperation; 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 org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import java.io.IOException; 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. * 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> { public enum BinaryStringStringOperation implements BiFunction<String, String, Number> {
POSITION((sub,str) -> { POSITION((sub,str) -> {
if (sub == null || str == null) return null;
int pos = str.indexOf(sub); int pos = str.indexOf(sub);
return pos < 0 ? 0 : pos+1; return pos < 0 ? 0 : pos+1;
}); });
@ -43,30 +32,22 @@ public class BinaryStringStringProcessor extends BinaryStringProcessor<BinaryStr
private final BiFunction<String, String, Number> op; private final BiFunction<String, String, Number> op;
@Override @Override
public Number apply(String stringExpLeft, String stringExpRight) { public Number apply(String left, String right) {
return op.apply(stringExpLeft, stringExpRight); if (left == null || right == null) {
return null;
}
return op.apply(left, right);
} }
} }
@Override public static final String NAME = "ss";
protected void doWrite(StreamOutput out) throws IOException {
out.writeEnum(operation()); public BinaryStringStringProcessor(StreamInput in) throws IOException {
super(in, i -> i.readEnum(BinaryStringStringOperation.class));
} }
@Override public BinaryStringStringProcessor(Processor left, Processor right, BinaryStringStringOperation operation) {
protected Object doProcess(Object left, Object right) { super(left, right, operation);
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);
} }
@Override @Override
@ -74,4 +55,15 @@ public class BinaryStringStringProcessor extends BinaryStringProcessor<BinaryStr
return NAME; 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.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataType;
import java.util.Locale; import static org.elasticsearch.xpack.sql.expression.function.scalar.string.ConcatFunctionProcessor.process;
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.gen.script.ParamsBuilder.paramsBuilder; import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
/** /**
@ -58,7 +55,7 @@ public class Concat extends BinaryScalarFunction {
@Override @Override
public Object fold() { public Object fold() {
return doProcessInScripts(left().fold(), right().fold()); return process(left().fold(), right().fold());
} }
@Override @Override
@ -71,18 +68,6 @@ public class Concat extends BinaryScalarFunction {
return NodeInfo.create(this, Concat::new, left(), right()); 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 @Override
public ScriptTemplate scriptWithField(FieldAttribute field) { public ScriptTemplate scriptWithField(FieldAttribute field) {
return new ScriptTemplate(processScript("doc[{}].value"), return new ScriptTemplate(processScript("doc[{}].value"),

View File

@ -15,8 +15,7 @@ import java.util.Objects;
public class ConcatFunctionPipe extends BinaryPipe { public class ConcatFunctionPipe extends BinaryPipe {
public ConcatFunctionPipe(Location location, Expression expression, Pipe left, public ConcatFunctionPipe(Location location, Expression expression, Pipe left, Pipe right) {
Pipe right) {
super(location, expression, left, 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.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.gen.processor.BinaryProcessor; import org.elasticsearch.xpack.sql.expression.gen.processor.BinaryProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor; import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
import org.elasticsearch.xpack.sql.util.StringUtils;
import java.io.IOException; import java.io.IOException;
import java.util.Objects; import java.util.Objects;
@ -27,14 +28,26 @@ public class ConcatFunctionProcessor extends BinaryProcessor {
} }
@Override @Override
protected Object doProcess(Object source1, Object source2) { public Object process(Object input) {
return doProcessInScripts(source1, source2); 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 * 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) { if (source1 == null) {
return source2; return source2;
} }
@ -79,4 +92,4 @@ public class ConcatFunctionProcessor extends BinaryProcessor {
@Override @Override
protected void doWrite(StreamOutput out) throws IOException { 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)); 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) { if (source == null) {
return null; return null;
} }
@ -63,9 +63,9 @@ public class LocateFunctionProcessor implements Processor {
String stringSource = source instanceof Character ? source.toString() : (String) source; String stringSource = source instanceof Character ? source.toString() : (String) source;
String stringPattern = pattern instanceof Character ? pattern.toString() : (String) pattern; 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, ((Number) start).intValue() - 1)
: stringSource.indexOf(stringPattern)); : stringSource.indexOf(stringPattern)));
} }
@Override @Override

View File

@ -75,24 +75,7 @@ public class StringProcessor implements Processor {
} }
StringOperation(NumericFunction<Object> apply) { StringOperation(NumericFunction<Object> apply) {
this.apply = l -> l == null ? null : apply.apply((l)); 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);
}
} }
public final Object apply(Object l) { public final Object apply(Object l) {

View File

@ -5,10 +5,14 @@
*/ */
package org.elasticsearch.xpack.sql.expression.function.scalar.whitelist; 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.DateTimeFunction;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NamedDateTimeProcessor.NameExtractor; 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.datetime.QuarterProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation; 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.BinaryStringNumericProcessor.BinaryStringNumericOperation;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.BinaryStringStringProcessor.BinaryStringStringOperation; import org.elasticsearch.xpack.sql.expression.function.scalar.string.BinaryStringStringProcessor.BinaryStringStringOperation;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.ConcatFunctionProcessor; 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.ReplaceFunctionProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.StringProcessor.StringOperation; 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.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. * Whitelisted class for SQL scripts.
@ -27,30 +39,244 @@ public final class InternalSqlScriptUtils {
private 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) { public static boolean nullSafeFilter(Boolean filter) {
return NameExtractor.DAY_NAME.extract(millis, tzId); 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) { public static Boolean lte(Object left, Object right) {
return NameExtractor.MONTH_NAME.extract(millis, tzId); 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) { public static Boolean or(Boolean left, Boolean right) {
return QuarterProcessor.quarter(millis, tzId); 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) { public static Number round(Number v, Number s) {
return BinaryMathOperation.ROUND.apply(v, s); return BinaryMathOperation.ROUND.apply(v, s);
} }
public static Number truncate(Number v, Number s) { public static Number truncate(Number v, Number s) {
return BinaryMathOperation.TRUNCATE.apply(v, 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) { public static Integer ascii(String s) {
return (Integer) StringOperation.ASCII.apply(s); return (Integer) StringOperation.ASCII.apply(s);
} }
@ -58,43 +284,43 @@ public final class InternalSqlScriptUtils {
public static Integer bitLength(String s) { public static Integer bitLength(String s) {
return (Integer) StringOperation.BIT_LENGTH.apply(s); return (Integer) StringOperation.BIT_LENGTH.apply(s);
} }
public static String character(Number n) { public static String character(Number n) {
return (String) StringOperation.CHAR.apply(n); return (String) StringOperation.CHAR.apply(n);
} }
public static Integer charLength(String s) { public static Integer charLength(String s) {
return (Integer) StringOperation.CHAR_LENGTH.apply(s); return (Integer) StringOperation.CHAR_LENGTH.apply(s);
} }
public static String concat(String s1, String s2) { 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) { public static String insert(String s, Number start, Number length, String r) {
return InsertFunctionProcessor.doProcess(s, start, length, r).toString(); return (String) InsertFunctionProcessor.doProcess(s, start, length, r);
} }
public static String lcase(String s) { public static String lcase(String s) {
return (String) StringOperation.LCASE.apply(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); return BinaryStringNumericOperation.LEFT.apply(s, count);
} }
public static Integer length(String s) { public static Integer length(String s) {
return (Integer) StringOperation.LENGTH.apply(s); return (Integer) StringOperation.LENGTH.apply(s);
} }
public static Integer locate(String s1, String s2) { public static Integer locate(String s1, String s2) {
return locate(s1, s2, null); return locate(s1, s2, null);
} }
public static Integer locate(String s1, String s2, Integer pos) { public static Integer locate(String s1, String s2, Number pos) {
return (Integer) LocateFunctionProcessor.doProcess(s1, s2, pos); return LocateFunctionProcessor.doProcess(s1, s2, pos);
} }
public static String ltrim(String s) { public static String ltrim(String s) {
return (String) StringOperation.LTRIM.apply(s); return (String) StringOperation.LTRIM.apply(s);
} }
@ -102,35 +328,35 @@ public final class InternalSqlScriptUtils {
public static Integer octetLength(String s) { public static Integer octetLength(String s) {
return (Integer) StringOperation.OCTET_LENGTH.apply(s); return (Integer) StringOperation.OCTET_LENGTH.apply(s);
} }
public static Integer position(String s1, String s2) { public static Integer position(String s1, String s2) {
return (Integer) BinaryStringStringOperation.POSITION.apply(s1, 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); return BinaryStringNumericOperation.REPEAT.apply(s, count);
} }
public static String replace(String s1, String s2, String s3) { 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); return BinaryStringNumericOperation.RIGHT.apply(s, count);
} }
public static String rtrim(String s) { public static String rtrim(String s) {
return (String) StringOperation.RTRIM.apply(s); return (String) StringOperation.RTRIM.apply(s);
} }
public static String space(Number n) { public static String space(Number n) {
return (String) StringOperation.SPACE.apply(n); return (String) StringOperation.SPACE.apply(n);
} }
public static String substring(String s, int start, int length) { public static String substring(String s, Number start, Number length) {
return SubstringFunctionProcessor.doProcess(s, start, length).toString(); return (String) SubstringFunctionProcessor.doProcess(s, start, length);
} }
public static String ucase(String s) { public static String ucase(String s) {
return (String) StringOperation.UCASE.apply(s); return (String) StringOperation.UCASE.apply(s);
} }

View File

@ -35,7 +35,26 @@ public abstract class BinaryProcessor implements Processor {
@Override @Override
public Object process(Object input) { 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() { 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.FieldAttribute;
import org.elasticsearch.xpack.sql.expression.function.aggregate.AggregateFunctionAttribute; 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.ScalarFunctionAttribute;
import org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalSqlScriptUtils;
import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataType;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder; import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
@ -75,6 +74,6 @@ public interface ScriptWeaver {
} }
default String formatTemplate(String template) { 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 * 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). * (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 { public interface Negateable {
BinaryOperator negate(); BinaryOperator<?, ?, ?, ?> negate();
} }
protected BinaryOperator(Location location, Expression left, Expression right, String symbol) { protected BinaryOperator(Location location, Expression left, Expression right, F function) {
super(location, left, right, symbol); super(location, left, right, function);
} }
protected abstract TypeResolution resolveInputType(DataType inputType); protected abstract TypeResolution resolveInputType(DataType inputType);
public abstract BinaryOperator swapLeftAndRight(); public abstract BinaryOperator<T, U, R, F> swapLeftAndRight();
@Override @Override
protected TypeResolution resolveType() { 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 * 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 * Further more they are not registered as the rest of the functions as are implicit
* to the language. * 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 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); super(location, left, right);
this.name = name(left, right, symbol); this.name = name(left, right, function.symbol());
this.symbol = 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 @Override
public int hashCode() { public int hashCode() {
return Objects.hash(left(), right()); return Objects.hash(left(), right(), function.symbol());
} }
@Override @Override
@ -47,13 +58,26 @@ public abstract class BinaryPredicate extends BinaryScalarFunction {
return false; 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(left(), other.left())
&& Objects.equals(right(), other.right()); && 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) { private static String name(Expression left, Expression right, String symbol) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(Expressions.name(left)); sb.append(Expressions.name(left));
@ -72,13 +96,4 @@ public abstract class BinaryPredicate extends BinaryScalarFunction {
} }
return sb.toString(); 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; package org.elasticsearch.xpack.sql.expression.predicate;
import org.elasticsearch.xpack.sql.expression.Expression; 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 org.elasticsearch.xpack.sql.plan.logical.LogicalPlan;
import java.util.ArrayList; import java.util.ArrayList;

View File

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

View File

@ -3,19 +3,18 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with 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.Expression;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator.Negateable; 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.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.tree.NodeInfo;
import java.util.Objects;
public class And extends BinaryLogic implements Negateable { public class And extends BinaryLogic implements Negateable {
public And(Location location, Expression left, Expression right) { public And(Location location, Expression left, Expression right) {
super(location, left, right, "&&"); super(location, left, right, BinaryLogicOperation.AND);
} }
@Override @Override
@ -24,22 +23,17 @@ public class And extends BinaryLogic implements Negateable {
} }
@Override @Override
protected BinaryOperator replaceChildren(Expression newLeft, Expression newRight) { protected And replaceChildren(Expression newLeft, Expression newRight) {
return new And(location(), newLeft, 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 @Override
public And swapLeftAndRight() { public And swapLeftAndRight() {
return new And(location(), right(), left()); 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; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with 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.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.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.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType; 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) { protected BinaryLogic(Location location, Expression left, Expression right, BinaryLogicOperation operation) {
super(location, left, right, symbol); super(location, left, right, operation);
} }
@Override @Override
@ -29,13 +30,8 @@ public abstract class BinaryLogic extends BinaryOperator {
"'%s' requires type %s not %s", symbol(), DataType.BOOLEAN.sqlName(), inputType.sqlName()); "'%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 @Override
protected Pipe makePipe() { 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; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with 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.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Expression;

View File

@ -3,19 +3,18 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with 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.Expression;
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator.Negateable; 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.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.tree.NodeInfo;
import java.util.Objects;
public class Or extends BinaryLogic implements Negateable { public class Or extends BinaryLogic implements Negateable {
public Or(Location location, Expression left, Expression right) { public Or(Location location, Expression left, Expression right) {
super(location, left, right, "||"); super(location, left, right, BinaryLogicOperation.OR);
} }
@Override @Override
@ -24,15 +23,10 @@ public class Or extends BinaryLogic implements Negateable {
} }
@Override @Override
protected BinaryOperator replaceChildren(Expression newLeft, Expression newRight) { protected Or replaceChildren(Expression newLeft, Expression newRight) {
return new Or(location(), newLeft, 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 @Override
public Or swapLeftAndRight() { public Or swapLeftAndRight() {
return new Or(location(), right(), left()); 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.Expression;
import org.elasticsearch.xpack.sql.expression.Expressions; import org.elasticsearch.xpack.sql.expression.Expressions;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe; 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.BinaryOperator;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation; import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation;
import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.type.DataTypeConversion; 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; protected ArithmeticOperation(Location location, Expression left, Expression right, BinaryArithmeticOperation operation) {
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder; super(location, left, right, operation);
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;
} }
@Override @Override
@ -46,26 +37,8 @@ public abstract class ArithmeticOperation extends BinaryOperator {
return DataTypeConversion.commonType(left().dataType(), right().dataType()); 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 @Override
protected Pipe makePipe() { 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; private final BinaryArithmeticOperation operation;
public BinaryArithmeticPipe(Location location, Expression expression, Pipe left, public BinaryArithmeticPipe(Location location, Expression expression, Pipe left, Pipe right, BinaryArithmeticOperation operation) {
Pipe right, BinaryArithmeticOperation operation) {
super(location, expression, left, right); super(location, expression, left, right);
this.operation = operation; this.operation = operation;
} }
@Override @Override
protected NodeInfo<BinaryArithmeticPipe> info() { protected NodeInfo<BinaryArithmeticPipe> info() {
return NodeInfo.create(this, BinaryArithmeticPipe::new, return NodeInfo.create(this, BinaryArithmeticPipe::new, expression(), left(), right(), operation);
expression(), left(), right(), operation);
} }
@Override @Override

View File

@ -6,18 +6,18 @@
package org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic; package org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic;
import org.elasticsearch.common.io.stream.StreamInput; 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.BinaryNumericProcessor; import org.elasticsearch.xpack.sql.expression.gen.processor.FunctionalBinaryProcessor;
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor; 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 org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation;
import java.io.IOException; import java.io.IOException;
import java.util.function.BiFunction; 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, "+"), ADD(Arithmetics::add, "+"),
SUB(Arithmetics::sub, "-"), SUB(Arithmetics::sub, "-"),
MUL(Arithmetics::mul, "*"), MUL(Arithmetics::mul, "*"),
@ -32,12 +32,13 @@ public class BinaryArithmeticProcessor extends BinaryNumericProcessor<BinaryArit
this.symbol = symbol; this.symbol = symbol;
} }
@Override
public String symbol() { public String symbol() {
return symbol; return symbol;
} }
@Override @Override
public final Number apply(Number left, Number right) { public final Number doApply(Number left, Number right) {
return process.apply(left, right); return process.apply(left, right);
} }
@ -57,13 +58,15 @@ public class BinaryArithmeticProcessor extends BinaryNumericProcessor<BinaryArit
super(in, i -> i.readEnum(BinaryArithmeticOperation.class)); super(in, i -> i.readEnum(BinaryArithmeticOperation.class));
} }
@Override
protected void doWrite(StreamOutput out) throws IOException {
out.writeEnum(operation());
}
@Override @Override
public String getWriteableName() { public String getWriteableName() {
return NAME; 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.Expression;
import org.elasticsearch.xpack.sql.expression.Expressions; import org.elasticsearch.xpack.sql.expression.Expressions;
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe; 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.BinaryOperator;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation; import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.DataType; 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 // 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; protected BinaryComparison(Location location, Expression left, Expression right, BinaryComparisonOperation operation) {
super(location, left, right, operation);
public BinaryComparison(Location location, Expression left, Expression right, BinaryComparisonOperation operation) {
super(location, left, right, operation.symbol());
this.operation = operation;
} }
@Override @Override
@ -34,25 +25,6 @@ public abstract class BinaryComparison extends BinaryOperator {
return TypeResolution.TYPE_RESOLVED; 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 @Override
protected Expression canonicalize() { protected Expression canonicalize() {
return left().hashCode() > right().hashCode() ? swapLeftAndRight() : this; return left().hashCode() > right().hashCode() ? swapLeftAndRight() : this;
@ -63,8 +35,9 @@ public abstract class BinaryComparison extends BinaryOperator {
return DataType.BOOLEAN; return DataType.BOOLEAN;
} }
public static Integer compare(Object left, Object right) { @Override
return Comparisons.compare(left, right); protected Pipe makePipe() {
return new BinaryComparisonPipe(location(), this, Expressions.pipe(left()), Expressions.pipe(right()), function());
} }
@Override @Override
@ -77,4 +50,8 @@ public abstract class BinaryComparison extends BinaryOperator {
sb.append(right()); sb.append(right());
return sb.toString(); 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; private final BinaryComparisonOperation operation;
public BinaryComparisonPipe(Location location, Expression expression, Pipe left, public BinaryComparisonPipe(Location location, Expression expression, Pipe left, Pipe right, BinaryComparisonOperation operation) {
Pipe right, BinaryComparisonOperation operation) {
super(location, expression, left, right); super(location, expression, left, right);
this.operation = operation; this.operation = operation;
} }

View File

@ -6,16 +6,17 @@
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison; package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
import org.elasticsearch.common.io.stream.StreamInput; 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.gen.processor.Processor;
import org.elasticsearch.xpack.sql.expression.predicate.PredicateBiFunction;
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation; import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
import java.io.IOException; import java.io.IOException;
import java.util.function.BiFunction; 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, "=="), EQ(Comparisons::eq, "=="),
GT(Comparisons::gt, ">"), GT(Comparisons::gt, ">"),
@ -31,12 +32,13 @@ public class BinaryComparisonProcessor extends BinaryOperatorProcessor<BinaryCom
this.symbol = symbol; this.symbol = symbol;
} }
@Override
public String symbol() { public String symbol() {
return symbol; return symbol;
} }
@Override @Override
public final Boolean apply(Object left, Object right) { public final Boolean doApply(Object left, Object right) {
return process.apply(left, right); return process.apply(left, right);
} }
@ -56,11 +58,6 @@ public class BinaryComparisonProcessor extends BinaryOperatorProcessor<BinaryCom
super(in, i -> i.readEnum(BinaryComparisonOperation.class)); super(in, i -> i.readEnum(BinaryComparisonOperation.class));
} }
@Override
protected void doWrite(StreamOutput out) throws IOException {
out.writeEnum(operation());
}
@Override @Override
public String getWriteableName() { public String getWriteableName() {
return NAME; 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; 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.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.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.regex.Pattern; public class Like extends RegexMatch {
public class Like extends BinaryPredicate {
public Like(Location location, Expression left, LikePattern right) { public Like(Location location, Expression left, LikePattern right) {
super(location, left, right, "LIKE"); super(location, left, right);
} }
@Override @Override
protected NodeInfo<Like> info() { 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 @Override
protected BinaryPredicate replaceChildren(Expression newLeft, Expression newRight) { protected Like replaceChildren(Expression newLeft, Expression newRight) {
return new Like(location(), newLeft, (LikePattern) newRight); return new Like(location(), newLeft, (LikePattern) newRight);
} }
@Override @Override
public LikePattern right() { protected String asString(Expression pattern) {
return (LikePattern) super.right(); return ((LikePattern) pattern).asJavaRegex();
}
@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");
} }
} }

View File

@ -5,57 +5,29 @@
*/ */
package org.elasticsearch.xpack.sql.expression.predicate.regex; 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.Expression;
import org.elasticsearch.xpack.sql.expression.Literal; 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.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType;
import java.util.regex.Pattern; public class RLike extends RegexMatch {
public class RLike extends BinaryPredicate {
public RLike(Location location, Expression left, Literal right) { public RLike(Location location, Expression left, Literal right) {
super(location, left, right, "RLIKE"); super(location, left, right);
} }
@Override @Override
protected NodeInfo<RLike> info() { protected NodeInfo<RLike> info() {
return NodeInfo.create(this, RLike::new, left(), right()); return NodeInfo.create(this, RLike::new, left(), (Literal) right());
} }
@Override @Override
protected BinaryPredicate replaceChildren(Expression newLeft, Expression newRight) { protected RLike replaceChildren(Expression newLeft, Expression newRight) {
return new RLike(location(), newLeft, (Literal) newRight); return new RLike(location(), newLeft, (Literal) newRight);
} }
@Override @Override
public Literal right() { protected String asString(Expression pattern) {
return (Literal) super.right(); return pattern.fold().toString();
}
@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");
} }
} }

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

View File

@ -5,14 +5,7 @@
*/ */
package org.elasticsearch.xpack.sql.querydsl.agg; package org.elasticsearch.xpack.sql.querydsl.agg;
import org.elasticsearch.xpack.sql.expression.gen.script.Params; import org.elasticsearch.xpack.sql.expression.gen.script.Scripts;
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;
public class AndAggFilter extends AggFilter { public class AndAggFilter extends AggFilter {
@ -21,12 +14,6 @@ public class AndAggFilter extends AggFilter {
} }
public AndAggFilter(String name, AggFilter left, AggFilter right) { public AndAggFilter(String name, AggFilter left, AggFilter right) {
super(name, and(left.scriptTemplate(), right.scriptTemplate())); super(name, Scripts.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);
} }
} }

View File

@ -5,14 +5,7 @@
*/ */
package org.elasticsearch.xpack.sql.querydsl.agg; package org.elasticsearch.xpack.sql.querydsl.agg;
import org.elasticsearch.xpack.sql.expression.gen.script.Params; import org.elasticsearch.xpack.sql.expression.gen.script.Scripts;
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;
public class OrAggFilter extends AggFilter { public class OrAggFilter extends AggFilter {
@ -21,12 +14,6 @@ public class OrAggFilter extends AggFilter {
} }
public OrAggFilter(String name, AggFilter left, AggFilter right) { public OrAggFilter(String name, AggFilter left, AggFilter right) {
super(name, and(left.scriptTemplate(), right.scriptTemplate())); super(name, Scripts.or(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);
} }
} }

View File

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

View File

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

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.sql.querydsl.container; package org.elasticsearch.xpack.sql.querydsl.container;
import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.xpack.sql.expression.Order.NullsPosition;
import org.elasticsearch.xpack.sql.expression.Order.OrderDirection; import org.elasticsearch.xpack.sql.expression.Order.OrderDirection;
public class Sort { 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.direction = direction;
this.missing = nulls;
} }
public Direction direction() { public Direction direction() {
return direction; return direction;
} }
public Missing missing() {
return missing;
}
} }

View File

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

View File

@ -24,7 +24,17 @@ public class ReflectionUtils {
c); 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(); clazz = clazz.getSuperclass();
} }

View File

@ -7,32 +7,101 @@
# This file contains a whitelist for SQL specific utilities available inside SQL scripting # This file contains a whitelist for SQL specific utilities available inside SQL scripting
class org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalSqlScriptUtils { class org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalSqlScriptUtils {
Integer dateTimeChrono(long, String, String) #
String dayName(long, String) # Utilities
String monthName(long, String) #
Integer quarter(long, String) 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 round(Number, Number)
Number truncate(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 ascii(String)
Integer bitLength(String) Integer bitLength(String)
String character(Number) String character(Number)
Integer charLength(String) Integer charLength(String)
String concat(String, String) String concat(String, String)
String insert(String, int, int, String) String insert(String, Number, Number, String)
String lcase(String) String lcase(String)
String left(String, int) String left(String, Number)
Integer length(String) Integer length(String)
Integer locate(String, String) Integer locate(String, String)
Integer locate(String, String, Integer) Integer locate(String, String, Number)
String ltrim(String) String ltrim(String)
Integer octetLength(String) Integer octetLength(String)
Integer position(String, String) Integer position(String, String)
String repeat(String, int) String repeat(String, Number)
String replace(String, String, String) String replace(String, String, String)
String right(String, int) String right(String, Number)
String rtrim(String) String rtrim(String)
String space(Number) String space(Number)
String substring(String, int, int) String substring(String, Number, Number)
String ucase(String) 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.AggregatorFactories.Builder;
import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.expression.FieldAttribute; 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.QueryContainer;
import org.elasticsearch.xpack.sql.querydsl.container.ScoreSort; 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.Direction;
import org.elasticsearch.xpack.sql.querydsl.container.Sort.Missing;
import org.elasticsearch.xpack.sql.querydsl.query.MatchQuery; import org.elasticsearch.xpack.sql.querydsl.query.MatchQuery;
import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.type.KeywordEsField; import org.elasticsearch.xpack.sql.type.KeywordEsField;
@ -84,21 +86,25 @@ public class SourceGeneratorTests extends ESTestCase {
public void testSortScoreSpecified() { public void testSortScoreSpecified() {
QueryContainer container = new QueryContainer() QueryContainer container = new QueryContainer()
.sort(new ScoreSort(Direction.DESC)); .sort(new ScoreSort(Direction.DESC, null));
SearchSourceBuilder sourceBuilder = SourceGenerator.sourceBuilder(container, null, randomIntBetween(1, 10)); SearchSourceBuilder sourceBuilder = SourceGenerator.sourceBuilder(container, null, randomIntBetween(1, 10));
assertEquals(singletonList(scoreSort()), sourceBuilder.sorts()); assertEquals(singletonList(scoreSort()), sourceBuilder.sorts());
} }
public void testSortFieldSpecified() { public void testSortFieldSpecified() {
FieldSortBuilder sortField = fieldSort("test").unmappedType("keyword");
QueryContainer container = new QueryContainer() 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)); 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() 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)); 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() { 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.function.scalar.Processors;
import org.elasticsearch.xpack.sql.expression.gen.processor.ConstantProcessor; 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.expression.function.scalar.FunctionTestUtils.l;
import static org.elasticsearch.xpack.sql.tree.Location.EMPTY;
public class ConcatProcessorTests extends AbstractWireSerializingTestCase<ConcatFunctionProcessor> { public class ConcatProcessorTests extends AbstractWireSerializingTestCase<ConcatFunctionProcessor> {
@Override @Override
protected ConcatFunctionProcessor createTestInstance() { protected ConcatFunctionProcessor createTestInstance() {
return new ConcatFunctionProcessor( return new ConcatFunctionProcessor(
new ConstantProcessor(randomRealisticUnicodeOfLengthBetween(0, 128)), 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() { public void testConcatFunctionWithEdgeCases() {
assertEquals("foo", new Concat(EMPTY, l("foo"), l(null)).makePipe().asProcessor().process(null)); 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)); 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() { 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.Abs;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.E; 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.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.BinaryOperator;
import org.elasticsearch.xpack.sql.expression.predicate.IsNotNull; 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.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.Add;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Div; import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Div;
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Mod; import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Mod;
@ -204,7 +204,7 @@ public class OptimizerTests extends ESTestCase {
// SELECT // SELECT
p = new Project(EMPTY, p, Arrays.asList(a, b)); p = new Project(EMPTY, p, Arrays.asList(a, b));
// ORDER BY // 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); LogicalPlan result = new ReplaceFoldableAttributes().apply(p);
assertNotSame(p, result); assertNotSame(p, result);
@ -319,7 +319,7 @@ public class OptimizerTests extends ESTestCase {
return ((Literal) new ConstantFolding().rule(f)).value(); 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(); 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) { private LikePattern likeEscape(String like, String character) {
Expression exp = parser.createExpression(format(Locale.ROOT, "exp LIKE '%s' {escape '%s'}", like, character)); Expression exp = parser.createExpression(format(Locale.ROOT, "exp LIKE '%s' {escape '%s'}", like, character));
assertThat(exp, instanceOf(Like.class)); assertThat(exp, instanceOf(Like.class));
return ((Like) exp).right(); return ((Like) exp).pattern();
} }
private Function function(String name) { private Function function(String name) {

View File

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

View File

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

View File

@ -74,12 +74,12 @@ public class JdbcDocCsvSpecIT extends SpecBaseIntegrationTestCase {
@Override @Override
protected boolean logEsResultSet() { protected boolean logEsResultSet() {
return false; return true;
} }
@Override @Override
protected final void doTest() throws Throwable { 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) // pass the testName as table for debugging purposes (in case the underlying reader is missing)
ResultSet expected = executeCsvQuery(csv, testName); ResultSet expected = executeCsvQuery(csv, testName);

View File

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

View File

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

View File

@ -6,12 +6,12 @@
package org.elasticsearch.xpack.qa.sql.jdbc; package org.elasticsearch.xpack.qa.sql.jdbc;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.logging.log4j.LogManager;
import org.elasticsearch.client.Request; import org.elasticsearch.client.Request;
import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClient;
import org.elasticsearch.common.CheckedBiConsumer; import org.elasticsearch.common.CheckedBiConsumer;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.common.xcontent.json.JsonXContent;
@ -33,7 +33,7 @@ public class DataLoader {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
try (RestClient client = RestClient.builder(new HttpHost("localhost", 9200)).build()) { try (RestClient client = RestClient.builder(new HttpHost("localhost", 9200)).build()) {
loadEmpDatasetIntoEs(client); 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 { protected static void loadEmpDatasetIntoEs(RestClient client) throws Exception {
loadEmpDatasetIntoEs(client, "test_emp", "employees"); loadEmpDatasetIntoEs(client, "test_emp", "employees");
loadEmpDatasetIntoEs(client, "test_emp_copy", "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", "test_emp", "test_emp_copy");
makeAlias(client, "test_alias_emp", "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("{\"index\":{}}\n");
bulk.append('{'); bulk.append('{');
String emp_no = fields.get(1); String emp_no = fields.get(1);
boolean hadLastItem = false;
for (int f = 0; f < fields.size(); f++) { 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 // 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 (fields.get(f).trim().length() > 0) {
if (f != 0) { if (hadLastItem) {
bulk.append(','); bulk.append(",");
} }
hadLastItem = true;
bulk.append('"').append(titles.get(f)).append("\":\"").append(fields.get(f)).append('"'); 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 @Override
protected final void doTest() throws Throwable { protected final void doTest() throws Throwable {
try (Connection csv = csvConnection(testCase.expectedResults); try (Connection csv = csvConnection(testCase); Connection es = esJdbc()) {
Connection es = esJdbc()) {
// pass the testName as table for debugging purposes (in case the underlying reader is missing) // pass the testName as table for debugging purposes (in case the underlying reader is missing)
ResultSet expected = executeCsvQuery(csv, testName); ResultSet expected = executeCsvQuery(csv, testName);
ResultSet elasticResults = executeJdbcQuery(es, testCase.query); 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.SMALLINT;
import static java.sql.Types.TINYINT; import static java.sql.Types.TINYINT;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@ -177,7 +178,12 @@ public class JdbcAssert {
// handle nulls first // handle nulls first
if (expectedObject == null || actualObject == null) { 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 // then timestamp
else if (type == Types.TIMESTAMP || type == Types.TIMESTAMP_WITH_TIMEZONE) { 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> { 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 { static {
try { try {
// Initialize h2 so we can use it for testing // 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. * Closing the connection will remove the db.
*/ */
public static Connection anonymousDb() throws SQLException { 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(); private static final Properties DEFAULTS = new Properties();
@ -41,19 +57,8 @@ public class LocalH2 extends ExternalResource implements CheckedSupplier<Connect
private CheckedConsumer<Connection, SQLException> initializer; 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) { 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; this.initializer = initializer;
} }
@ -76,4 +81,4 @@ public class LocalH2 extends ExternalResource implements CheckedSupplier<Connect
public Connection get() throws SQLException { public Connection get() throws SQLException {
return DriverManager.getConnection(url, DEFAULTS); return DriverManager.getConnection(url, DEFAULTS);
} }
} }

View File

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

View File

@ -464,7 +464,7 @@ public abstract class RestSqlTestCase extends ESRestTestCase implements ErrorsTe
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, Object> termsScript = (Map<String, Object>) terms.get("script"); Map<String, Object> termsScript = (Map<String, Object>) terms.get("script");
assertEquals(3, termsScript.size()); 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")); assertEquals("painless", termsScript.get("lang"));
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -494,7 +494,7 @@ public abstract class RestSqlTestCase extends ESRestTestCase implements ErrorsTe
} }
} }
Collections.sort(aggKeys); Collections.sort(aggKeys);
assertEquals("having" + aggKeys.get(1), aggFilterKey); assertEquals("having." + aggKeys.get(1), aggFilterKey);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, Object> having = (Map<String, Object>) aggregations2.get(aggFilterKey); Map<String, Object> having = (Map<String, Object>) aggregations2.get(aggFilterKey);
@ -513,7 +513,7 @@ public abstract class RestSqlTestCase extends ESRestTestCase implements ErrorsTe
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, Object> filterScript = (Map<String, Object>) bucketSelector.get("script"); Map<String, Object> filterScript = (Map<String, Object>) bucketSelector.get("script");
assertEquals(3, filterScript.size()); 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")); assertEquals("painless", filterScript.get("lang"));
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, Object> filterScriptParams = (Map<String, Object>) filterScript.get("params"); 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; SELECT gender, PERCENTILE(emp_no, 97) p1 FROM test_emp GROUP BY gender;
gender:s | p1:d gender:s | p1:d
F | 10099.1936 null |10019.0
M | 10095.6112 F |10099.51
M |10095.789999999999
; ;
singlePercentileWithComma singlePercentileWithComma
SELECT gender, PERCENTILE(emp_no, 97.76) p1 FROM test_emp GROUP BY gender; SELECT gender, PERCENTILE(emp_no, 97.76) p1 FROM test_emp GROUP BY gender;
gender:s | p1:d gender:s | p1:d
F | 10099.1936 null |10019.0
M | 10095.6112 F |10099.51
M |10095.789999999999
; ;
multiplePercentilesOneWithCommaOneWithout multiplePercentilesOneWithCommaOneWithout
SELECT gender, PERCENTILE(emp_no, 92.45) p1, PERCENTILE(emp_no, 91) p2 FROM test_emp GROUP BY gender; 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 gender:s | p1:d | p2:d
F | 10096.826000000001 | 10094.68 null |10018.745 |10018.599999999999
M | 10090.319 | 10089.320000000002 F |10098.0085 |10096.119999999999
M |10091.393 |10090.37
; ;
multiplePercentilesWithoutComma multiplePercentilesWithoutComma
SELECT gender, PERCENTILE(emp_no, 91) p1, PERCENTILE(emp_no, 89) p2 FROM test_emp GROUP BY gender; SELECT gender, PERCENTILE(emp_no, 91) p1, PERCENTILE(emp_no, 89) p2 FROM test_emp GROUP BY gender;
gender:s | p1:d | p2:d gender:s | p1:d | p2:d
F | 10094.68 | 10092.08 null |10018.599999999999 |10018.4
M | 10089.320000000002 | 10085.18 F |10096.119999999999 |10093.74
M |10090.37 |10086.92
; ;
multiplePercentilesWithComma multiplePercentilesWithComma
SELECT gender, PERCENTILE(emp_no, 85.7) p1, PERCENTILE(emp_no, 94.3) p2 FROM test_emp GROUP BY gender; 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 gender:s | p1:d | p2:d
F | 10088.852 | 10097.792 null |10018.070000000002 |10018.929999999998
M | 10083.134 | 10091.932 F |10091.343 |10098.619
M |10084.349 |10093.502
; ;
percentileRank percentileRank
SELECT gender, PERCENTILE_RANK(emp_no, 10025) rank FROM test_emp GROUP BY gender; SELECT gender, PERCENTILE_RANK(emp_no, 10025) rank FROM test_emp GROUP BY gender;
gender:s | rank:d gender:s | rank:d
F | 26.351351351351347 null |100.0
M | 23.41269841269841 F |17.424242424242426
M |15.350877192982457
; ;
multiplePercentileRanks multiplePercentileRanks
SELECT gender, PERCENTILE_RANK(emp_no, 10030.0) rank1, PERCENTILE_RANK(emp_no, 10025) rank2 FROM test_emp GROUP BY gender; 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 gender:s | rank1:d | rank2:d
F | 29.93762993762994 | 26.351351351351347 null |100.0 |100.0
M | 29.365079365079367 | 23.41269841269841 F |21.445221445221442 |17.424242424242426
M |21.929824561403507 |15.350877192982457
; ;
multiplePercentilesAndPercentileRank 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; 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 gender:s | p1:d | p2:d | rank:d
F | 10099.1936 | 10098.021 | 26.351351351351347 null |10019.0 |10018.83 |100.0
M | 10095.6112 | 10090.846 | 23.41269841269841 F |10099.7608 |10098.289 |17.424242424242426
M |10096.2232 |10092.362 |15.350877192982457
; ;
sum 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 gender:s | k:d | s:d
F | 1.8427808415250482 | 0.04517149340491813 null |2.2215791166941923 |-0.03373126000214023
M | 2.259327644285826 | 0.40268950715550333 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