SQL: Wrap ZonedDateTime parameters inside scripts (#39911)

Painless allows ZonedDateTime objects to be passed natively to scripts
which creates problematic translate queries as the ZonedDateTime is
passed as a string instead.
Wrap this with a dedicated method to perform the conversion.

Fix #39877

(cherry picked from commit 4957cad5bda77257d10430ac102e93f5e062148a)
This commit is contained in:
Costin Leau 2019-03-11 17:13:25 +02:00 committed by Costin Leau
parent b7be724e50
commit 92a87a45bf
3 changed files with 33 additions and 2 deletions

View File

@ -348,7 +348,7 @@ public final class InternalSqlScriptUtils {
public static ZonedDateTime asDateTime(Object dateTime) {
return (ZonedDateTime) asDateTime(dateTime, false);
}
private static Object asDateTime(Object dateTime, boolean lenient) {
if (dateTime == null) {
return null;
@ -363,7 +363,10 @@ public final class InternalSqlScriptUtils {
if (dateTime instanceof Number) {
return DateUtils.asDateTime(((Number) dateTime).longValue());
}
if (dateTime instanceof String) {
return DateUtils.asDateTime(dateTime.toString());
}
throw new SqlIllegalArgumentException("Invalid date encountered [{}]", dateTime);
}
return dateTime;

View File

@ -17,6 +17,9 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunctionAttr
import org.elasticsearch.xpack.sql.expression.literal.IntervalDayTime;
import org.elasticsearch.xpack.sql.expression.literal.IntervalYearMonth;
import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.util.DateUtils;
import java.time.ZonedDateTime;
import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder;
@ -52,7 +55,18 @@ public interface ScriptWeaver {
default ScriptTemplate scriptWithFoldable(Expression foldable) {
Object fold = foldable.fold();
//
// Custom type handling
//
// wrap intervals with dedicated methods for serialization
if (fold instanceof ZonedDateTime) {
ZonedDateTime zdt = (ZonedDateTime) fold;
return new ScriptTemplate(processScript("{sql}.asDateTime({})"),
paramsBuilder().variable(DateUtils.toString(zdt)).build(), dataType());
}
if (fold instanceof IntervalYearMonth) {
IntervalYearMonth iym = (IntervalYearMonth) fold;
return new ScriptTemplate(processScript("{sql}.intervalYearMonth({},{})"),

View File

@ -59,6 +59,7 @@ import java.util.stream.Stream;
import static org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation.E;
import static org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation.PI;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.startsWith;
@ -728,4 +729,17 @@ public class QueryTranslatorTests extends ESTestCase {
EsQueryExec eqe = (EsQueryExec) p;
assertFalse("Should NOT be tracking hits", eqe.queryContainer().shouldTrackHits());
}
public void testZonedDateTimeInScripts() throws Exception {
PhysicalPlan p = optimizeAndPlan(
"SELECT date FROM test WHERE date + INTERVAL 1 YEAR > CAST('2019-03-11T12:34:56.000Z' AS DATETIME)");
assertEquals(EsQueryExec.class, p.getClass());
EsQueryExec eqe = (EsQueryExec) p;
assertThat(eqe.queryContainer().toString().replaceAll("\\s+", ""), containsString(
"\"script\":{\"script\":{\"source\":\"InternalSqlScriptUtils.nullSafeFilter("
+ "InternalSqlScriptUtils.gt(InternalSqlScriptUtils.add(InternalSqlScriptUtils.docValue(doc,params.v0),"
+ "InternalSqlScriptUtils.intervalYearMonth(params.v1,params.v2)),InternalSqlScriptUtils.asDateTime(params.v3)))\","
+ "\"lang\":\"painless\","
+ "\"params\":{\"v0\":\"date\",\"v1\":\"P1Y\",\"v2\":\"INTERVAL_YEAR\",\"v3\":\"2019-03-11T12:34:56.000Z\"}},"));
}
}