diff --git a/core/src/main/java/io/questdb/griffin/SqlCodeGenerator.java b/core/src/main/java/io/questdb/griffin/SqlCodeGenerator.java index 3dc164cbd8b77a4120a9374c0152c7e41c90bec5..09fa8d0ea6ca91ff96582973612713ca9500b2a3 100644 --- a/core/src/main/java/io/questdb/griffin/SqlCodeGenerator.java +++ b/core/src/main/java/io/questdb/griffin/SqlCodeGenerator.java @@ -54,6 +54,7 @@ import org.jetbrains.annotations.Nullable; import static io.questdb.griffin.SqlKeywords.*; import static io.questdb.griffin.model.ExpressionNode.FUNCTION; import static io.questdb.griffin.model.ExpressionNode.LITERAL; +import static io.questdb.griffin.model.QueryModel.*; public class SqlCodeGenerator implements Mutable { public static final int GKK_VANILLA_INT = 0; @@ -61,7 +62,7 @@ public class SqlCodeGenerator implements Mutable { private static final IntHashSet limitTypes = new IntHashSet(); private static final FullFatJoinGenerator CREATE_FULL_FAT_LT_JOIN = SqlCodeGenerator::createFullFatLtJoin; private static final FullFatJoinGenerator CREATE_FULL_FAT_AS_OF_JOIN = SqlCodeGenerator::createFullFatAsOfJoin; - private static final boolean[] joinsRequiringTimestamp = {false, false, false, true, true, false, true}; + private static final boolean[] joinsRequiringTimestamp = new boolean[JOIN_MAX + 1]; private static final IntObjHashMap sumConstructors = new IntObjHashMap<>(); private static final IntObjHashMap ksumConstructors = new IntObjHashMap<>(); private static final IntObjHashMap nsumConstructors = new IntObjHashMap<>(); @@ -102,6 +103,15 @@ public class SqlCodeGenerator implements Mutable { private boolean fullFatJoins = false; private final CharSequenceHashSet prefixes = new CharSequenceHashSet(); + static { + joinsRequiringTimestamp[JOIN_INNER] = false; + joinsRequiringTimestamp[JOIN_OUTER] = false; + joinsRequiringTimestamp[JOIN_CROSS] = false; + joinsRequiringTimestamp[JOIN_ASOF] = true; + joinsRequiringTimestamp[JOIN_SPLICE] = true; + joinsRequiringTimestamp[JOIN_LT] = true; + } + public SqlCodeGenerator( CairoEngine engine, CairoConfiguration configuration, @@ -508,7 +518,7 @@ public class SqlCodeGenerator implements Mutable { valueTypes.add(ColumnType.LONG); if (slave.recordCursorSupportsRandomAccess() && !fullFatJoins) { - if (joinType == QueryModel.JOIN_INNER) { + if (joinType == JOIN_INNER) { return new HashJoinLightRecordCursorFactory( configuration, metadata, @@ -543,7 +553,7 @@ public class SqlCodeGenerator implements Mutable { false ); - if (joinType == QueryModel.JOIN_INNER) { + if (joinType == JOIN_INNER) { return new HashJoinRecordCursorFactory( configuration, metadata, @@ -708,15 +718,19 @@ public class SqlCodeGenerator implements Mutable { try { int n = ordered.size(); - assert n > 0; + assert n > 1; for (int i = 0; i < n; i++) { int index = ordered.getQuick(i); QueryModel slaveModel = joinModels.getQuick(index); - boolean pop = false; - if (master != null) { + if (i > 0) { executionContext.pushTimestampRequiredFlag(joinsRequiringTimestamp[slaveModel.getJoinType()]); - pop = true; + } else { // i == 0 + // This is first model in the sequence of joins + // TS requirement is symmetrical on both right and left sides + // check if next join requires a timestamp + int nextJointType = joinModels.getQuick(ordered.getQuick(1)).getJoinType(); + executionContext.pushTimestampRequiredFlag(joinsRequiringTimestamp[nextJointType]); } try { @@ -739,7 +753,7 @@ public class SqlCodeGenerator implements Mutable { final RecordMetadata slaveMetadata = slave.getMetadata(); switch (joinType) { - case QueryModel.JOIN_CROSS: + case JOIN_CROSS: master = new CrossJoinRecordCursorFactory( createJoinMetadata(masterAlias, masterMetadata, slaveModel.getName(), slaveMetadata), master, @@ -748,7 +762,7 @@ public class SqlCodeGenerator implements Mutable { ); masterAlias = null; break; - case QueryModel.JOIN_ASOF: + case JOIN_ASOF: validateBothTimestamps(slaveModel, masterMetadata, slaveMetadata); processJoinContext(index == 1, slaveModel.getContext(), masterMetadata, slaveMetadata); if (slave.recordCursorSupportsRandomAccess() && !fullFatJoins) { @@ -793,7 +807,7 @@ public class SqlCodeGenerator implements Mutable { } masterAlias = null; break; - case QueryModel.JOIN_LT: + case JOIN_LT: validateBothTimestamps(slaveModel, masterMetadata, slaveMetadata); processJoinContext(index == 1, slaveModel.getContext(), masterMetadata, slaveMetadata); if (slave.recordCursorSupportsRandomAccess() && !fullFatJoins) { @@ -838,7 +852,7 @@ public class SqlCodeGenerator implements Mutable { } masterAlias = null; break; - case QueryModel.JOIN_SPLICE: + case JOIN_SPLICE: validateBothTimestamps(slaveModel, masterMetadata, slaveMetadata); processJoinContext(index == 1, slaveModel.getContext(), masterMetadata, slaveMetadata); if (slave.recordCursorSupportsRandomAccess() && master.recordCursorSupportsRandomAccess() && !fullFatJoins) { @@ -878,9 +892,7 @@ public class SqlCodeGenerator implements Mutable { } } } finally { - if (pop) { - executionContext.popTimestampRequiredFlag(); - } + executionContext.popTimestampRequiredFlag(); } // check if there are post-filters diff --git a/core/src/main/java/io/questdb/griffin/model/QueryModel.java b/core/src/main/java/io/questdb/griffin/model/QueryModel.java index a1831e137a55700048c59499667651a4054bd9a1..3593abbe06645d96df91e34fc57d63ac10781ccf 100644 --- a/core/src/main/java/io/questdb/griffin/model/QueryModel.java +++ b/core/src/main/java/io/questdb/griffin/model/QueryModel.java @@ -43,6 +43,7 @@ public class QueryModel implements Mutable, ExecutionModel, AliasTranslator, Sin public static final int JOIN_ASOF = 4; public static final int JOIN_SPLICE = 5; public static final int JOIN_LT = 6; + public static final int JOIN_MAX = JOIN_LT; public static final String SUB_QUERY_ALIAS_PREFIX = "_xQdbA"; public static final int SELECT_MODEL_NONE = 0; public static final int SELECT_MODEL_CHOOSE = 1; diff --git a/core/src/test/java/io/questdb/griffin/AsOfJoinTest.java b/core/src/test/java/io/questdb/griffin/AsOfJoinTest.java index 4286dca169da4ea29cc686db4a2c41b9ab04ebb4..488b4b954eafb6f4ca53bf851d6a54c94da3e122 100644 --- a/core/src/test/java/io/questdb/griffin/AsOfJoinTest.java +++ b/core/src/test/java/io/questdb/griffin/AsOfJoinTest.java @@ -79,6 +79,36 @@ public class AsOfJoinTest extends AbstractGriffinTest { ); } + @Test + public void testAsofJoinDynamicTimestamp() throws Exception { + compiler.compile( + "create table positions2 as (" + + "select x, cast(x * 1000000L as TIMESTAMP) time from long_sequence(10)" + + ") timestamp(time)", sqlExecutionContext); + + assertSql("select t1.time1 + 1 as time, t1.x, t2.x, t1.x - t2.x\n" + + "from \n" + + "(\n" + + " (\n" + + " select time - 1 as time1, x\n" + + " from positions2\n" + + " )\n" + + " timestamp(time1)\n" + + ") t1\n" + + "asof join positions2 t2", + "time\tx\tx1\tcolumn\n" + + "1970-01-01T00:00:01.000000Z\t1\tNaN\tNaN\n" + + "1970-01-01T00:00:02.000000Z\t2\t1\t1\n" + + "1970-01-01T00:00:03.000000Z\t3\t2\t1\n" + + "1970-01-01T00:00:04.000000Z\t4\t3\t1\n" + + "1970-01-01T00:00:05.000000Z\t5\t4\t1\n" + + "1970-01-01T00:00:06.000000Z\t6\t5\t1\n" + + "1970-01-01T00:00:07.000000Z\t7\t6\t1\n" + + "1970-01-01T00:00:08.000000Z\t8\t7\t1\n" + + "1970-01-01T00:00:09.000000Z\t9\t8\t1\n" + + "1970-01-01T00:00:10.000000Z\t10\t9\t1\n"); + } + @Test public void testAsofJoinForSelectWithTimestamps() throws Exception { final String expected = "tag\thi\tlo\tts\tts1\n" +