This is an automated email from the ASF dual-hosted git repository.
cwylie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new 20b60793a79 Vectorized implementation of timestamp_ceil,
timestamp_extract. (#18500)
20b60793a79 is described below
commit 20b60793a7900ce1343e550ebe4190902c6b121c
Author: Gian Merlino <[email protected]>
AuthorDate: Thu Sep 11 02:25:58 2025 -0400
Vectorized implementation of timestamp_ceil, timestamp_extract. (#18500)
---
.../query/expression/TimestampCeilExprMacro.java | 26 ++++
.../expression/TimestampExtractExprMacro.java | 107 ++++++++++----
.../math/expr/VectorExprResultConsistencyTest.java | 17 +++
.../expression/TimestampExtractExprMacroTest.java | 154 +++++++++++++++++++++
.../druid/sql/calcite/CalciteJoinQueryTest.java | 1 -
.../apache/druid/sql/calcite/CalciteQueryTest.java | 8 --
6 files changed, 276 insertions(+), 37 deletions(-)
diff --git
a/processing/src/main/java/org/apache/druid/query/expression/TimestampCeilExprMacro.java
b/processing/src/main/java/org/apache/druid/query/expression/TimestampCeilExprMacro.java
index 3c5102ae7a2..29460529afa 100644
---
a/processing/src/main/java/org/apache/druid/query/expression/TimestampCeilExprMacro.java
+++
b/processing/src/main/java/org/apache/druid/query/expression/TimestampCeilExprMacro.java
@@ -27,6 +27,9 @@ import org.apache.druid.math.expr.ExprEval;
import org.apache.druid.math.expr.ExprMacroTable;
import org.apache.druid.math.expr.ExpressionType;
import org.apache.druid.math.expr.InputBindings;
+import org.apache.druid.math.expr.vector.CastToTypeVectorProcessor;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.LongUnivariateLongFunctionVectorProcessor;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -90,6 +93,29 @@ public class TimestampCeilExprMacro implements
ExprMacroTable.ExprMacro
return ExpressionType.LONG;
}
+ @Override
+ public boolean canVectorize(InputBindingInspector inspector)
+ {
+ return args.get(0).canVectorize(inspector);
+ }
+
+ @Override
+ public <T> ExprVectorProcessor<T>
asVectorProcessor(VectorInputBindingInspector inspector)
+ {
+ final ExprVectorProcessor<?> processor = new
LongUnivariateLongFunctionVectorProcessor(
+
CastToTypeVectorProcessor.cast(args.get(0).asVectorProcessor(inspector),
ExpressionType.LONG),
+ argTime -> {
+ long bucketStartTime = granularity.bucketStart(argTime);
+ if (argTime == bucketStartTime) {
+ return bucketStartTime;
+ }
+ return granularity.increment(bucketStartTime);
+ }
+ );
+
+ return (ExprVectorProcessor<T>) processor;
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/processing/src/main/java/org/apache/druid/query/expression/TimestampExtractExprMacro.java
b/processing/src/main/java/org/apache/druid/query/expression/TimestampExtractExprMacro.java
index 2fac53ec8d5..722f75e51ab 100644
---
a/processing/src/main/java/org/apache/druid/query/expression/TimestampExtractExprMacro.java
+++
b/processing/src/main/java/org/apache/druid/query/expression/TimestampExtractExprMacro.java
@@ -19,12 +19,18 @@
package org.apache.druid.query.expression;
+import org.apache.druid.error.DruidException;
import org.apache.druid.java.util.common.DateTimes;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.math.expr.Expr;
import org.apache.druid.math.expr.ExprEval;
import org.apache.druid.math.expr.ExprMacroTable;
+import org.apache.druid.math.expr.ExprType;
import org.apache.druid.math.expr.ExpressionType;
+import
org.apache.druid.math.expr.vector.DoubleUnivariateLongFunctionVectorProcessor;
+import org.apache.druid.math.expr.vector.ExprVectorProcessor;
+import
org.apache.druid.math.expr.vector.LongUnivariateLongFunctionVectorProcessor;
+import org.joda.time.Chronology;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.chrono.ISOChronology;
@@ -64,47 +70,52 @@ public class TimestampExtractExprMacro implements
ExprMacroTable.ExprMacro
return FN_NAME;
}
- private ExprEval getExprEval(final DateTime dateTime, final Unit unit)
+ private long evalAsLong(final DateTime dateTime, final Unit unit)
{
- long epoch = dateTime.getMillis() / 1000;
switch (unit) {
case EPOCH:
- return ExprEval.of(epoch);
+ return dateTime.getMillis() / 1000;
case MILLISECOND:
- return ExprEval.of(dateTime.millisOfSecond().get());
+ return dateTime.millisOfSecond().get();
case SECOND:
- return ExprEval.of(dateTime.secondOfMinute().get());
+ return dateTime.secondOfMinute().get();
case MINUTE:
- return ExprEval.of(dateTime.minuteOfHour().get());
+ return dateTime.minuteOfHour().get();
case HOUR:
- return ExprEval.of(dateTime.hourOfDay().get());
+ return dateTime.hourOfDay().get();
case DAY:
- return ExprEval.of(dateTime.dayOfMonth().get());
+ return dateTime.dayOfMonth().get();
case DOW:
- return ExprEval.of(dateTime.dayOfWeek().get());
case ISODOW:
- return ExprEval.of(dateTime.dayOfWeek().get());
+ return dateTime.dayOfWeek().get();
case DOY:
- return ExprEval.of(dateTime.dayOfYear().get());
+ return dateTime.dayOfYear().get();
case WEEK:
- return ExprEval.of(dateTime.weekOfWeekyear().get());
+ return dateTime.weekOfWeekyear().get();
case MONTH:
- return ExprEval.of(dateTime.monthOfYear().get());
+ return dateTime.monthOfYear().get();
case QUARTER:
- return ExprEval.of((dateTime.monthOfYear().get() - 1) / 3 + 1);
+ return (dateTime.monthOfYear().get() - 1) / 3 + 1;
case YEAR:
- return ExprEval.of(dateTime.year().get());
case ISOYEAR:
- return ExprEval.of(dateTime.year().get());
+ return dateTime.year().get();
case DECADE:
// The year field divided by 10, See
https://www.postgresql.org/docs/10/functions-datetime.html
- return ExprEval.of(dateTime.year().get() / 10);
+ return dateTime.year().get() / 10;
+ default:
+ throw TimestampExtractExprMacro.this.validationFailed("unhandled
unit[%s]", unit);
+ }
+ }
+
+ private double evalAsDouble(final DateTime dateTime, final Unit unit)
+ {
+ switch (unit) {
case CENTURY:
- return ExprEval.of(Math.ceil((double) dateTime.year().get() / 100));
+ return Math.ceil((double) dateTime.year().get() / 100);
case MILLENNIUM:
// Years in the 1900s are in the second millennium. The third
millennium started January 1, 2001.
// See https://www.postgresql.org/docs/10/functions-datetime.html
- return ExprEval.of(Math.ceil((double) dateTime.year().get() / 1000));
+ return Math.ceil((double) dateTime.year().get() / 1000);
default:
throw TimestampExtractExprMacro.this.validationFailed("unhandled
unit[%s]", unit);
}
@@ -168,13 +179,20 @@ public class TimestampExtractExprMacro implements
ExprMacroTable.ExprMacro
@Override
public ExprEval eval(final ObjectBinding bindings)
{
- Object val = args.get(0).eval(bindings).value();
- if (val == null) {
+ final ExprEval<?> eval = args.get(0).eval(bindings);
+ if (eval.value() == null) {
// Return null if the argument if null.
return ExprEval.of(null);
}
- final DateTime dateTime = new DateTime(val, chronology);
- return getExprEval(dateTime, unit);
+ final DateTime dateTime = new DateTime(eval.value(), chronology);
+ switch (getOutputExpressionType(unit).getType()) {
+ case LONG:
+ return ExprEval.of(evalAsLong(dateTime, unit));
+ case DOUBLE:
+ return ExprEval.of(evalAsDouble(dateTime, unit));
+ default:
+ throw DruidException.defensive("Unexpected type[%s]",
getOutputExpressionType(unit).getType());
+ }
}
@Nullable
@@ -183,6 +201,32 @@ public class TimestampExtractExprMacro implements
ExprMacroTable.ExprMacro
{
return getOutputExpressionType(unit);
}
+
+ @Override
+ public boolean canVectorize(InputBindingInspector inspector)
+ {
+ return args.get(0).canVectorize(inspector);
+ }
+
+ @Override
+ public <T> ExprVectorProcessor<T>
asVectorProcessor(VectorInputBindingInspector inspector)
+ {
+ final ExprVectorProcessor<?> processor;
+
+ if (getOutputExpressionType(unit).is(ExprType.DOUBLE)) {
+ processor = new DoubleUnivariateLongFunctionVectorProcessor(
+ args.get(0).asVectorProcessor(inspector),
+ input -> evalAsDouble(new DateTime(input, chronology), unit)
+ );
+ } else {
+ processor = new LongUnivariateLongFunctionVectorProcessor(
+ args.get(0).asVectorProcessor(inspector),
+ input -> evalAsLong(new DateTime(input, chronology), unit)
+ );
+ }
+
+ return (ExprVectorProcessor<T>) processor;
+ }
}
public class TimestampExtractDynamicExpr extends
ExprMacroTable.BaseScalarMacroFunctionExpr
@@ -199,14 +243,21 @@ public class TimestampExtractExprMacro implements
ExprMacroTable.ExprMacro
@Override
public ExprEval eval(final ObjectBinding bindings)
{
- Object val = args.get(0).eval(bindings).value();
- if (val == null) {
+ final ExprEval<?> eval = args.get(0).eval(bindings);
+ if (eval.value() == null) {
// Return null if the argument if null.
return ExprEval.of(null);
}
- final ISOChronology chronology = computeChronology(args, bindings);
- final DateTime dateTime = new DateTime(val, chronology);
- return getExprEval(dateTime, unit);
+ final Chronology chronology = computeChronology(args, bindings);
+ final DateTime dateTime = new DateTime(eval.value(), chronology);
+ switch (getOutputExpressionType(unit).getType()) {
+ case LONG:
+ return ExprEval.of(evalAsLong(dateTime, unit));
+ case DOUBLE:
+ return ExprEval.of(evalAsDouble(dateTime, unit));
+ default:
+ throw DruidException.defensive("Unexpected type[%s]",
getOutputExpressionType(unit).getType());
+ }
}
@Nullable
diff --git
a/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyTest.java
b/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyTest.java
index 64a955f2e6f..addd0023170 100644
---
a/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyTest.java
+++
b/processing/src/test/java/org/apache/druid/math/expr/VectorExprResultConsistencyTest.java
@@ -29,6 +29,8 @@ import org.apache.druid.math.expr.vector.ExprEvalVector;
import org.apache.druid.math.expr.vector.ExprVectorProcessor;
import org.apache.druid.query.expression.LookupExprMacro;
import org.apache.druid.query.expression.NestedDataExpressions;
+import org.apache.druid.query.expression.TimestampCeilExprMacro;
+import org.apache.druid.query.expression.TimestampExtractExprMacro;
import org.apache.druid.query.expression.TimestampFloorExprMacro;
import org.apache.druid.query.expression.TimestampShiftExprMacro;
import org.apache.druid.query.lookup.LookupExtractorFactoryContainer;
@@ -85,6 +87,8 @@ public class VectorExprResultConsistencyTest extends
InitializedNullHandlingTest
private static final ExprMacroTable MACRO_TABLE = new ExprMacroTable(
ImmutableList.of(
+ new TimestampCeilExprMacro(),
+ new TimestampExtractExprMacro(),
new TimestampFloorExprMacro(),
new TimestampShiftExprMacro(),
new NestedDataExpressions.JsonObjectExprMacro(),
@@ -422,7 +426,20 @@ public class VectorExprResultConsistencyTest extends
InitializedNullHandlingTest
@Test
public void testTimeFunctions()
{
+ testExpression("timestamp_ceil(l1, 'P1D')", types);
+ testExpression("timestamp_ceil(l1, 'PT1H')", types);
+ testExpression("timestamp_floor(l1, 'P1D')", types);
testExpression("timestamp_floor(l1, 'PT1H')", types);
+ testExpression("timestamp_extract(l1, 'MILLENNIUM')", types);
+ testExpression("timestamp_extract(l1, 'CENTURY')", types);
+ testExpression("timestamp_extract(l1, 'YEAR')", types);
+ testExpression("timestamp_extract(l1, 'MONTH')", types);
+ testExpression("timestamp_extract(l1, 'DAY')", types);
+ testExpression("timestamp_extract(l1, 'HOUR')", types);
+ testExpression("timestamp_extract(l1, 'MINUTE')", types);
+ testExpression("timestamp_extract(l1, 'SECOND')", types);
+ testExpression("timestamp_extract(l1, 'MILLISECOND')", types);
+ testExpression("timestamp_extract(l1, 'EPOCH')", types);
testExpression("timestamp_shift(l1, 'P1M', 1)", types);
}
diff --git
a/processing/src/test/java/org/apache/druid/query/expression/TimestampExtractExprMacroTest.java
b/processing/src/test/java/org/apache/druid/query/expression/TimestampExtractExprMacroTest.java
index 94f9e74439e..128a6fd5108 100644
---
a/processing/src/test/java/org/apache/druid/query/expression/TimestampExtractExprMacroTest.java
+++
b/processing/src/test/java/org/apache/druid/query/expression/TimestampExtractExprMacroTest.java
@@ -123,4 +123,158 @@ public class TimestampExtractExprMacroTest
);
Assert.assertEquals(5, expression.eval(bindings).asInt());
}
+
+ @Test
+ public void testApplyExtractEpochShouldExtractTheCorrectEpochSeconds()
+ {
+ Expr expression = target.apply(
+ ImmutableList.of(
+ ExprEval.of("2001-01-01T00:00:00Z").toExpr(),
+
ExprEval.of(TimestampExtractExprMacro.Unit.EPOCH.toString()).toExpr()
+ ));
+ Assert.assertEquals(978307200,
expression.eval(InputBindings.nilBindings()).asInt());
+ }
+
+ @Test
+ public void testApplyExtractMillisecondShouldExtractTheCorrectMillisecond()
+ {
+ Expr expression = target.apply(
+ ImmutableList.of(
+ ExprEval.of("2001-02-16T12:34:56.789Z").toExpr(),
+
ExprEval.of(TimestampExtractExprMacro.Unit.MILLISECOND.toString()).toExpr()
+ ));
+ Assert.assertEquals(789,
expression.eval(InputBindings.nilBindings()).asInt());
+ }
+
+ @Test
+ public void testApplyExtractSecondShouldExtractTheCorrectSecond()
+ {
+ Expr expression = target.apply(
+ ImmutableList.of(
+ ExprEval.of("2001-02-16T12:34:45Z").toExpr(),
+
ExprEval.of(TimestampExtractExprMacro.Unit.SECOND.toString()).toExpr()
+ ));
+ Assert.assertEquals(45,
expression.eval(InputBindings.nilBindings()).asInt());
+ }
+
+ @Test
+ public void testApplyExtractMinuteShouldExtractTheCorrectMinute()
+ {
+ Expr expression = target.apply(
+ ImmutableList.of(
+ ExprEval.of("2001-02-16T12:34:56Z").toExpr(),
+
ExprEval.of(TimestampExtractExprMacro.Unit.MINUTE.toString()).toExpr()
+ ));
+ Assert.assertEquals(34,
expression.eval(InputBindings.nilBindings()).asInt());
+ }
+
+ @Test
+ public void testApplyExtractHourShouldExtractTheCorrectHour()
+ {
+ Expr expression = target.apply(
+ ImmutableList.of(
+ ExprEval.of("2001-02-16T12:34:56Z").toExpr(),
+
ExprEval.of(TimestampExtractExprMacro.Unit.HOUR.toString()).toExpr()
+ ));
+ Assert.assertEquals(12,
expression.eval(InputBindings.nilBindings()).asInt());
+ }
+
+ @Test
+ public void testApplyExtractDayShouldExtractTheCorrectDay()
+ {
+ Expr expression = target.apply(
+ ImmutableList.of(
+ ExprEval.of("2001-02-16T12:34:56Z").toExpr(),
+ ExprEval.of(TimestampExtractExprMacro.Unit.DAY.toString()).toExpr()
+ ));
+ Assert.assertEquals(16,
expression.eval(InputBindings.nilBindings()).asInt());
+ }
+
+ @Test
+ public void testApplyExtractIsodowShouldExtractTheCorrectIsoDayOfWeek()
+ {
+ Expr expression = target.apply(
+ ImmutableList.of(
+ ExprEval.of("2023-12-15").toExpr(),
+
ExprEval.of(TimestampExtractExprMacro.Unit.ISODOW.toString()).toExpr()
+ ));
+ Assert.assertEquals(5,
expression.eval(InputBindings.nilBindings()).asInt());
+ }
+
+ @Test
+ public void testApplyExtractDoyShouldExtractTheCorrectDayOfYear()
+ {
+ Expr expression = target.apply(
+ ImmutableList.of(
+ ExprEval.of("2001-02-16").toExpr(),
+ ExprEval.of(TimestampExtractExprMacro.Unit.DOY.toString()).toExpr()
+ ));
+ Assert.assertEquals(47,
expression.eval(InputBindings.nilBindings()).asInt());
+ }
+
+ @Test
+ public void testApplyExtractWeekShouldExtractTheCorrectWeek()
+ {
+ Expr expression = target.apply(
+ ImmutableList.of(
+ ExprEval.of("2001-02-16").toExpr(),
+
ExprEval.of(TimestampExtractExprMacro.Unit.WEEK.toString()).toExpr()
+ ));
+ Assert.assertEquals(7,
expression.eval(InputBindings.nilBindings()).asInt());
+ }
+
+ @Test
+ public void testApplyExtractMonthShouldExtractTheCorrectMonth()
+ {
+ Expr expression = target.apply(
+ ImmutableList.of(
+ ExprEval.of("2001-02-16").toExpr(),
+
ExprEval.of(TimestampExtractExprMacro.Unit.MONTH.toString()).toExpr()
+ ));
+ Assert.assertEquals(2,
expression.eval(InputBindings.nilBindings()).asInt());
+ }
+
+ @Test
+ public void testApplyExtractQuarterShouldExtractTheCorrectQuarter()
+ {
+ Expr expression = target.apply(
+ ImmutableList.of(
+ ExprEval.of("2001-02-16").toExpr(),
+
ExprEval.of(TimestampExtractExprMacro.Unit.QUARTER.toString()).toExpr()
+ ));
+ Assert.assertEquals(1,
expression.eval(InputBindings.nilBindings()).asInt());
+ }
+
+ @Test
+ public void
testApplyExtractQuarterSecondQuarterShouldExtractTheCorrectQuarter()
+ {
+ Expr expression = target.apply(
+ ImmutableList.of(
+ ExprEval.of("2001-05-16").toExpr(),
+
ExprEval.of(TimestampExtractExprMacro.Unit.QUARTER.toString()).toExpr()
+ ));
+ Assert.assertEquals(2,
expression.eval(InputBindings.nilBindings()).asInt());
+ }
+
+ @Test
+ public void testApplyExtractYearShouldExtractTheCorrectYear()
+ {
+ Expr expression = target.apply(
+ ImmutableList.of(
+ ExprEval.of("2001-02-16").toExpr(),
+
ExprEval.of(TimestampExtractExprMacro.Unit.YEAR.toString()).toExpr()
+ ));
+ Assert.assertEquals(2001,
expression.eval(InputBindings.nilBindings()).asInt());
+ }
+
+ @Test
+ public void testApplyExtractIsoYearShouldExtractTheCorrectIsoYear()
+ {
+ Expr expression = target.apply(
+ ImmutableList.of(
+ ExprEval.of("2001-02-16").toExpr(),
+
ExprEval.of(TimestampExtractExprMacro.Unit.ISOYEAR.toString()).toExpr()
+ ));
+ Assert.assertEquals(2001,
expression.eval(InputBindings.nilBindings()).asInt());
+ }
}
diff --git
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteJoinQueryTest.java
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteJoinQueryTest.java
index 650135ef43d..b683b8bce91 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteJoinQueryTest.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteJoinQueryTest.java
@@ -4210,7 +4210,6 @@ public class CalciteJoinQueryTest extends
BaseCalciteQueryTest
@Test
public void testSemiJoinWithOuterTimeExtractAggregateWithOrderBy()
{
- cannotVectorizeUnlessFallback();
testQuery(
"SELECT COUNT(DISTINCT dim1), EXTRACT(MONTH FROM __time) FROM
druid.foo\n"
+ " WHERE dim2 IN (\n"
diff --git
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
index f749ad0cc52..8b92f9eddca 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
@@ -8284,7 +8284,6 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
@Test
public void testSillyQuarters()
{
- cannotVectorizeUnlessFallback();
// Like FLOOR(__time TO QUARTER) but silly.
testQuery(
"SELECT CAST((EXTRACT(MONTH FROM __time) - 1 ) / 3 + 1 AS INTEGER) AS
quarter, COUNT(*)\n"
@@ -8884,7 +8883,6 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
@Test
public void testFilterOnTimeExtract()
{
- cannotVectorizeUnlessFallback();
testQuery(
"SELECT COUNT(*) FROM druid.foo\n"
+ "WHERE EXTRACT(YEAR FROM __time) = 2000\n"
@@ -8917,7 +8915,6 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
@Test
public void testFilterOnTimeExtractWithMultipleDays()
{
- cannotVectorizeUnlessFallback();
testQuery(
"SELECT COUNT(*) FROM druid.foo\n"
+ "WHERE EXTRACT(YEAR FROM __time) = 2000\n"
@@ -8958,7 +8955,6 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
@Test
public void testFilterOnTimeExtractWithVariousTimeUnits()
{
- cannotVectorizeUnlessFallback();
msqIncompatible();
testQuery(
"SELECT COUNT(*) FROM druid.foo4\n"
@@ -10654,7 +10650,6 @@ public class CalciteQueryTest extends
BaseCalciteQueryTest
@Test
public void testGroupByExtractYear()
{
- cannotVectorizeUnlessFallback();
testQuery(
"SELECT\n"
+ " EXTRACT(YEAR FROM __time) AS \"year\",\n"
@@ -10752,7 +10747,6 @@ public class CalciteQueryTest extends
BaseCalciteQueryTest
@Test
public void testGroupByExtractFloorTime()
{
- cannotVectorizeUnlessFallback();
testQuery(
"SELECT\n"
+ "EXTRACT(YEAR FROM FLOOR(__time TO YEAR)) AS \"year\", SUM(cnt)\n"
@@ -10785,7 +10779,6 @@ public class CalciteQueryTest extends
BaseCalciteQueryTest
@Test
public void testGroupByExtractFloorTimeLosAngeles()
{
- cannotVectorizeUnlessFallback();
testQuery(
PLANNER_CONFIG_DEFAULT,
QUERY_CONTEXT_LOS_ANGELES,
@@ -16011,7 +16004,6 @@ public class CalciteQueryTest extends
BaseCalciteQueryTest
@Test
public void testMultiStatementSetsContextTimezone()
{
- cannotVectorizeUnlessFallback();
testBuilder().sql(
"SET sqlTimeZone = 'America/Los_Angeles';\n"
+ "SELECT\n"
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]