From cf4f21fb1280e10981b8b7c3f69da188035a6f55 Mon Sep 17 00:00:00 2001
From: MT <12283268+thoniTUB@users.noreply.github.com>
Date: Wed, 26 Aug 2026 16:32:51 +0200
Subject: [PATCH 01/81] feat(sql): introduce resolved query model
Co-authored-by: Codex GPT-5
---
pom.xml | 1 +
sql-connector/README.md | 22 ++++
sql-connector/pom.xml | 24 ++++
.../bakdata/conquery/sql/query/AndNode.java | 14 +++
.../conquery/sql/query/ColumnType.java | 13 +++
.../conquery/sql/query/ConceptNode.java | 21 ++++
.../sql/query/DateAggregationAction.java | 9 ++
.../bakdata/conquery/sql/query/DateRange.java | 22 ++++
.../sql/query/DateRestrictionNode.java | 12 ++
.../conquery/sql/query/EntitySchema.java | 15 +++
.../conquery/sql/query/ExecutionTarget.java | 19 +++
.../conquery/sql/query/ModelValidation.java | 26 +++++
.../conquery/sql/query/NegationNode.java | 11 ++
.../bakdata/conquery/sql/query/OrNode.java | 15 +++
.../bakdata/conquery/sql/query/QueryNode.java | 5 +
.../conquery/sql/query/ResolvedColumn.java | 20 ++++
.../conquery/sql/query/ResolvedCondition.java | 5 +
.../conquery/sql/query/ResolvedConnector.java | 52 +++++++++
.../conquery/sql/query/ResolvedFilter.java | 10 ++
.../conquery/sql/query/ResolvedQuery.java | 25 ++++
.../conquery/sql/query/ResolvedSelect.java | 9 ++
.../sql/query/ResolvedValidityDate.java | 33 ++++++
.../conquery/sql/query/ResultColumn.java | 24 ++++
.../conquery/sql/query/ResultType.java | 23 ++++
.../bakdata/conquery/sql/query/SqlTable.java | 19 +++
.../conquery/sql/query/package-info.java | 8 ++
.../conquery/sql/query/ResolvedQueryTest.java | 110 ++++++++++++++++++
27 files changed, 567 insertions(+)
create mode 100644 sql-connector/README.md
create mode 100644 sql-connector/pom.xml
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/AndNode.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ColumnType.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ConceptNode.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateAggregationAction.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRange.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRestrictionNode.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/EntitySchema.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ExecutionTarget.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ModelValidation.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/NegationNode.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/OrNode.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/QueryNode.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedColumn.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedCondition.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedConnector.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedFilter.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedQuery.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedSelect.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedValidityDate.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultColumn.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultType.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/SqlTable.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/package-info.java
create mode 100644 sql-connector/src/test/java/com/bakdata/conquery/sql/query/ResolvedQueryTest.java
diff --git a/pom.xml b/pom.xml
index 860dda8e2f..1fb3cca8fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,6 +29,7 @@
+ sql-connector
backend
executable
autodoc
diff --git a/sql-connector/README.md b/sql-connector/README.md
new file mode 100644
index 0000000000..7222983e10
--- /dev/null
+++ b/sql-connector/README.md
@@ -0,0 +1,22 @@
+# SQL connector
+
+This module contains the framework-neutral contract between query resolution and SQL compilation.
+
+## Resolved query model
+
+`ResolvedQuery` is an in-memory execution model. A producer must resolve and validate a request against one dataset
+catalog snapshot before constructing it. In particular:
+
+- logical references are replaced with physical tables and typed columns;
+- saved or reusable query references are expanded;
+- defaults and date-aggregation actions are materialized;
+- filter values, selects, conditions, validity dates, and secondary IDs are validated;
+- result columns are ordered and their presentation metadata is finalized.
+
+The SQL compiler may perform dialect capability checks, but it must not access a dataset repository or resolve logical
+identifiers. Connections, dialect implementations, compiler state, and execution services are intentionally not part of
+the resolved model.
+
+`ResolvedFilter`, `ResolvedSelect`, and `ResolvedCondition` are open extension points. Implementations must be immutable
+and carry resolved columns and typed values. They must not carry unresolved repository identifiers or arbitrary SQL
+received from a query request.
diff --git a/sql-connector/pom.xml b/sql-connector/pom.xml
new file mode 100644
index 0000000000..f06f2c1c34
--- /dev/null
+++ b/sql-connector/pom.xml
@@ -0,0 +1,24 @@
+
+
+ 4.0.0
+
+
+ com.bakdata.conquery
+ parent
+ ${revision}
+ ../pom.xml
+
+
+ sql-connector
+ Conquery SQL Connector
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/AndNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/AndNode.java
new file mode 100644
index 0000000000..10bd7b3f16
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/AndNode.java
@@ -0,0 +1,14 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.List;
+import java.util.Objects;
+
+/** Logical conjunction with its already-derived validity-date behavior. */
+public record AndNode(List children, DateAggregationAction dateAction, boolean createExists) implements QueryNode {
+
+ public AndNode {
+ ModelValidation.requireNotEmpty(children, "children");
+ children = List.copyOf(children);
+ dateAction = Objects.requireNonNull(dateAction, "dateAction");
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ColumnType.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ColumnType.java
new file mode 100644
index 0000000000..6f57bc40be
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ColumnType.java
@@ -0,0 +1,13 @@
+package com.bakdata.conquery.sql.query;
+
+/** Logical type of a resolved database column. */
+public enum ColumnType {
+ STRING,
+ INTEGER,
+ BOOLEAN,
+ REAL,
+ DECIMAL,
+ MONEY,
+ DATE,
+ DATE_RANGE
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ConceptNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ConceptNode.java
new file mode 100644
index 0000000000..5b1d7baad7
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ConceptNode.java
@@ -0,0 +1,21 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.List;
+import java.util.Objects;
+
+/** A concept selection whose connectors, conditions, filters, and selects are fully resolved. */
+public record ConceptNode(
+ String logicalId,
+ List connectors,
+ List selects,
+ DateAggregationAction dateAction
+) implements QueryNode {
+
+ public ConceptNode {
+ logicalId = ModelValidation.requireNonBlank(logicalId, "logicalId");
+ ModelValidation.requireNotEmpty(connectors, "connectors");
+ connectors = List.copyOf(connectors);
+ selects = List.copyOf(Objects.requireNonNull(selects, "selects"));
+ dateAction = Objects.requireNonNull(dateAction, "dateAction");
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateAggregationAction.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateAggregationAction.java
new file mode 100644
index 0000000000..0f5abe96ee
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateAggregationAction.java
@@ -0,0 +1,9 @@
+package com.bakdata.conquery.sql.query;
+
+/** Defines how validity dates produced by child nodes are propagated. */
+public enum DateAggregationAction {
+ BLOCK,
+ MERGE,
+ INTERSECT,
+ NEGATE
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRange.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRange.java
new file mode 100644
index 0000000000..3830a7d175
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRange.java
@@ -0,0 +1,22 @@
+package com.bakdata.conquery.sql.query;
+
+import java.time.LocalDate;
+import java.util.Objects;
+import java.util.Optional;
+
+/** Inclusive date restriction. An empty bound represents an unbounded side. */
+public record DateRange(Optional startInclusive, Optional endInclusive) {
+
+ public DateRange {
+ startInclusive = Objects.requireNonNull(startInclusive, "startInclusive");
+ endInclusive = Objects.requireNonNull(endInclusive, "endInclusive");
+ if (startInclusive.isPresent() && endInclusive.isPresent()
+ && startInclusive.get().isAfter(endInclusive.get())) {
+ throw new IllegalArgumentException("startInclusive must not be after endInclusive");
+ }
+ }
+
+ public static DateRange closed(LocalDate startInclusive, LocalDate endInclusive) {
+ return new DateRange(Optional.of(startInclusive), Optional.of(endInclusive));
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRestrictionNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRestrictionNode.java
new file mode 100644
index 0000000000..0c6c59e64f
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRestrictionNode.java
@@ -0,0 +1,12 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.Objects;
+
+/** Applies an inclusive date restriction to its child. */
+public record DateRestrictionNode(DateRange dateRange, QueryNode child) implements QueryNode {
+
+ public DateRestrictionNode {
+ dateRange = Objects.requireNonNull(dateRange, "dateRange");
+ child = Objects.requireNonNull(child, "child");
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/EntitySchema.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/EntitySchema.java
new file mode 100644
index 0000000000..2cf2ee125c
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/EntitySchema.java
@@ -0,0 +1,15 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.Objects;
+
+/** Physical source used to enumerate all entities, for example when compiling a root-level negation. */
+public record EntitySchema(SqlTable table, ResolvedColumn primaryId) {
+
+ public EntitySchema {
+ table = Objects.requireNonNull(table, "table");
+ primaryId = Objects.requireNonNull(primaryId, "primaryId");
+ if (!table.equals(primaryId.table())) {
+ throw new IllegalArgumentException("primaryId must belong to the entity table");
+ }
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ExecutionTarget.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ExecutionTarget.java
new file mode 100644
index 0000000000..542b6c664d
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ExecutionTarget.java
@@ -0,0 +1,19 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.Objects;
+import java.util.Optional;
+
+/** Selects the dataset snapshot and configured datasource used to execute a query. */
+public record ExecutionTarget(String datasetId, String dataSource, Optional catalogRevision) {
+
+ public ExecutionTarget {
+ datasetId = ModelValidation.requireNonBlank(datasetId, "datasetId");
+ dataSource = ModelValidation.requireNonBlank(dataSource, "dataSource");
+ catalogRevision = Objects.requireNonNull(catalogRevision, "catalogRevision")
+ .map(revision -> ModelValidation.requireNonBlank(revision, "catalogRevision"));
+ }
+
+ public ExecutionTarget(String datasetId, String dataSource) {
+ this(datasetId, dataSource, Optional.empty());
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ModelValidation.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ModelValidation.java
new file mode 100644
index 0000000000..8f65dc6d7e
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ModelValidation.java
@@ -0,0 +1,26 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.Collection;
+import java.util.Objects;
+
+final class ModelValidation {
+
+ private ModelValidation() {
+ }
+
+ static String requireNonBlank(String value, String name) {
+ Objects.requireNonNull(value, name);
+ if (value.isBlank()) {
+ throw new IllegalArgumentException(name + " must not be blank");
+ }
+ return value;
+ }
+
+ static Collection requireNotEmpty(Collection values, String name) {
+ Objects.requireNonNull(values, name);
+ if (values.isEmpty()) {
+ throw new IllegalArgumentException(name + " must not be empty");
+ }
+ return values;
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/NegationNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/NegationNode.java
new file mode 100644
index 0000000000..9562532f1b
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/NegationNode.java
@@ -0,0 +1,11 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.Objects;
+
+/** Logical negation. */
+public record NegationNode(QueryNode child) implements QueryNode {
+
+ public NegationNode {
+ Objects.requireNonNull(child, "child");
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/OrNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/OrNode.java
new file mode 100644
index 0000000000..33d21304f4
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/OrNode.java
@@ -0,0 +1,15 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.List;
+import java.util.Objects;
+
+/** Logical disjunction with its already-derived validity-date behavior. */
+public record OrNode(List children, DateAggregationAction dateAction,
+ boolean createExists) implements QueryNode {
+
+ public OrNode {
+ ModelValidation.requireNotEmpty(children, "children");
+ children = List.copyOf(children);
+ Objects.requireNonNull(dateAction, "dateAction");
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/QueryNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/QueryNode.java
new file mode 100644
index 0000000000..8813838524
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/QueryNode.java
@@ -0,0 +1,5 @@
+package com.bakdata.conquery.sql.query;
+
+/** A normalized query-tree node accepted by SQL compilation. */
+public sealed interface QueryNode permits AndNode, OrNode, NegationNode, DateRestrictionNode, ConceptNode {
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedColumn.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedColumn.java
new file mode 100644
index 0000000000..c1840a9325
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedColumn.java
@@ -0,0 +1,20 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.Objects;
+
+/** A column whose owning table, physical name, and logical type are known. */
+public record ResolvedColumn(
+ String logicalId,
+ SqlTable table,
+ String physicalName,
+ ColumnType type,
+ boolean nullable
+) {
+
+ public ResolvedColumn {
+ logicalId = ModelValidation.requireNonBlank(logicalId, "logicalId");
+ Objects.requireNonNull(table, "table");
+ physicalName = ModelValidation.requireNonBlank(physicalName, "physicalName");
+ Objects.requireNonNull(type, "type");
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedCondition.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedCondition.java
new file mode 100644
index 0000000000..2e9255bc53
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedCondition.java
@@ -0,0 +1,5 @@
+package com.bakdata.conquery.sql.query;
+
+/** A resolved connector or concept-element condition. */
+public interface ResolvedCondition {
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedConnector.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedConnector.java
new file mode 100644
index 0000000000..df50742fe4
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedConnector.java
@@ -0,0 +1,52 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/** A selected connector with all physical columns and operations resolved. */
+public record ResolvedConnector(
+ String logicalId,
+ SqlTable table,
+ ResolvedColumn primaryId,
+ Optional secondaryId,
+ ResolvedValidityDate validityDate,
+ List filters,
+ List selects,
+ List conditions
+) {
+
+ public ResolvedConnector {
+ logicalId = ModelValidation.requireNonBlank(logicalId, "logicalId");
+ Objects.requireNonNull(table, "table");
+ requireColumnOnTable(primaryId, table, "primaryId");
+ if (Objects.requireNonNull(secondaryId, "secondaryId").isPresent()) {
+ secondaryId = Optional.of(requireColumnOnTable(secondaryId.get(), table, "secondaryId"));
+ }
+ Objects.requireNonNull(validityDate, "validityDate");
+ validateValidityDateTable(validityDate, table);
+ filters = List.copyOf(Objects.requireNonNull(filters, "filters"));
+ selects = List.copyOf(Objects.requireNonNull(selects, "selects"));
+ conditions = List.copyOf(Objects.requireNonNull(conditions, "conditions"));
+ }
+
+ private static ResolvedColumn requireColumnOnTable(ResolvedColumn column, SqlTable table, String name) {
+ Objects.requireNonNull(column, name);
+ if (!table.equals(column.table())) {
+ throw new IllegalArgumentException(name + " must belong to the connector table");
+ }
+ return column;
+ }
+
+ private static void validateValidityDateTable(ResolvedValidityDate validityDate, SqlTable table) {
+ switch (validityDate) {
+ case ResolvedValidityDate.None ignored -> {
+ }
+ case ResolvedValidityDate.Point point -> requireColumnOnTable(point.column(), table, "validityDate");
+ case ResolvedValidityDate.Range range -> {
+ requireColumnOnTable(range.start(), table, "validityDate.start");
+ requireColumnOnTable(range.end(), table, "validityDate.end");
+ }
+ }
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedFilter.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedFilter.java
new file mode 100644
index 0000000000..c6712c958b
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedFilter.java
@@ -0,0 +1,10 @@
+package com.bakdata.conquery.sql.query;
+
+/**
+ * Marker for an immutable, validated filter operation.
+ *
+ * Implementations may be supplied by extensions. They must contain resolved columns and typed values rather than
+ * repository identifiers.
+ */
+public interface ResolvedFilter {
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedQuery.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedQuery.java
new file mode 100644
index 0000000000..c6fcba1565
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedQuery.java
@@ -0,0 +1,25 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Complete input for compiling a concept query to SQL.
+ *
+ * @param includeValidityDate whether the final result exposes an aggregated validity-date column
+ */
+public record ResolvedQuery(
+ ExecutionTarget target,
+ EntitySchema entitySchema,
+ QueryNode root,
+ boolean includeValidityDate,
+ List resultColumns
+) {
+
+ public ResolvedQuery {
+ Objects.requireNonNull(target, "target");
+ Objects.requireNonNull(entitySchema, "entitySchema");
+ Objects.requireNonNull(root, "root");
+ resultColumns = List.copyOf(Objects.requireNonNull(resultColumns, "resultColumns"));
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedSelect.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedSelect.java
new file mode 100644
index 0000000000..44cb0b2707
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedSelect.java
@@ -0,0 +1,9 @@
+package com.bakdata.conquery.sql.query;
+
+/**
+ * Marker for an immutable, validated select operation.
+ *
+ * Implementations may be supplied by extensions and are dispatched to matching SQL converters.
+ */
+public interface ResolvedSelect {
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedValidityDate.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedValidityDate.java
new file mode 100644
index 0000000000..88f3e4e93e
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedValidityDate.java
@@ -0,0 +1,33 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.Objects;
+
+/** Physical representation of event validity for a connector. */
+public sealed interface ResolvedValidityDate permits ResolvedValidityDate.None, ResolvedValidityDate.Point, ResolvedValidityDate.Range {
+
+ record None() implements ResolvedValidityDate {
+ }
+
+ record Point(ResolvedColumn column) implements ResolvedValidityDate {
+ public Point {
+ requireDateColumn(column, "column");
+ }
+ }
+
+ record Range(ResolvedColumn start, ResolvedColumn end) implements ResolvedValidityDate {
+ public Range {
+ requireDateColumn(start, "start");
+ requireDateColumn(end, "end");
+ if (!start.table().equals(end.table())) {
+ throw new IllegalArgumentException("validity-date columns must belong to the same table");
+ }
+ }
+ }
+
+ private static void requireDateColumn(ResolvedColumn column, String name) {
+ Objects.requireNonNull(column, name);
+ if (column.type() != ColumnType.DATE) {
+ throw new IllegalArgumentException(name + " must be a DATE column");
+ }
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultColumn.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultColumn.java
new file mode 100644
index 0000000000..e02a0e584c
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultColumn.java
@@ -0,0 +1,24 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+/** Ordered output-column metadata, with presentation names already resolved for the query locale. */
+public record ResultColumn(
+ String label,
+ Optional defaultLabel,
+ Optional description,
+ ResultType type,
+ Set semantics
+) {
+
+ public ResultColumn {
+ label = ModelValidation.requireNonBlank(label, "label");
+ defaultLabel = Objects.requireNonNull(defaultLabel, "defaultLabel")
+ .map(value -> ModelValidation.requireNonBlank(value, "defaultLabel"));
+ Objects.requireNonNull(description, "description");
+ Objects.requireNonNull(type, "type");
+ semantics = Set.copyOf(Objects.requireNonNull(semantics, "semantics"));
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultType.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultType.java
new file mode 100644
index 0000000000..929c1af930
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultType.java
@@ -0,0 +1,23 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.Objects;
+
+/** Logical type of a result column. */
+public sealed interface ResultType permits ResultType.Primitive, ResultType.ListType {
+
+ enum Primitive implements ResultType {
+ BOOLEAN,
+ INTEGER,
+ NUMERIC,
+ DATE,
+ DATE_RANGE,
+ STRING,
+ MONEY
+ }
+
+ record ListType(Primitive elementType) implements ResultType {
+ public ListType {
+ Objects.requireNonNull(elementType, "elementType");
+ }
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/SqlTable.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/SqlTable.java
new file mode 100644
index 0000000000..1909f560d6
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/SqlTable.java
@@ -0,0 +1,19 @@
+package com.bakdata.conquery.sql.query;
+
+import java.util.Arrays;
+import java.util.List;
+
+/** A logical table together with its unquoted physical SQL name. */
+public record SqlTable(String logicalId, List physicalName) {
+
+ public SqlTable {
+ logicalId = ModelValidation.requireNonBlank(logicalId, "logicalId");
+ ModelValidation.requireNotEmpty(physicalName, "physicalName");
+ physicalName = List.copyOf(physicalName);
+ physicalName.forEach(part -> ModelValidation.requireNonBlank(part, "physicalName part"));
+ }
+
+ public static SqlTable of(String logicalId, String... physicalName) {
+ return new SqlTable(logicalId, Arrays.asList(physicalName));
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/package-info.java
new file mode 100644
index 0000000000..75e6f6214f
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/package-info.java
@@ -0,0 +1,8 @@
+/**
+ * Framework-neutral, fully resolved input model for SQL query compilation.
+ *
+ * Instances in this package must not require repository access or identifier resolution during compilation. They
+ * describe query semantics and physical database objects, but deliberately exclude connections, dialects, and SQL
+ * compiler state.
+ */
+package com.bakdata.conquery.sql.query;
diff --git a/sql-connector/src/test/java/com/bakdata/conquery/sql/query/ResolvedQueryTest.java b/sql-connector/src/test/java/com/bakdata/conquery/sql/query/ResolvedQueryTest.java
new file mode 100644
index 0000000000..692df371c1
--- /dev/null
+++ b/sql-connector/src/test/java/com/bakdata/conquery/sql/query/ResolvedQueryTest.java
@@ -0,0 +1,110 @@
+package com.bakdata.conquery.sql.query;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+class ResolvedQueryTest {
+
+ private static final SqlTable EVENTS = SqlTable.of("dataset.events", "analytics", "events");
+ private static final ResolvedColumn ENTITY_ID = new ResolvedColumn(
+ "dataset.events.entity_id", EVENTS, "entity_id", ColumnType.STRING, false
+ );
+
+ @Test
+ void shouldCreateResolvedQueryWithoutFrameworkTypes() {
+ ResolvedConnector connector = new ResolvedConnector(
+ "dataset.concept.connector",
+ EVENTS,
+ ENTITY_ID,
+ Optional.empty(),
+ new ResolvedValidityDate.Point(dateColumn("event_date")),
+ List.of(),
+ List.of(),
+ List.of()
+ );
+ ConceptNode concept = new ConceptNode(
+ "dataset.concept", List.of(connector), List.of(), DateAggregationAction.MERGE
+ );
+
+ ResolvedQuery query = new ResolvedQuery(
+ new ExecutionTarget("dataset", "warehouse"),
+ new EntitySchema(EVENTS, ENTITY_ID),
+ concept,
+ true,
+ List.of(new ResultColumn(
+ "Entity", Optional.of("Entity"), Optional.empty(), ResultType.Primitive.STRING, Set.of("ID")
+ ))
+ );
+
+ assertEquals(concept, query.root());
+ assertEquals("warehouse", query.target().dataSource());
+ }
+
+ @Test
+ void shouldDefensivelyCopyOrderedCollections() {
+ List children = new ArrayList<>();
+ children.add(conceptNode(EVENTS));
+ AndNode andNode = new AndNode(children, DateAggregationAction.INTERSECT, false);
+
+ children.add(conceptNode(EVENTS));
+
+ assertEquals(1, andNode.children().size());
+ assertThrows(UnsupportedOperationException.class, () -> andNode.children().clear());
+ }
+
+ @Test
+ void shouldRejectColumnsFromAnotherConnectorTable() {
+ SqlTable otherTable = SqlTable.of("dataset.other", "analytics", "other");
+ ResolvedColumn otherId = new ResolvedColumn(
+ "dataset.other.entity_id", otherTable, "entity_id", ColumnType.STRING, false
+ );
+
+ assertThrows(IllegalArgumentException.class, () -> new ResolvedConnector(
+ "dataset.concept.connector",
+ EVENTS,
+ otherId,
+ Optional.empty(),
+ new ResolvedValidityDate.None(),
+ List.of(),
+ List.of(),
+ List.of()
+ ));
+ }
+
+ @Test
+ void shouldRejectNonDateValidityColumns() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new ResolvedValidityDate.Point(ENTITY_ID)
+ );
+ }
+
+ @Test
+ void shouldRejectInvertedDateRestriction() {
+ assertThrows(IllegalArgumentException.class, () -> DateRange.closed(
+ LocalDate.of(2025, 2, 1),
+ LocalDate.of(2025, 1, 1)
+ ));
+ }
+
+ private static ConceptNode conceptNode(SqlTable table) {
+ ResolvedColumn id = new ResolvedColumn("concept.id", table, "entity_id", ColumnType.STRING, false);
+ ResolvedConnector connector = new ResolvedConnector(
+ "concept.connector", table, id, Optional.empty(), new ResolvedValidityDate.None(),
+ List.of(), List.of(), List.of()
+ );
+ return new ConceptNode("concept", List.of(connector), List.of(), DateAggregationAction.BLOCK);
+ }
+
+ private static ResolvedColumn dateColumn(String name) {
+ return new ResolvedColumn("dataset.events." + name, EVENTS, name, ColumnType.DATE, true);
+ }
+}
From 2aad4d28a4d7a9aac5d890fca83dc36a2459efc3 Mon Sep 17 00:00:00 2001
From: MT <12283268+thoniTUB@users.noreply.github.com>
Date: Wed, 26 Aug 2026 17:40:25 +0200
Subject: [PATCH 02/81] refactor(sql): decouple node conversion dispatch
Co-authored-by: Codex GPT-5
---
.../bakdata/conquery/sql/conversion/Conversions.java | 5 ++---
.../sql/conversion/NodeConversionDispatcher.java | 10 ++++++++++
.../conquery/sql/conversion/NodeConversions.java | 11 ++++++++---
.../sql/conversion/cqelement/ConversionContext.java | 6 ++----
4 files changed, 22 insertions(+), 10 deletions(-)
create mode 100644 backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConversionDispatcher.java
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/Conversions.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/Conversions.java
index 50116dea6e..48a59e2b84 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/Conversions.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/Conversions.java
@@ -13,7 +13,7 @@
* @param context of the convertible
* @see Converter
*/
-public abstract class Conversions {
+public class Conversions {
@Getter
private final List extends Converter extends C, R, X>> converters;
@@ -29,8 +29,7 @@ public R convert(C node, X context) {
if (maybeConverted.isPresent()) {
if (converted == null) {
converted = maybeConverted.get();
- }
- else {
+ } else {
throw new IllegalStateException("Multiple Converters for %s".formatted(node));
}
}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConversionDispatcher.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConversionDispatcher.java
new file mode 100644
index 0000000000..64c1749fe9
--- /dev/null
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConversionDispatcher.java
@@ -0,0 +1,10 @@
+package com.bakdata.conquery.sql.conversion;
+
+import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
+
+/** Recursively dispatches a query node to its matching SQL converter. */
+@FunctionalInterface
+public interface NodeConversionDispatcher {
+
+ ConversionContext convert(Object node, ConversionContext context);
+}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConversions.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConversions.java
index 742538f3a1..c61a301d8f 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConversions.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConversions.java
@@ -4,7 +4,6 @@
import com.bakdata.conquery.models.config.ConqueryConfig;
import com.bakdata.conquery.models.config.IdColumnConfig;
import com.bakdata.conquery.models.query.PrintSettings;
-import com.bakdata.conquery.models.query.Visitable;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.DialectBundle;
import com.bakdata.conquery.sql.conversion.model.NameGenerator;
@@ -18,8 +17,9 @@
/**
* Entry point for converting {@link QueryDescription} to an SQL query.
*/
-public class NodeConversions extends Conversions {
+public class NodeConversions implements NodeConversionDispatcher {
+ private final Conversions conversions;
private final IdColumnConfig idColumns;
private final DialectBundle dialect;
private final NameGenerator nameGenerator;
@@ -36,7 +36,7 @@ public NodeConversions(
Clock clock,
String defaultPrimaryColumn
) {
- super(dialectBundle.getNodeConverters(dslContext));
+ this.conversions = new Conversions<>(dialectBundle.getNodeConverters(dslContext));
this.idColumns = idColumns;
this.dialect = dialectBundle;
this.nameGenerator = new NameGenerator(dialectBundle.getNameMaxLength());
@@ -45,6 +45,11 @@ public NodeConversions(
this.defaultPrimaryColumn = defaultPrimaryColumn;
}
+ @Override
+ public ConversionContext convert(Object node, ConversionContext context) {
+ return conversions.convert(node, context);
+ }
+
public ConversionContext convert(QueryDescription queryDescription, ConqueryConfig conqueryConfig) {
ConversionContext initialCtx = ConversionContext.builder()
.idColumns(idColumns)
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/ConversionContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/ConversionContext.java
index a2973ec90d..235ba767f9 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/ConversionContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/ConversionContext.java
@@ -11,9 +11,8 @@
import com.bakdata.conquery.models.datasets.SecondaryIdDescription;
import com.bakdata.conquery.models.query.PrintSettings;
import com.bakdata.conquery.sql.conversion.Context;
-import com.bakdata.conquery.sql.conversion.NodeConversions;
+import com.bakdata.conquery.sql.conversion.NodeConversionDispatcher;
import com.bakdata.conquery.sql.conversion.dialect.DialectBundle;
-import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.forms.StratificationFunctions;
import com.bakdata.conquery.sql.conversion.model.NameGenerator;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
@@ -38,7 +37,7 @@ public class ConversionContext implements Context {
StratificationFunctions stratificationFunctions;
String defaultPrimaryColumn;
PrintSettings sqlPrintSettings;
- NodeConversions nodeConversions;
+ NodeConversionDispatcher nodeConversions;
DialectBundle dialectBundle;
NameGenerator nameGenerator;
SqlExecutionService executionService;
@@ -76,7 +75,6 @@ public boolean isWithStratification() {
}
-
/**
* Adds a query step to the list of {@link QueryStep} of this context.
*/
From a2eecb4950373ccdaf3df3c5a722e576ff69d7d2 Mon Sep 17 00:00:00 2001
From: MT <12283268+thoniTUB@users.noreply.github.com>
Date: Fri, 28 Aug 2026 12:59:59 +0200
Subject: [PATCH 03/81] feat(sql): define validated resolved operations
Co-authored-by: Codex GPT-5
---
dataset-model/README.md | 7 +
dataset-model/pom.xml | 16 ++
.../models/datasets/ColumnDescriptor.java | 5 +
.../conquery/models/datasets}/ColumnType.java | 4 +-
.../models/datasets/package-info.java | 2 +
pom.xml | 1 +
sql-connector/README.md | 30 +++-
sql-connector/pom.xml | 19 ++
.../bakdata/conquery/sql/query/AndNode.java | 14 --
.../conquery/sql/query/ConceptNode.java | 21 ---
.../bakdata/conquery/sql/query/DateRange.java | 22 ---
.../sql/query/DateRestrictionNode.java | 12 --
.../conquery/sql/query/EntitySchema.java | 15 --
.../conquery/sql/query/ExecutionTarget.java | 19 +-
.../conquery/sql/query/ModelValidation.java | 26 ---
.../conquery/sql/query/NegationNode.java | 11 --
.../bakdata/conquery/sql/query/OrNode.java | 15 --
.../conquery/sql/query/ResolvedColumn.java | 20 ---
.../conquery/sql/query/ResolvedConnector.java | 52 ------
.../conquery/sql/query/ResolvedQuery.java | 21 ++-
.../sql/query/ResolvedValidityDate.java | 33 ----
.../conquery/sql/query/ResultColumn.java | 24 ---
.../bakdata/conquery/sql/query/SqlTable.java | 19 --
.../query/internal/ModelNormalization.java | 27 +++
.../conquery/sql/query/node/AndNode.java | 20 +++
.../conquery/sql/query/node/ConceptNode.java | 25 +++
.../{ => node}/DateAggregationAction.java | 2 +-
.../sql/query/node/DateRestrictionNode.java | 12 ++
.../conquery/sql/query/node/NegationNode.java | 8 +
.../conquery/sql/query/node/OrNode.java | 20 +++
.../sql/query/{ => node}/QueryNode.java | 2 +-
.../conquery/sql/query/node/package-info.java | 2 +
.../query/operation/BuiltInAggregations.java | 132 ++++++++++++++
.../query/operation/BuiltInConditions.java | 69 ++++++++
.../sql/query/operation/BuiltInFilters.java | 81 +++++++++
.../sql/query/operation/BuiltInSelects.java | 76 ++++++++
.../query/operation/ResolvedAggregation.java | 5 +
.../{ => operation}/ResolvedCondition.java | 2 +-
.../query/{ => operation}/ResolvedFilter.java | 2 +-
.../query/{ => operation}/ResolvedSelect.java | 2 +-
.../sql/query/operation/package-info.java | 2 +
.../conquery/sql/query/package-info.java | 3 +
.../conquery/sql/query/range/DateRange.java | 25 +++
.../conquery/sql/query/range/NumberRange.java | 43 +++++
.../sql/query/range/SubstringRange.java | 27 +++
.../sql/query/range/package-info.java | 2 +
.../sql/query/result/ResultColumn.java | 29 ++++
.../sql/query/{ => result}/ResultType.java | 9 +-
.../sql/query/result/package-info.java | 2 +
.../sql/query/schema/DateColumns.java | 42 +++++
.../sql/query/schema/EntitySchema.java | 17 ++
.../sql/query/schema/ResolvedColumn.java | 16 ++
.../sql/query/schema/ResolvedConnector.java | 59 +++++++
.../query/schema/ResolvedValidityDate.java | 32 ++++
.../conquery/sql/query/schema/SqlTable.java | 26 +++
.../sql/query/schema/package-info.java | 2 +
.../query/validation/AllowedColumnTypes.java | 26 +++
.../AllowedColumnTypesValidator.java | 23 +++
.../validation/ResolvedQueryValidation.java | 26 +++
.../sql/query/validation/package-info.java | 2 +
.../sql/query/BuiltInOperationsTest.java | 164 ++++++++++++++++++
.../conquery/sql/query/ResolvedQueryTest.java | 58 ++++++-
.../sql/query/ValidationTestSupport.java | 28 +++
63 files changed, 1235 insertions(+), 323 deletions(-)
create mode 100644 dataset-model/README.md
create mode 100644 dataset-model/pom.xml
create mode 100644 dataset-model/src/main/java/com/bakdata/conquery/models/datasets/ColumnDescriptor.java
rename {sql-connector/src/main/java/com/bakdata/conquery/sql/query => dataset-model/src/main/java/com/bakdata/conquery/models/datasets}/ColumnType.java (52%)
create mode 100644 dataset-model/src/main/java/com/bakdata/conquery/models/datasets/package-info.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/AndNode.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ConceptNode.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRange.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRestrictionNode.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/EntitySchema.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ModelValidation.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/NegationNode.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/OrNode.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedColumn.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedConnector.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedValidityDate.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultColumn.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/SqlTable.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/internal/ModelNormalization.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/AndNode.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/ConceptNode.java
rename sql-connector/src/main/java/com/bakdata/conquery/sql/query/{ => node}/DateAggregationAction.java (76%)
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/DateRestrictionNode.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/NegationNode.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/OrNode.java
rename sql-connector/src/main/java/com/bakdata/conquery/sql/query/{ => node}/QueryNode.java (79%)
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/package-info.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInAggregations.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInConditions.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInFilters.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInSelects.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedAggregation.java
rename sql-connector/src/main/java/com/bakdata/conquery/sql/query/{ => operation}/ResolvedCondition.java (66%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/query/{ => operation}/ResolvedFilter.java (83%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/query/{ => operation}/ResolvedSelect.java (80%)
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/package-info.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/DateRange.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/NumberRange.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/SubstringRange.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/package-info.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/ResultColumn.java
rename sql-connector/src/main/java/com/bakdata/conquery/sql/query/{ => result}/ResultType.java (54%)
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/package-info.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/DateColumns.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/EntitySchema.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedColumn.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedConnector.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedValidityDate.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/SqlTable.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/package-info.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/AllowedColumnTypes.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/AllowedColumnTypesValidator.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/ResolvedQueryValidation.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/package-info.java
create mode 100644 sql-connector/src/test/java/com/bakdata/conquery/sql/query/BuiltInOperationsTest.java
create mode 100644 sql-connector/src/test/java/com/bakdata/conquery/sql/query/ValidationTestSupport.java
diff --git a/dataset-model/README.md b/dataset-model/README.md
new file mode 100644
index 0000000000..d10f263d63
--- /dev/null
+++ b/dataset-model/README.md
@@ -0,0 +1,7 @@
+# Dataset model
+
+This module contains the dependency-free dataset vocabulary shared by query producers, plugins, and execution engines.
+It must not depend on a backend framework, persistence technology, or SQL implementation.
+
+Keep types here only when they have the same meaning for every consumer. Backend storage records and SQL compilation
+types remain in their respective modules and reference this shared vocabulary.
diff --git a/dataset-model/pom.xml b/dataset-model/pom.xml
new file mode 100644
index 0000000000..e567eca481
--- /dev/null
+++ b/dataset-model/pom.xml
@@ -0,0 +1,16 @@
+
+
+ 4.0.0
+
+
+ com.bakdata.conquery
+ parent
+ ${revision}
+ ../pom.xml
+
+
+ dataset-model
+ Conquery Dataset Model
+
diff --git a/dataset-model/src/main/java/com/bakdata/conquery/models/datasets/ColumnDescriptor.java b/dataset-model/src/main/java/com/bakdata/conquery/models/datasets/ColumnDescriptor.java
new file mode 100644
index 0000000000..3f61572d6c
--- /dev/null
+++ b/dataset-model/src/main/java/com/bakdata/conquery/models/datasets/ColumnDescriptor.java
@@ -0,0 +1,5 @@
+package com.bakdata.conquery.models.datasets;
+
+/** Framework-neutral description of a validated dataset column. */
+public record ColumnDescriptor(String name, ColumnType type) {
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ColumnType.java b/dataset-model/src/main/java/com/bakdata/conquery/models/datasets/ColumnType.java
similarity index 52%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/ColumnType.java
rename to dataset-model/src/main/java/com/bakdata/conquery/models/datasets/ColumnType.java
index 6f57bc40be..d522d392d8 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ColumnType.java
+++ b/dataset-model/src/main/java/com/bakdata/conquery/models/datasets/ColumnType.java
@@ -1,6 +1,6 @@
-package com.bakdata.conquery.sql.query;
+package com.bakdata.conquery.models.datasets;
-/** Logical type of a resolved database column. */
+/** Logical type of a dataset column. */
public enum ColumnType {
STRING,
INTEGER,
diff --git a/dataset-model/src/main/java/com/bakdata/conquery/models/datasets/package-info.java b/dataset-model/src/main/java/com/bakdata/conquery/models/datasets/package-info.java
new file mode 100644
index 0000000000..4f9a0019d6
--- /dev/null
+++ b/dataset-model/src/main/java/com/bakdata/conquery/models/datasets/package-info.java
@@ -0,0 +1,2 @@
+/** Framework-neutral dataset types shared by query producers, plugins, and execution engines. */
+package com.bakdata.conquery.models.datasets;
diff --git a/pom.xml b/pom.xml
index 1fb3cca8fe..5e2cd7311e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,6 +29,7 @@
+ dataset-model
sql-connector
backend
executable
diff --git a/sql-connector/README.md b/sql-connector/README.md
index 7222983e10..959b9358b4 100644
--- a/sql-connector/README.md
+++ b/sql-connector/README.md
@@ -17,6 +17,30 @@ The SQL compiler may perform dialect capability checks, but it must not access a
identifiers. Connections, dialect implementations, compiler state, and execution services are intentionally not part of
the resolved model.
-`ResolvedFilter`, `ResolvedSelect`, and `ResolvedCondition` are open extension points. Implementations must be immutable
-and carry resolved columns and typed values. They must not carry unresolved repository identifiers or arbitrary SQL
-received from a query request.
+The model is organized by responsibility:
+
+- `query.node` contains the normalized query tree;
+- `query.operation` contains the open operation interfaces and standard implementations;
+- `query.schema` contains resolved physical tables, columns, connectors, and entity metadata;
+- `query.range` and `query.result` contain shared value objects;
+- `query.validation` contains the validation boundary and reusable constraints.
+
+Shared dataset vocabulary such as `ColumnType` comes from the dependency-free `dataset-model` module. SQL-specific
+physical metadata remains in `query.schema`.
+
+`ResolvedFilter`, `ResolvedSelect`, `ResolvedCondition`, and `ResolvedAggregation` are open extension points.
+Implementations must be immutable and carry resolved columns and typed values. They must not carry unresolved repository
+identifiers or arbitrary SQL received from a query request.
+
+The `BuiltInFilters`, `BuiltInSelects`, `BuiltInConditions`, and `BuiltInAggregations` types define the normalized
+operations understood by the standard compiler. They intentionally describe semantics rather than the configuration
+classes used by a particular query-producing application.
+
+## Validation
+
+The resolved model uses Jakarta Bean Validation annotations. The application supplies a validation provider, normally
+Hibernate Validator, and calls `ResolvedQueryValidation` once before passing a query to the SQL compiler. Validation is
+cascaded through the complete query graph, including extension-provided filters, selects, conditions, and aggregations.
+
+Compact record constructors only create immutable copies of collections. Domain constraints such as compatible column
+types, ordered ranges, and same-table requirements are declarative and are reported together as constraint violations.
diff --git a/sql-connector/pom.xml b/sql-connector/pom.xml
index f06f2c1c34..073afc8767 100644
--- a/sql-connector/pom.xml
+++ b/sql-connector/pom.xml
@@ -15,10 +15,29 @@
Conquery SQL Connector
+
+ com.bakdata.conquery
+ dataset-model
+ ${project.version}
+
+
+ jakarta.validation
+ jakarta.validation-api
+
org.junit.jupiter
junit-jupiter-api
test
+
+ org.hibernate.validator
+ hibernate-validator
+ test
+
+
+ org.glassfish
+ jakarta.el
+ test
+
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/AndNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/AndNode.java
deleted file mode 100644
index 10bd7b3f16..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/AndNode.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.bakdata.conquery.sql.query;
-
-import java.util.List;
-import java.util.Objects;
-
-/** Logical conjunction with its already-derived validity-date behavior. */
-public record AndNode(List children, DateAggregationAction dateAction, boolean createExists) implements QueryNode {
-
- public AndNode {
- ModelValidation.requireNotEmpty(children, "children");
- children = List.copyOf(children);
- dateAction = Objects.requireNonNull(dateAction, "dateAction");
- }
-}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ConceptNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ConceptNode.java
deleted file mode 100644
index 5b1d7baad7..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ConceptNode.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.bakdata.conquery.sql.query;
-
-import java.util.List;
-import java.util.Objects;
-
-/** A concept selection whose connectors, conditions, filters, and selects are fully resolved. */
-public record ConceptNode(
- String logicalId,
- List connectors,
- List selects,
- DateAggregationAction dateAction
-) implements QueryNode {
-
- public ConceptNode {
- logicalId = ModelValidation.requireNonBlank(logicalId, "logicalId");
- ModelValidation.requireNotEmpty(connectors, "connectors");
- connectors = List.copyOf(connectors);
- selects = List.copyOf(Objects.requireNonNull(selects, "selects"));
- dateAction = Objects.requireNonNull(dateAction, "dateAction");
- }
-}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRange.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRange.java
deleted file mode 100644
index 3830a7d175..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRange.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.bakdata.conquery.sql.query;
-
-import java.time.LocalDate;
-import java.util.Objects;
-import java.util.Optional;
-
-/** Inclusive date restriction. An empty bound represents an unbounded side. */
-public record DateRange(Optional startInclusive, Optional endInclusive) {
-
- public DateRange {
- startInclusive = Objects.requireNonNull(startInclusive, "startInclusive");
- endInclusive = Objects.requireNonNull(endInclusive, "endInclusive");
- if (startInclusive.isPresent() && endInclusive.isPresent()
- && startInclusive.get().isAfter(endInclusive.get())) {
- throw new IllegalArgumentException("startInclusive must not be after endInclusive");
- }
- }
-
- public static DateRange closed(LocalDate startInclusive, LocalDate endInclusive) {
- return new DateRange(Optional.of(startInclusive), Optional.of(endInclusive));
- }
-}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRestrictionNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRestrictionNode.java
deleted file mode 100644
index 0c6c59e64f..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateRestrictionNode.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.bakdata.conquery.sql.query;
-
-import java.util.Objects;
-
-/** Applies an inclusive date restriction to its child. */
-public record DateRestrictionNode(DateRange dateRange, QueryNode child) implements QueryNode {
-
- public DateRestrictionNode {
- dateRange = Objects.requireNonNull(dateRange, "dateRange");
- child = Objects.requireNonNull(child, "child");
- }
-}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/EntitySchema.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/EntitySchema.java
deleted file mode 100644
index 2cf2ee125c..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/EntitySchema.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.bakdata.conquery.sql.query;
-
-import java.util.Objects;
-
-/** Physical source used to enumerate all entities, for example when compiling a root-level negation. */
-public record EntitySchema(SqlTable table, ResolvedColumn primaryId) {
-
- public EntitySchema {
- table = Objects.requireNonNull(table, "table");
- primaryId = Objects.requireNonNull(primaryId, "primaryId");
- if (!table.equals(primaryId.table())) {
- throw new IllegalArgumentException("primaryId must belong to the entity table");
- }
- }
-}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ExecutionTarget.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ExecutionTarget.java
index 542b6c664d..7b38bea7b2 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ExecutionTarget.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ExecutionTarget.java
@@ -1,16 +1,21 @@
package com.bakdata.conquery.sql.query;
-import java.util.Objects;
import java.util.Optional;
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
/** Selects the dataset snapshot and configured datasource used to execute a query. */
-public record ExecutionTarget(String datasetId, String dataSource, Optional catalogRevision) {
+public record ExecutionTarget(
+ @NotBlank String datasetId,
+ @NotBlank String dataSource,
+ @NotNull Optional catalogRevision
+) {
- public ExecutionTarget {
- datasetId = ModelValidation.requireNonBlank(datasetId, "datasetId");
- dataSource = ModelValidation.requireNonBlank(dataSource, "dataSource");
- catalogRevision = Objects.requireNonNull(catalogRevision, "catalogRevision")
- .map(revision -> ModelValidation.requireNonBlank(revision, "catalogRevision"));
+ @AssertTrue(message = "catalogRevision must not be blank when present")
+ public boolean isCatalogRevisionValid() {
+ return catalogRevision == null || catalogRevision.isEmpty() || !catalogRevision.get().isBlank();
}
public ExecutionTarget(String datasetId, String dataSource) {
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ModelValidation.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ModelValidation.java
deleted file mode 100644
index 8f65dc6d7e..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ModelValidation.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.bakdata.conquery.sql.query;
-
-import java.util.Collection;
-import java.util.Objects;
-
-final class ModelValidation {
-
- private ModelValidation() {
- }
-
- static String requireNonBlank(String value, String name) {
- Objects.requireNonNull(value, name);
- if (value.isBlank()) {
- throw new IllegalArgumentException(name + " must not be blank");
- }
- return value;
- }
-
- static Collection requireNotEmpty(Collection values, String name) {
- Objects.requireNonNull(values, name);
- if (values.isEmpty()) {
- throw new IllegalArgumentException(name + " must not be empty");
- }
- return values;
- }
-}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/NegationNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/NegationNode.java
deleted file mode 100644
index 9562532f1b..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/NegationNode.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.bakdata.conquery.sql.query;
-
-import java.util.Objects;
-
-/** Logical negation. */
-public record NegationNode(QueryNode child) implements QueryNode {
-
- public NegationNode {
- Objects.requireNonNull(child, "child");
- }
-}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/OrNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/OrNode.java
deleted file mode 100644
index 33d21304f4..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/OrNode.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.bakdata.conquery.sql.query;
-
-import java.util.List;
-import java.util.Objects;
-
-/** Logical disjunction with its already-derived validity-date behavior. */
-public record OrNode(List children, DateAggregationAction dateAction,
- boolean createExists) implements QueryNode {
-
- public OrNode {
- ModelValidation.requireNotEmpty(children, "children");
- children = List.copyOf(children);
- Objects.requireNonNull(dateAction, "dateAction");
- }
-}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedColumn.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedColumn.java
deleted file mode 100644
index c1840a9325..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedColumn.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.bakdata.conquery.sql.query;
-
-import java.util.Objects;
-
-/** A column whose owning table, physical name, and logical type are known. */
-public record ResolvedColumn(
- String logicalId,
- SqlTable table,
- String physicalName,
- ColumnType type,
- boolean nullable
-) {
-
- public ResolvedColumn {
- logicalId = ModelValidation.requireNonBlank(logicalId, "logicalId");
- Objects.requireNonNull(table, "table");
- physicalName = ModelValidation.requireNonBlank(physicalName, "physicalName");
- Objects.requireNonNull(type, "type");
- }
-}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedConnector.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedConnector.java
deleted file mode 100644
index df50742fe4..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedConnector.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.bakdata.conquery.sql.query;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-
-/** A selected connector with all physical columns and operations resolved. */
-public record ResolvedConnector(
- String logicalId,
- SqlTable table,
- ResolvedColumn primaryId,
- Optional secondaryId,
- ResolvedValidityDate validityDate,
- List filters,
- List selects,
- List conditions
-) {
-
- public ResolvedConnector {
- logicalId = ModelValidation.requireNonBlank(logicalId, "logicalId");
- Objects.requireNonNull(table, "table");
- requireColumnOnTable(primaryId, table, "primaryId");
- if (Objects.requireNonNull(secondaryId, "secondaryId").isPresent()) {
- secondaryId = Optional.of(requireColumnOnTable(secondaryId.get(), table, "secondaryId"));
- }
- Objects.requireNonNull(validityDate, "validityDate");
- validateValidityDateTable(validityDate, table);
- filters = List.copyOf(Objects.requireNonNull(filters, "filters"));
- selects = List.copyOf(Objects.requireNonNull(selects, "selects"));
- conditions = List.copyOf(Objects.requireNonNull(conditions, "conditions"));
- }
-
- private static ResolvedColumn requireColumnOnTable(ResolvedColumn column, SqlTable table, String name) {
- Objects.requireNonNull(column, name);
- if (!table.equals(column.table())) {
- throw new IllegalArgumentException(name + " must belong to the connector table");
- }
- return column;
- }
-
- private static void validateValidityDateTable(ResolvedValidityDate validityDate, SqlTable table) {
- switch (validityDate) {
- case ResolvedValidityDate.None ignored -> {
- }
- case ResolvedValidityDate.Point point -> requireColumnOnTable(point.column(), table, "validityDate");
- case ResolvedValidityDate.Range range -> {
- requireColumnOnTable(range.start(), table, "validityDate.start");
- requireColumnOnTable(range.end(), table, "validityDate.end");
- }
- }
- }
-}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedQuery.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedQuery.java
index c6fcba1565..f820a6db00 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedQuery.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedQuery.java
@@ -1,7 +1,13 @@
package com.bakdata.conquery.sql.query;
import java.util.List;
-import java.util.Objects;
+
+import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import com.bakdata.conquery.sql.query.node.QueryNode;
+import com.bakdata.conquery.sql.query.result.ResultColumn;
+import com.bakdata.conquery.sql.query.schema.EntitySchema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
/**
* Complete input for compiling a concept query to SQL.
@@ -9,17 +15,14 @@
* @param includeValidityDate whether the final result exposes an aggregated validity-date column
*/
public record ResolvedQuery(
- ExecutionTarget target,
- EntitySchema entitySchema,
- QueryNode root,
+ @NotNull @Valid ExecutionTarget target,
+ @NotNull @Valid EntitySchema entitySchema,
+ @NotNull @Valid QueryNode root,
boolean includeValidityDate,
- List resultColumns
+ @NotNull List<@NotNull @Valid ResultColumn> resultColumns
) {
public ResolvedQuery {
- Objects.requireNonNull(target, "target");
- Objects.requireNonNull(entitySchema, "entitySchema");
- Objects.requireNonNull(root, "root");
- resultColumns = List.copyOf(Objects.requireNonNull(resultColumns, "resultColumns"));
+ resultColumns = ModelNormalization.immutableCopy(resultColumns);
}
}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedValidityDate.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedValidityDate.java
deleted file mode 100644
index 88f3e4e93e..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedValidityDate.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.bakdata.conquery.sql.query;
-
-import java.util.Objects;
-
-/** Physical representation of event validity for a connector. */
-public sealed interface ResolvedValidityDate permits ResolvedValidityDate.None, ResolvedValidityDate.Point, ResolvedValidityDate.Range {
-
- record None() implements ResolvedValidityDate {
- }
-
- record Point(ResolvedColumn column) implements ResolvedValidityDate {
- public Point {
- requireDateColumn(column, "column");
- }
- }
-
- record Range(ResolvedColumn start, ResolvedColumn end) implements ResolvedValidityDate {
- public Range {
- requireDateColumn(start, "start");
- requireDateColumn(end, "end");
- if (!start.table().equals(end.table())) {
- throw new IllegalArgumentException("validity-date columns must belong to the same table");
- }
- }
- }
-
- private static void requireDateColumn(ResolvedColumn column, String name) {
- Objects.requireNonNull(column, name);
- if (column.type() != ColumnType.DATE) {
- throw new IllegalArgumentException(name + " must be a DATE column");
- }
- }
-}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultColumn.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultColumn.java
deleted file mode 100644
index e02a0e584c..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultColumn.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.bakdata.conquery.sql.query;
-
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-/** Ordered output-column metadata, with presentation names already resolved for the query locale. */
-public record ResultColumn(
- String label,
- Optional defaultLabel,
- Optional description,
- ResultType type,
- Set semantics
-) {
-
- public ResultColumn {
- label = ModelValidation.requireNonBlank(label, "label");
- defaultLabel = Objects.requireNonNull(defaultLabel, "defaultLabel")
- .map(value -> ModelValidation.requireNonBlank(value, "defaultLabel"));
- Objects.requireNonNull(description, "description");
- Objects.requireNonNull(type, "type");
- semantics = Set.copyOf(Objects.requireNonNull(semantics, "semantics"));
- }
-}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/SqlTable.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/SqlTable.java
deleted file mode 100644
index 1909f560d6..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/SqlTable.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.bakdata.conquery.sql.query;
-
-import java.util.Arrays;
-import java.util.List;
-
-/** A logical table together with its unquoted physical SQL name. */
-public record SqlTable(String logicalId, List physicalName) {
-
- public SqlTable {
- logicalId = ModelValidation.requireNonBlank(logicalId, "logicalId");
- ModelValidation.requireNotEmpty(physicalName, "physicalName");
- physicalName = List.copyOf(physicalName);
- physicalName.forEach(part -> ModelValidation.requireNonBlank(part, "physicalName part"));
- }
-
- public static SqlTable of(String logicalId, String... physicalName) {
- return new SqlTable(logicalId, Arrays.asList(physicalName));
- }
-}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/internal/ModelNormalization.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/internal/ModelNormalization.java
new file mode 100644
index 0000000000..f004d0ab79
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/internal/ModelNormalization.java
@@ -0,0 +1,27 @@
+package com.bakdata.conquery.sql.query.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public final class ModelNormalization {
+
+ private ModelNormalization() {
+ }
+
+ public static List immutableCopy(List values) {
+ return values == null ? null : Collections.unmodifiableList(new ArrayList<>(values));
+ }
+
+ public static Set immutableCopy(Set values) {
+ return values == null ? null : Collections.unmodifiableSet(new LinkedHashSet<>(values));
+ }
+
+ public static Map immutableCopy(Map values) {
+ return values == null ? null : Collections.unmodifiableMap(new LinkedHashMap<>(values));
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/AndNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/AndNode.java
new file mode 100644
index 0000000000..a3b614ce53
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/AndNode.java
@@ -0,0 +1,20 @@
+package com.bakdata.conquery.sql.query.node;
+
+import java.util.List;
+
+import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+
+/** Logical conjunction with its already-derived validity-date behavior. */
+public record AndNode(
+ @NotEmpty List<@NotNull @Valid QueryNode> children,
+ @NotNull DateAggregationAction dateAction,
+ boolean createExists
+) implements QueryNode {
+
+ public AndNode {
+ children = ModelNormalization.immutableCopy(children);
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/ConceptNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/ConceptNode.java
new file mode 100644
index 0000000000..4e23392cef
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/ConceptNode.java
@@ -0,0 +1,25 @@
+package com.bakdata.conquery.sql.query.node;
+
+import java.util.List;
+
+import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import com.bakdata.conquery.sql.query.operation.ResolvedSelect;
+import com.bakdata.conquery.sql.query.schema.ResolvedConnector;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+
+/** A concept selection whose connectors, conditions, filters, and selects are fully resolved. */
+public record ConceptNode(
+ @NotBlank String logicalId,
+ @NotEmpty List<@NotNull @Valid ResolvedConnector> connectors,
+ @NotNull List<@NotNull @Valid ResolvedSelect> selects,
+ @NotNull DateAggregationAction dateAction
+) implements QueryNode {
+
+ public ConceptNode {
+ connectors = ModelNormalization.immutableCopy(connectors);
+ selects = ModelNormalization.immutableCopy(selects);
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateAggregationAction.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/DateAggregationAction.java
similarity index 76%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateAggregationAction.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/DateAggregationAction.java
index 0f5abe96ee..17a56d680c 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/DateAggregationAction.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/DateAggregationAction.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query;
+package com.bakdata.conquery.sql.query.node;
/** Defines how validity dates produced by child nodes are propagated. */
public enum DateAggregationAction {
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/DateRestrictionNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/DateRestrictionNode.java
new file mode 100644
index 0000000000..8a0dbe6367
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/DateRestrictionNode.java
@@ -0,0 +1,12 @@
+package com.bakdata.conquery.sql.query.node;
+
+import com.bakdata.conquery.sql.query.range.DateRange;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+
+/** Applies an inclusive date restriction to its child. */
+public record DateRestrictionNode(
+ @NotNull @Valid DateRange dateRange,
+ @NotNull @Valid QueryNode child
+) implements QueryNode {
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/NegationNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/NegationNode.java
new file mode 100644
index 0000000000..4fbe3100f8
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/NegationNode.java
@@ -0,0 +1,8 @@
+package com.bakdata.conquery.sql.query.node;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+
+/** Logical negation. */
+public record NegationNode(@NotNull @Valid QueryNode child) implements QueryNode {
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/OrNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/OrNode.java
new file mode 100644
index 0000000000..14cdb6c9b6
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/OrNode.java
@@ -0,0 +1,20 @@
+package com.bakdata.conquery.sql.query.node;
+
+import java.util.List;
+
+import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+
+/** Logical disjunction with its already-derived validity-date behavior. */
+public record OrNode(
+ @NotEmpty List<@NotNull @Valid QueryNode> children,
+ @NotNull DateAggregationAction dateAction,
+ boolean createExists
+) implements QueryNode {
+
+ public OrNode {
+ children = ModelNormalization.immutableCopy(children);
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/QueryNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/QueryNode.java
similarity index 79%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/QueryNode.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/QueryNode.java
index 8813838524..23f932c988 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/QueryNode.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/QueryNode.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query;
+package com.bakdata.conquery.sql.query.node;
/** A normalized query-tree node accepted by SQL compilation. */
public sealed interface QueryNode permits AndNode, OrNode, NegationNode, DateRestrictionNode, ConceptNode {
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/package-info.java
new file mode 100644
index 0000000000..df44c5f5ee
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/package-info.java
@@ -0,0 +1,2 @@
+/** Normalized nodes of a fully resolved query tree. */
+package com.bakdata.conquery.sql.query.node;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInAggregations.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInAggregations.java
new file mode 100644
index 0000000000..e4b0fb50cb
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInAggregations.java
@@ -0,0 +1,132 @@
+package com.bakdata.conquery.sql.query.operation;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import com.bakdata.conquery.models.datasets.ColumnType;
+import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import com.bakdata.conquery.sql.query.schema.DateColumns;
+import com.bakdata.conquery.sql.query.schema.ResolvedColumn;
+import com.bakdata.conquery.sql.query.validation.AllowedColumnTypes;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+
+/** Framework-neutral aggregation operations supported by the SQL connector. */
+public final class BuiltInAggregations {
+
+ private BuiltInAggregations() {
+ }
+
+ public record Count(
+ @NotNull @Valid ResolvedColumn column,
+ @NotNull List<@NotNull @Valid ResolvedColumn> distinctBy
+ ) implements ResolvedAggregation {
+
+ public Count {
+ distinctBy = ModelNormalization.immutableCopy(distinctBy);
+ }
+
+ @AssertTrue(message = "count columns must belong to the same table")
+ public boolean isOnSameTable() {
+ return sameTable(withColumn(column, distinctBy));
+ }
+ }
+
+ public record CountQuarters(@NotNull @Valid DateColumns dates) implements ResolvedAggregation {
+ }
+
+ public record DurationSum(
+ @NotNull @Valid DateColumns dates,
+ @NotNull List<@NotNull @Valid ResolvedColumn> distinctBy
+ ) implements ResolvedAggregation {
+
+ public DurationSum {
+ distinctBy = ModelNormalization.immutableCopy(distinctBy);
+ }
+
+ @AssertTrue(message = "duration-sum columns must belong to the same table")
+ public boolean isOnSameTable() {
+ return dates == null || sameTable(withColumns(dates.columns(), distinctBy));
+ }
+ }
+
+ public record Sum(
+ @NotNull @Valid @AllowedColumnTypes({ColumnType.INTEGER, ColumnType.REAL, ColumnType.DECIMAL, ColumnType.MONEY})
+ ResolvedColumn column,
+ @NotNull Optional<@Valid @AllowedColumnTypes({
+ ColumnType.INTEGER, ColumnType.REAL, ColumnType.DECIMAL, ColumnType.MONEY
+ }) ResolvedColumn> subtractColumn,
+ @NotNull List<@NotNull @Valid ResolvedColumn> distinctBy
+ ) implements ResolvedAggregation {
+
+ public Sum {
+ distinctBy = ModelNormalization.immutableCopy(distinctBy);
+ }
+
+ @AssertTrue(message = "column and subtractColumn must have the same type")
+ public boolean isMatchingColumnType() {
+ return column == null || subtractColumn == null || subtractColumn.isEmpty()
+ || column.type() == subtractColumn.get().type();
+ }
+
+ @AssertTrue(message = "sum columns must belong to the same table")
+ public boolean isOnSameTable() {
+ if (subtractColumn == null) {
+ return true;
+ }
+ List columns = withColumn(column, distinctBy);
+ subtractColumn.ifPresent(columns::add);
+ return sameTable(columns);
+ }
+ }
+
+ public record Flags(
+ @NotEmpty Map<@NotBlank String, @NotNull @Valid @AllowedColumnTypes(ColumnType.BOOLEAN) ResolvedColumn> columns
+ ) implements ResolvedAggregation {
+
+ public Flags {
+ columns = ModelNormalization.immutableCopy(columns);
+ }
+
+ @AssertTrue(message = "flag columns must be unique")
+ public boolean isUnique() {
+ return columns == null || columns.values().stream().distinct().count() == columns.size();
+ }
+
+ @AssertTrue(message = "flag columns must belong to the same table")
+ public boolean isOnSameTable() {
+ return columns == null || sameTable(columns.values());
+ }
+ }
+
+ private static List withColumn(ResolvedColumn column, List other) {
+ List columns = new ArrayList<>();
+ if (column != null) {
+ columns.add(column);
+ }
+ return withColumns(columns, other);
+ }
+
+ private static List withColumns(List columns, List other) {
+ List combined = new ArrayList<>(columns);
+ if (other != null) {
+ combined.addAll(other);
+ }
+ return combined;
+ }
+
+ private static boolean sameTable(Collection columns) {
+ return columns.stream()
+ .filter(java.util.Objects::nonNull)
+ .map(ResolvedColumn::table)
+ .distinct()
+ .limit(2)
+ .count() < 2;
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInConditions.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInConditions.java
new file mode 100644
index 0000000000..10e7215273
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInConditions.java
@@ -0,0 +1,69 @@
+package com.bakdata.conquery.sql.query.operation;
+
+import java.util.List;
+import java.util.Set;
+
+import com.bakdata.conquery.models.datasets.ColumnType;
+import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import com.bakdata.conquery.sql.query.schema.ResolvedColumn;
+import com.bakdata.conquery.sql.query.validation.AllowedColumnTypes;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+
+/** Declarative connector and concept-element conditions supported by the SQL connector. */
+public final class BuiltInConditions {
+
+ private BuiltInConditions() {
+ }
+
+ public record AllOf(
+ @NotEmpty List<@NotNull @Valid ResolvedCondition> conditions
+ ) implements ResolvedCondition {
+
+ public AllOf {
+ conditions = ModelNormalization.immutableCopy(conditions);
+ }
+ }
+
+ public record Not(@NotNull @Valid ResolvedCondition condition) implements ResolvedCondition {
+ }
+
+ public record StringValues(
+ @NotNull @Valid @AllowedColumnTypes(ColumnType.STRING) ResolvedColumn column,
+ @NotEmpty Set<@NotNull String> values
+ ) implements ResolvedCondition {
+
+ public StringValues {
+ values = ModelNormalization.immutableCopy(values);
+ }
+ }
+
+ public record Presence(@NotNull @Valid ResolvedColumn column, boolean present) implements ResolvedCondition {
+ }
+
+ public record Prefixes(
+ @NotNull @Valid @AllowedColumnTypes(ColumnType.STRING) ResolvedColumn column,
+ @NotEmpty List<@NotBlank String> prefixes
+ ) implements ResolvedCondition {
+
+ public Prefixes {
+ prefixes = ModelNormalization.immutableCopy(prefixes);
+ }
+ }
+
+ public record PrefixRange(
+ @NotNull @Valid @AllowedColumnTypes(ColumnType.STRING) ResolvedColumn column,
+ @NotBlank String minimum,
+ @NotBlank String maximum
+ ) implements ResolvedCondition {
+
+ @AssertTrue(message = "prefix bounds must have equal length and be ordered")
+ public boolean isOrdered() {
+ return minimum == null || maximum == null
+ || minimum.length() == maximum.length() && minimum.compareTo(maximum) <= 0;
+ }
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInFilters.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInFilters.java
new file mode 100644
index 0000000000..c44cbcb4d4
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInFilters.java
@@ -0,0 +1,81 @@
+package com.bakdata.conquery.sql.query.operation;
+
+import java.time.LocalDate;
+import java.time.temporal.ChronoUnit;
+import java.util.Optional;
+import java.util.Set;
+
+import com.bakdata.conquery.models.datasets.ColumnType;
+import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import com.bakdata.conquery.sql.query.range.NumberRange;
+import com.bakdata.conquery.sql.query.range.SubstringRange;
+import com.bakdata.conquery.sql.query.schema.ResolvedColumn;
+import com.bakdata.conquery.sql.query.validation.AllowedColumnTypes;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+
+/** Framework-neutral filter operations supported by the SQL connector. */
+public final class BuiltInFilters {
+
+ private BuiltInFilters() {
+ }
+
+ public record StringValues(
+ @NotNull @Valid @AllowedColumnTypes(ColumnType.STRING) ResolvedColumn column,
+ @NotEmpty Set<@NotNull String> values,
+ @NotNull Optional<@Valid SubstringRange> substring
+ ) implements ResolvedFilter {
+
+ public StringValues {
+ values = ModelNormalization.immutableCopy(values);
+ }
+
+ public StringValues(ResolvedColumn column, Set values) {
+ this(column, values, Optional.empty());
+ }
+ }
+
+ public record NumericColumnRange(
+ @NotNull @Valid @AllowedColumnTypes({ColumnType.INTEGER, ColumnType.REAL, ColumnType.DECIMAL, ColumnType.MONEY})
+ ResolvedColumn column,
+ @NotNull @Valid NumberRange range
+ ) implements ResolvedFilter {
+ }
+
+ public record AggregationRange(
+ @NotNull @Valid ResolvedAggregation aggregation,
+ @NotNull @Valid NumberRange range
+ ) implements ResolvedFilter {
+ }
+
+ /** Filters by distance to an end date that was frozen while resolving the query. */
+ public record DateDistanceRange(
+ @NotNull @Valid @AllowedColumnTypes(ColumnType.DATE) ResolvedColumn column,
+ @NotNull ChronoUnit unit,
+ @NotNull LocalDate endDate,
+ @NotNull @Valid NumberRange range
+ ) implements ResolvedFilter {
+ }
+
+ public record Flags(
+ @NotNull @Valid BuiltInAggregations.Flags availableFlags,
+ @NotEmpty Set<@NotNull String> selectedFlags
+ ) implements ResolvedFilter {
+
+ public Flags {
+ selectedFlags = ModelNormalization.immutableCopy(selectedFlags);
+ }
+
+ public Flags(java.util.Map availableFlags, Set selectedFlags) {
+ this(new BuiltInAggregations.Flags(availableFlags), selectedFlags);
+ }
+
+ @AssertTrue(message = "selectedFlags must be defined by availableFlags")
+ public boolean isSelectionKnown() {
+ return availableFlags == null || availableFlags.columns() == null || selectedFlags == null
+ || availableFlags.columns().keySet().containsAll(selectedFlags);
+ }
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInSelects.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInSelects.java
new file mode 100644
index 0000000000..b06d8f4b00
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInSelects.java
@@ -0,0 +1,76 @@
+package com.bakdata.conquery.sql.query.operation;
+
+import java.time.LocalDate;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+import java.util.Optional;
+
+import com.bakdata.conquery.models.datasets.ColumnType;
+import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import com.bakdata.conquery.sql.query.range.SubstringRange;
+import com.bakdata.conquery.sql.query.schema.DateColumns;
+import com.bakdata.conquery.sql.query.schema.ResolvedColumn;
+import com.bakdata.conquery.sql.query.validation.AllowedColumnTypes;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+
+/** Framework-neutral select operations supported by the SQL connector. */
+public final class BuiltInSelects {
+
+ private BuiltInSelects() {
+ }
+
+ public enum ValueOperation {
+ DISTINCT,
+ FIRST,
+ LAST,
+ RANDOM
+ }
+
+ public record Aggregation(@NotNull @Valid ResolvedAggregation aggregation) implements ResolvedSelect {
+ }
+
+ public record Values(
+ @NotNull @Valid ResolvedColumn column,
+ @NotNull ValueOperation operation,
+ @NotNull Optional<@Valid SubstringRange> substring
+ ) implements ResolvedSelect {
+
+ @AssertTrue(message = "substring requires a STRING column")
+ public boolean isSubstringOnStringColumn() {
+ return column == null || substring == null || substring.isEmpty() || column.type() == ColumnType.STRING;
+ }
+ }
+
+ public record DateUnion(@NotNull @Valid DateColumns dates) implements ResolvedSelect {
+ }
+
+ /** Selects the distance to an end date that was frozen while resolving the query. */
+ public record DateDistance(
+ @NotNull @Valid @AllowedColumnTypes({ColumnType.DATE, ColumnType.DATE_RANGE}) ResolvedColumn column,
+ @NotNull ChronoUnit unit,
+ @NotNull LocalDate endDate
+ ) implements ResolvedSelect {
+ }
+
+ /** Aggregates the resolved concept-value columns across connector tables. */
+ public record ConceptValues(
+ @NotEmpty List<@NotNull @Valid ResolvedColumn> columns
+ ) implements ResolvedSelect {
+
+ public ConceptValues {
+ columns = ModelNormalization.immutableCopy(columns);
+ }
+ }
+
+ public record EventDateUnion() implements ResolvedSelect {
+ }
+
+ public record EventDurationSum() implements ResolvedSelect {
+ }
+
+ public record Exists() implements ResolvedSelect {
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedAggregation.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedAggregation.java
new file mode 100644
index 0000000000..b33eff1500
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedAggregation.java
@@ -0,0 +1,5 @@
+package com.bakdata.conquery.sql.query.operation;
+
+/** A resolved aggregation that can be exposed as a result or constrained by an aggregation filter. */
+public interface ResolvedAggregation {
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedCondition.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedCondition.java
similarity index 66%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedCondition.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedCondition.java
index 2e9255bc53..7ce1db4d1b 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedCondition.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedCondition.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query;
+package com.bakdata.conquery.sql.query.operation;
/** A resolved connector or concept-element condition. */
public interface ResolvedCondition {
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedFilter.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedFilter.java
similarity index 83%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedFilter.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedFilter.java
index c6712c958b..5a2eef35fa 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedFilter.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedFilter.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query;
+package com.bakdata.conquery.sql.query.operation;
/**
* Marker for an immutable, validated filter operation.
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedSelect.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedSelect.java
similarity index 80%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedSelect.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedSelect.java
index 44cb0b2707..c9210791dd 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedSelect.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedSelect.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query;
+package com.bakdata.conquery.sql.query.operation;
/**
* Marker for an immutable, validated select operation.
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/package-info.java
new file mode 100644
index 0000000000..381f90f0fd
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/package-info.java
@@ -0,0 +1,2 @@
+/** Extensible filter, select, condition, and aggregation operations accepted by SQL compilation. */
+package com.bakdata.conquery.sql.query.operation;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/package-info.java
index 75e6f6214f..bd3cf6dbf2 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/package-info.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/package-info.java
@@ -4,5 +4,8 @@
* Instances in this package must not require repository access or identifier resolution during compilation. They
* describe query semantics and physical database objects, but deliberately exclude connections, dialects, and SQL
* compiler state.
+ *
+ * The root query aggregate is complemented by dedicated packages for query nodes, operations, ranges, result
+ * metadata, physical schema references, and validation.
*/
package com.bakdata.conquery.sql.query;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/DateRange.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/DateRange.java
new file mode 100644
index 0000000000..5251e45b95
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/DateRange.java
@@ -0,0 +1,25 @@
+package com.bakdata.conquery.sql.query.range;
+
+import java.time.LocalDate;
+import java.util.Optional;
+
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotNull;
+
+/** Inclusive date restriction. An empty bound represents an unbounded side. */
+public record DateRange(
+ @NotNull Optional startInclusive,
+ @NotNull Optional endInclusive
+) {
+
+ @AssertTrue(message = "startInclusive must not be after endInclusive")
+ public boolean isOrdered() {
+ return startInclusive == null || endInclusive == null
+ || startInclusive.isEmpty() || endInclusive.isEmpty()
+ || !startInclusive.get().isAfter(endInclusive.get());
+ }
+
+ public static DateRange closed(LocalDate startInclusive, LocalDate endInclusive) {
+ return new DateRange(Optional.of(startInclusive), Optional.of(endInclusive));
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/NumberRange.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/NumberRange.java
new file mode 100644
index 0000000000..82c91aef72
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/NumberRange.java
@@ -0,0 +1,43 @@
+package com.bakdata.conquery.sql.query.range;
+
+import java.math.BigDecimal;
+import java.util.Objects;
+import java.util.Optional;
+
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotNull;
+
+/** Inclusive numeric range. An empty bound represents an unbounded side. */
+public record NumberRange(
+ @NotNull Optional minimum,
+ @NotNull Optional maximum
+) {
+
+ @AssertTrue(message = "minimum must not be greater than maximum")
+ public boolean isOrdered() {
+ return minimum == null || maximum == null
+ || minimum.isEmpty() || maximum.isEmpty()
+ || minimum.get().compareTo(maximum.get()) <= 0;
+ }
+
+ public static NumberRange closed(Number minimum, Number maximum) {
+ return new NumberRange(Optional.of(decimal(minimum)), Optional.of(decimal(maximum)));
+ }
+
+ public static NumberRange atLeast(Number minimum) {
+ return new NumberRange(Optional.of(decimal(minimum)), Optional.empty());
+ }
+
+ public static NumberRange atMost(Number maximum) {
+ return new NumberRange(Optional.empty(), Optional.of(decimal(maximum)));
+ }
+
+ public static NumberRange unbounded() {
+ return new NumberRange(Optional.empty(), Optional.empty());
+ }
+
+ private static BigDecimal decimal(Number value) {
+ Objects.requireNonNull(value, "value");
+ return value instanceof BigDecimal decimal ? decimal : new BigDecimal(value.toString());
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/SubstringRange.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/SubstringRange.java
new file mode 100644
index 0000000000..ce45606115
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/SubstringRange.java
@@ -0,0 +1,27 @@
+package com.bakdata.conquery.sql.query.range;
+
+import java.util.Optional;
+
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.PositiveOrZero;
+
+/** Zero-based substring bounds with an exclusive, optionally unbounded end. */
+public record SubstringRange(
+ @PositiveOrZero int startInclusive,
+ @NotNull Optional<@PositiveOrZero Integer> endExclusive
+) {
+
+ @AssertTrue(message = "endExclusive must not be smaller than startInclusive")
+ public boolean isOrdered() {
+ return endExclusive == null || endExclusive.isEmpty() || endExclusive.get() >= startInclusive;
+ }
+
+ public static SubstringRange from(int startInclusive) {
+ return new SubstringRange(startInclusive, Optional.empty());
+ }
+
+ public static SubstringRange between(int startInclusive, int endExclusive) {
+ return new SubstringRange(startInclusive, Optional.of(endExclusive));
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/package-info.java
new file mode 100644
index 0000000000..a9ec695bf5
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/package-info.java
@@ -0,0 +1,2 @@
+/** Typed, framework-neutral range values used by resolved nodes and operations. */
+package com.bakdata.conquery.sql.query.range;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/ResultColumn.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/ResultColumn.java
new file mode 100644
index 0000000000..9d7a3619d5
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/ResultColumn.java
@@ -0,0 +1,29 @@
+package com.bakdata.conquery.sql.query.result;
+
+import java.util.Optional;
+import java.util.Set;
+
+import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+/** Ordered output-column metadata, with presentation names already resolved for the query locale. */
+public record ResultColumn(
+ @NotBlank String label,
+ @NotNull Optional defaultLabel,
+ @NotNull Optional description,
+ @NotNull @Valid ResultType type,
+ @NotNull Set<@NotBlank String> semantics
+) {
+
+ public ResultColumn {
+ semantics = ModelNormalization.immutableCopy(semantics);
+ }
+
+ @AssertTrue(message = "defaultLabel must not be blank when present")
+ public boolean isDefaultLabelValid() {
+ return defaultLabel == null || defaultLabel.isEmpty() || !defaultLabel.get().isBlank();
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultType.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/ResultType.java
similarity index 54%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultType.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/ResultType.java
index 929c1af930..a1bb6826af 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResultType.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/ResultType.java
@@ -1,6 +1,6 @@
-package com.bakdata.conquery.sql.query;
+package com.bakdata.conquery.sql.query.result;
-import java.util.Objects;
+import jakarta.validation.constraints.NotNull;
/** Logical type of a result column. */
public sealed interface ResultType permits ResultType.Primitive, ResultType.ListType {
@@ -15,9 +15,6 @@ enum Primitive implements ResultType {
MONEY
}
- record ListType(Primitive elementType) implements ResultType {
- public ListType {
- Objects.requireNonNull(elementType, "elementType");
- }
+ record ListType(@NotNull Primitive elementType) implements ResultType {
}
}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/package-info.java
new file mode 100644
index 0000000000..54870eda8a
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/package-info.java
@@ -0,0 +1,2 @@
+/** Ordered result-column metadata and logical result types. */
+package com.bakdata.conquery.sql.query.result;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/DateColumns.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/DateColumns.java
new file mode 100644
index 0000000000..ea566c006c
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/DateColumns.java
@@ -0,0 +1,42 @@
+package com.bakdata.conquery.sql.query.schema;
+
+import java.util.List;
+import java.util.Objects;
+
+import com.bakdata.conquery.models.datasets.ColumnType;
+import com.bakdata.conquery.sql.query.validation.AllowedColumnTypes;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotNull;
+
+/** Resolved physical representation of a date or date range used by an operation. */
+public sealed interface DateColumns permits DateColumns.Single, DateColumns.Pair {
+
+ List columns();
+
+ record Single(
+ @NotNull @Valid @AllowedColumnTypes({ColumnType.DATE, ColumnType.DATE_RANGE}) ResolvedColumn column
+ ) implements DateColumns {
+
+ @Override
+ public List columns() {
+ return column == null ? List.of() : List.of(column);
+ }
+ }
+
+ record Pair(
+ @NotNull @Valid @AllowedColumnTypes(ColumnType.DATE) ResolvedColumn start,
+ @NotNull @Valid @AllowedColumnTypes(ColumnType.DATE) ResolvedColumn end
+ ) implements DateColumns {
+
+ @Override
+ public List columns() {
+ return start == null || end == null ? List.of() : List.of(start, end);
+ }
+
+ @AssertTrue(message = "date columns must belong to the same table")
+ public boolean isOnSameTable() {
+ return start == null || end == null || Objects.equals(start.table(), end.table());
+ }
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/EntitySchema.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/EntitySchema.java
new file mode 100644
index 0000000000..212e1ab712
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/EntitySchema.java
@@ -0,0 +1,17 @@
+package com.bakdata.conquery.sql.query.schema;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotNull;
+
+/** Physical source used to enumerate all entities, for example when compiling a root-level negation. */
+public record EntitySchema(
+ @NotNull @Valid SqlTable table,
+ @NotNull @Valid ResolvedColumn primaryId
+) {
+
+ @AssertTrue(message = "primaryId must belong to the entity table")
+ public boolean isPrimaryIdOnEntityTable() {
+ return table == null || primaryId == null || table.equals(primaryId.table());
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedColumn.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedColumn.java
new file mode 100644
index 0000000000..ff285f38b9
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedColumn.java
@@ -0,0 +1,16 @@
+package com.bakdata.conquery.sql.query.schema;
+
+import com.bakdata.conquery.models.datasets.ColumnType;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+/** A column whose owning table, physical name, and logical type are known. */
+public record ResolvedColumn(
+ @NotBlank String logicalId,
+ @NotNull @Valid SqlTable table,
+ @NotBlank String physicalName,
+ @NotNull ColumnType type,
+ boolean nullable
+) {
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedConnector.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedConnector.java
new file mode 100644
index 0000000000..d23a9ec1af
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedConnector.java
@@ -0,0 +1,59 @@
+package com.bakdata.conquery.sql.query.schema;
+
+import java.util.List;
+import java.util.Optional;
+
+import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import com.bakdata.conquery.sql.query.operation.ResolvedCondition;
+import com.bakdata.conquery.sql.query.operation.ResolvedFilter;
+import com.bakdata.conquery.sql.query.operation.ResolvedSelect;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+/** A selected connector with all physical columns and operations resolved. */
+public record ResolvedConnector(
+ @NotBlank String logicalId,
+ @NotNull @Valid SqlTable table,
+ @NotNull @Valid ResolvedColumn primaryId,
+ @NotNull Optional<@Valid ResolvedColumn> secondaryId,
+ @NotNull @Valid ResolvedValidityDate validityDate,
+ @NotNull List<@NotNull @Valid ResolvedFilter> filters,
+ @NotNull List<@NotNull @Valid ResolvedSelect> selects,
+ @NotNull List<@NotNull @Valid ResolvedCondition> conditions
+) {
+
+ public ResolvedConnector {
+ filters = ModelNormalization.immutableCopy(filters);
+ selects = ModelNormalization.immutableCopy(selects);
+ conditions = ModelNormalization.immutableCopy(conditions);
+ }
+
+ @AssertTrue(message = "identifier columns must belong to the connector table")
+ public boolean isIdentifierColumnsOnConnectorTable() {
+ return table == null
+ || primaryId == null
+ || secondaryId == null
+ || table.equals(primaryId.table()) && secondaryId.stream().map(ResolvedColumn::table).allMatch(table::equals);
+ }
+
+ @AssertTrue(message = "validity-date columns must belong to the connector table")
+ public boolean isValidityDateOnConnectorTable() {
+ if (validityDate == null || table == null) {
+ return true;
+ }
+ switch (validityDate) {
+ case ResolvedValidityDate.None ignored -> {
+ return true;
+ }
+ case ResolvedValidityDate.Point point -> {
+ return point.column() == null || table.equals(point.column().table());
+ }
+ case ResolvedValidityDate.Range range -> {
+ return range.start() == null || range.end() == null
+ || table.equals(range.start().table()) && table.equals(range.end().table());
+ }
+ }
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedValidityDate.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedValidityDate.java
new file mode 100644
index 0000000000..73d71f6179
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedValidityDate.java
@@ -0,0 +1,32 @@
+package com.bakdata.conquery.sql.query.schema;
+
+import java.util.Objects;
+
+import com.bakdata.conquery.models.datasets.ColumnType;
+import com.bakdata.conquery.sql.query.validation.AllowedColumnTypes;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotNull;
+
+/** Physical representation of event validity for a connector. */
+public sealed interface ResolvedValidityDate permits ResolvedValidityDate.None, ResolvedValidityDate.Point, ResolvedValidityDate.Range {
+
+ record None() implements ResolvedValidityDate {
+ }
+
+ record Point(
+ @NotNull @Valid @AllowedColumnTypes(ColumnType.DATE) ResolvedColumn column
+ ) implements ResolvedValidityDate {
+ }
+
+ record Range(
+ @NotNull @Valid @AllowedColumnTypes(ColumnType.DATE) ResolvedColumn start,
+ @NotNull @Valid @AllowedColumnTypes(ColumnType.DATE) ResolvedColumn end
+ ) implements ResolvedValidityDate {
+
+ @AssertTrue(message = "validity-date columns must belong to the same table")
+ public boolean isOnSameTable() {
+ return start == null || end == null || Objects.equals(start.table(), end.table());
+ }
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/SqlTable.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/SqlTable.java
new file mode 100644
index 0000000000..e927aaaa57
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/SqlTable.java
@@ -0,0 +1,26 @@
+package com.bakdata.conquery.sql.query.schema;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+
+/** A logical table together with its unquoted physical SQL name. */
+public record SqlTable(
+ @NotBlank String logicalId,
+ @NotEmpty List<@NotBlank String> physicalName
+) {
+
+ public SqlTable {
+ physicalName = ModelNormalization.immutableCopy(physicalName);
+ }
+
+ public static SqlTable of(String logicalId, String... physicalName) {
+ if (physicalName == null) {
+ return new SqlTable(logicalId, null);
+ }
+ return new SqlTable(logicalId, Arrays.asList(physicalName));
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/package-info.java
new file mode 100644
index 0000000000..1b56e9adc2
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/package-info.java
@@ -0,0 +1,2 @@
+/** Resolved physical tables, columns, connectors, and entity schema used during SQL compilation. */
+package com.bakdata.conquery.sql.query.schema;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/AllowedColumnTypes.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/AllowedColumnTypes.java
new file mode 100644
index 0000000000..9f7b5beeba
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/AllowedColumnTypes.java
@@ -0,0 +1,26 @@
+package com.bakdata.conquery.sql.query.validation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.bakdata.conquery.models.datasets.ColumnType;
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+
+@Documented
+@Constraint(validatedBy = AllowedColumnTypesValidator.class)
+@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE_USE, ElementType.RECORD_COMPONENT})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AllowedColumnTypes {
+
+ String message() default "column type is not supported";
+
+ Class>[] groups() default {};
+
+ Class extends Payload>[] payload() default {};
+
+ ColumnType[] value();
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/AllowedColumnTypesValidator.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/AllowedColumnTypesValidator.java
new file mode 100644
index 0000000000..d79d3703be
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/AllowedColumnTypesValidator.java
@@ -0,0 +1,23 @@
+package com.bakdata.conquery.sql.query.validation;
+
+import java.util.EnumSet;
+
+import com.bakdata.conquery.models.datasets.ColumnType;
+import com.bakdata.conquery.sql.query.schema.ResolvedColumn;
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+
+public final class AllowedColumnTypesValidator implements ConstraintValidator {
+
+ private EnumSet allowedTypes;
+
+ @Override
+ public void initialize(AllowedColumnTypes constraint) {
+ allowedTypes = EnumSet.copyOf(java.util.List.of(constraint.value()));
+ }
+
+ @Override
+ public boolean isValid(ResolvedColumn column, ConstraintValidatorContext context) {
+ return column == null || column.type() == null || allowedTypes.contains(column.type());
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/ResolvedQueryValidation.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/ResolvedQueryValidation.java
new file mode 100644
index 0000000000..5991b5e759
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/ResolvedQueryValidation.java
@@ -0,0 +1,26 @@
+package com.bakdata.conquery.sql.query.validation;
+
+import java.util.Objects;
+import java.util.Set;
+
+import com.bakdata.conquery.sql.query.ResolvedQuery;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.ConstraintViolationException;
+import jakarta.validation.Validator;
+
+/** Validates the complete resolved query graph before SQL compilation starts. */
+public final class ResolvedQueryValidation {
+
+ private final Validator validator;
+
+ public ResolvedQueryValidation(Validator validator) {
+ this.validator = Objects.requireNonNull(validator, "validator");
+ }
+
+ public void validate(ResolvedQuery query) {
+ Set> violations = validator.validate(Objects.requireNonNull(query, "query"));
+ if (!violations.isEmpty()) {
+ throw new ConstraintViolationException(violations);
+ }
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/package-info.java
new file mode 100644
index 0000000000..06434d8420
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/package-info.java
@@ -0,0 +1,2 @@
+/** Jakarta Bean Validation constraints and the validation boundary for resolved queries. */
+package com.bakdata.conquery.sql.query.validation;
diff --git a/sql-connector/src/test/java/com/bakdata/conquery/sql/query/BuiltInOperationsTest.java b/sql-connector/src/test/java/com/bakdata/conquery/sql/query/BuiltInOperationsTest.java
new file mode 100644
index 0000000000..4dcc56e333
--- /dev/null
+++ b/sql-connector/src/test/java/com/bakdata/conquery/sql/query/BuiltInOperationsTest.java
@@ -0,0 +1,164 @@
+package com.bakdata.conquery.sql.query;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static com.bakdata.conquery.sql.query.ValidationTestSupport.assertInvalid;
+import static com.bakdata.conquery.sql.query.ValidationTestSupport.assertValid;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import com.bakdata.conquery.models.datasets.ColumnType;
+import com.bakdata.conquery.sql.query.operation.BuiltInAggregations;
+import com.bakdata.conquery.sql.query.operation.BuiltInConditions;
+import com.bakdata.conquery.sql.query.operation.BuiltInFilters;
+import com.bakdata.conquery.sql.query.operation.BuiltInSelects;
+import com.bakdata.conquery.sql.query.operation.ResolvedCondition;
+import com.bakdata.conquery.sql.query.operation.ResolvedFilter;
+import com.bakdata.conquery.sql.query.operation.ResolvedSelect;
+import com.bakdata.conquery.sql.query.range.NumberRange;
+import com.bakdata.conquery.sql.query.range.SubstringRange;
+import com.bakdata.conquery.sql.query.schema.DateColumns;
+import com.bakdata.conquery.sql.query.schema.ResolvedColumn;
+import com.bakdata.conquery.sql.query.schema.SqlTable;
+import org.junit.jupiter.api.Test;
+
+class BuiltInOperationsTest {
+
+ private static final SqlTable EVENTS = SqlTable.of("events", "analytics", "events");
+ private static final SqlTable OTHER_EVENTS = SqlTable.of("other-events", "analytics", "other_events");
+ private static final ResolvedColumn CODE = column("code", ColumnType.STRING);
+ private static final ResolvedColumn AMOUNT = column("amount", ColumnType.DECIMAL);
+ private static final ResolvedColumn START = column("start_date", ColumnType.DATE);
+ private static final ResolvedColumn END = column("end_date", ColumnType.DATE);
+ private static final ResolvedColumn FLAG_A = column("flag_a", ColumnType.BOOLEAN);
+ private static final ResolvedColumn FLAG_B = column("flag_b", ColumnType.BOOLEAN);
+
+ @Test
+ void shouldRepresentStandardFiltersWithResolvedInputs() {
+ ResolvedFilter values = new BuiltInFilters.StringValues(
+ CODE, Set.of("A", "B"), Optional.of(SubstringRange.between(0, 3))
+ );
+ ResolvedFilter number = new BuiltInFilters.NumericColumnRange(AMOUNT, NumberRange.closed(1, 10));
+ ResolvedFilter aggregation = new BuiltInFilters.AggregationRange(
+ new BuiltInAggregations.Sum(AMOUNT, Optional.empty(), List.of()),
+ NumberRange.atLeast(BigDecimal.TEN)
+ );
+ ResolvedFilter distance = new BuiltInFilters.DateDistanceRange(
+ START, ChronoUnit.YEARS, LocalDate.of(2026, 8, 27), NumberRange.atMost(65)
+ );
+ ResolvedFilter flags = new BuiltInFilters.Flags(
+ Map.of("A", FLAG_A, "B", FLAG_B), Set.of("A")
+ );
+
+ assertValid(values);
+ assertValid(number);
+ assertValid(aggregation);
+ assertValid(distance);
+ assertValid(flags);
+ assertInstanceOf(BuiltInFilters.StringValues.class, values);
+ assertInstanceOf(BuiltInFilters.NumericColumnRange.class, number);
+ assertInstanceOf(BuiltInFilters.AggregationRange.class, aggregation);
+ assertInstanceOf(BuiltInFilters.DateDistanceRange.class, distance);
+ assertInstanceOf(BuiltInFilters.Flags.class, flags);
+ }
+
+ @Test
+ void shouldRepresentStandardSelectsWithResolvedInputs() {
+ List selects = List.of(
+ new BuiltInSelects.Aggregation(new BuiltInAggregations.Count(CODE, List.of(CODE))),
+ new BuiltInSelects.Aggregation(new BuiltInAggregations.CountQuarters(new DateColumns.Pair(START, END))),
+ new BuiltInSelects.Aggregation(new BuiltInAggregations.DurationSum(new DateColumns.Pair(START, END), List.of(CODE))),
+ new BuiltInSelects.Aggregation(new BuiltInAggregations.Flags(Map.of("A", FLAG_A, "B", FLAG_B))),
+ new BuiltInSelects.Values(
+ CODE, BuiltInSelects.ValueOperation.DISTINCT, Optional.of(SubstringRange.between(0, 3))
+ ),
+ new BuiltInSelects.DateUnion(new DateColumns.Pair(START, END)),
+ new BuiltInSelects.DateDistance(START, ChronoUnit.DAYS, LocalDate.of(2026, 8, 27)),
+ new BuiltInSelects.ConceptValues(List.of(CODE)),
+ new BuiltInSelects.EventDateUnion(),
+ new BuiltInSelects.EventDurationSum(),
+ new BuiltInSelects.Exists()
+ );
+
+ selects.forEach(ValidationTestSupport::assertValid);
+ assertEquals(11, selects.size());
+ }
+
+ @Test
+ void shouldRepresentDeclarativeConditions() {
+ ResolvedCondition condition = new BuiltInConditions.AllOf(List.of(
+ new BuiltInConditions.StringValues(CODE, Set.of("A")),
+ new BuiltInConditions.Presence(AMOUNT, true),
+ new BuiltInConditions.Prefixes(CODE, List.of("AB", "CD")),
+ new BuiltInConditions.Not(new BuiltInConditions.PrefixRange(CODE, "10", "20"))
+ ));
+
+ assertValid(condition);
+ assertEquals(4, assertInstanceOf(BuiltInConditions.AllOf.class, condition).conditions().size());
+ }
+
+ @Test
+ void shouldDefensivelyCopyOperationCollections() {
+ List distinctBy = new ArrayList<>(List.of(CODE));
+ BuiltInAggregations.Count count = new BuiltInAggregations.Count(AMOUNT, distinctBy);
+ distinctBy.clear();
+
+ Map flagColumns = new LinkedHashMap<>();
+ flagColumns.put("A", FLAG_A);
+ flagColumns.put("B", FLAG_B);
+ BuiltInAggregations.Flags flags = new BuiltInAggregations.Flags(flagColumns);
+ flagColumns.clear();
+
+ assertEquals(List.of(CODE), count.distinctBy());
+ assertEquals(List.of("A", "B"), new ArrayList<>(flags.columns().keySet()));
+ assertThrows(UnsupportedOperationException.class, () -> flags.columns().clear());
+ }
+
+ @Test
+ void shouldRejectColumnsFromDifferentTablesInOneOperation() {
+ ResolvedColumn otherColumn = new ResolvedColumn(
+ "other.code", OTHER_EVENTS, "code", ColumnType.STRING, true
+ );
+
+ assertInvalid(new BuiltInAggregations.Count(CODE, List.of(otherColumn)));
+ assertInvalid(new DateColumns.Pair(START, new ResolvedColumn(
+ "other.end", OTHER_EVENTS, "end_date", ColumnType.DATE, true
+ )));
+ }
+
+ @Test
+ void shouldRejectInvalidColumnTypes() {
+ assertInvalid(new BuiltInAggregations.Sum(CODE, Optional.empty(), List.of()));
+ assertInvalid(new BuiltInFilters.DateDistanceRange(
+ CODE, ChronoUnit.DAYS, LocalDate.of(2026, 8, 27), NumberRange.unbounded()
+ ));
+ assertInvalid(new BuiltInSelects.Values(
+ AMOUNT, BuiltInSelects.ValueOperation.FIRST, Optional.of(SubstringRange.from(1))
+ ));
+ }
+
+ @Test
+ void shouldRejectUnknownSelectedFlags() {
+ assertInvalid(new BuiltInFilters.Flags(Map.of("A", FLAG_A), Set.of("missing")));
+ }
+
+ @Test
+ void shouldRejectInvalidRanges() {
+ assertInvalid(NumberRange.closed(2, 1));
+ assertInvalid(SubstringRange.between(3, 2));
+ assertInvalid(new BuiltInConditions.PrefixRange(CODE, "100", "20"));
+ }
+
+ private static ResolvedColumn column(String name, ColumnType type) {
+ return new ResolvedColumn("events." + name, EVENTS, name, type, true);
+ }
+}
diff --git a/sql-connector/src/test/java/com/bakdata/conquery/sql/query/ResolvedQueryTest.java b/sql-connector/src/test/java/com/bakdata/conquery/sql/query/ResolvedQueryTest.java
index 692df371c1..1f4831370f 100644
--- a/sql-connector/src/test/java/com/bakdata/conquery/sql/query/ResolvedQueryTest.java
+++ b/sql-connector/src/test/java/com/bakdata/conquery/sql/query/ResolvedQueryTest.java
@@ -2,6 +2,8 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static com.bakdata.conquery.sql.query.ValidationTestSupport.assertInvalid;
+import static com.bakdata.conquery.sql.query.ValidationTestSupport.assertValid;
import java.time.LocalDate;
import java.util.ArrayList;
@@ -9,6 +11,21 @@
import java.util.Optional;
import java.util.Set;
+import com.bakdata.conquery.models.datasets.ColumnType;
+import com.bakdata.conquery.sql.query.node.AndNode;
+import com.bakdata.conquery.sql.query.node.ConceptNode;
+import com.bakdata.conquery.sql.query.node.DateAggregationAction;
+import com.bakdata.conquery.sql.query.node.QueryNode;
+import com.bakdata.conquery.sql.query.range.DateRange;
+import com.bakdata.conquery.sql.query.result.ResultColumn;
+import com.bakdata.conquery.sql.query.result.ResultType;
+import com.bakdata.conquery.sql.query.schema.EntitySchema;
+import com.bakdata.conquery.sql.query.schema.ResolvedColumn;
+import com.bakdata.conquery.sql.query.schema.ResolvedConnector;
+import com.bakdata.conquery.sql.query.schema.ResolvedValidityDate;
+import com.bakdata.conquery.sql.query.schema.SqlTable;
+import com.bakdata.conquery.sql.query.validation.ResolvedQueryValidation;
+import jakarta.validation.ConstraintViolationException;
import org.junit.jupiter.api.Test;
class ResolvedQueryTest {
@@ -44,6 +61,8 @@ void shouldCreateResolvedQueryWithoutFrameworkTypes() {
))
);
+ assertValid(query);
+ new ResolvedQueryValidation(ValidationTestSupport.VALIDATOR).validate(query);
assertEquals(concept, query.root());
assertEquals("warehouse", query.target().dataSource());
}
@@ -67,7 +86,7 @@ void shouldRejectColumnsFromAnotherConnectorTable() {
"dataset.other.entity_id", otherTable, "entity_id", ColumnType.STRING, false
);
- assertThrows(IllegalArgumentException.class, () -> new ResolvedConnector(
+ ResolvedConnector connector = new ResolvedConnector(
"dataset.concept.connector",
EVENTS,
otherId,
@@ -76,25 +95,50 @@ void shouldRejectColumnsFromAnotherConnectorTable() {
List.of(),
List.of(),
List.of()
- ));
+ );
+
+ assertInvalid(connector);
}
@Test
void shouldRejectNonDateValidityColumns() {
- assertThrows(
- IllegalArgumentException.class,
- () -> new ResolvedValidityDate.Point(ENTITY_ID)
- );
+ assertInvalid(new ResolvedValidityDate.Point(ENTITY_ID));
}
@Test
void shouldRejectInvertedDateRestriction() {
- assertThrows(IllegalArgumentException.class, () -> DateRange.closed(
+ assertInvalid(DateRange.closed(
LocalDate.of(2025, 2, 1),
LocalDate.of(2025, 1, 1)
));
}
+ @Test
+ void shouldValidateTheCompleteQueryGraphAtTheCompilerBoundary() {
+ ResolvedConnector invalidConnector = new ResolvedConnector(
+ "concept.connector",
+ EVENTS,
+ ENTITY_ID,
+ Optional.empty(),
+ new ResolvedValidityDate.Point(ENTITY_ID),
+ List.of(),
+ List.of(),
+ List.of()
+ );
+ ResolvedQuery query = new ResolvedQuery(
+ new ExecutionTarget("dataset", "warehouse"),
+ new EntitySchema(EVENTS, ENTITY_ID),
+ new ConceptNode("concept", List.of(invalidConnector), List.of(), DateAggregationAction.MERGE),
+ true,
+ List.of()
+ );
+
+ assertThrows(
+ ConstraintViolationException.class,
+ () -> new ResolvedQueryValidation(ValidationTestSupport.VALIDATOR).validate(query)
+ );
+ }
+
private static ConceptNode conceptNode(SqlTable table) {
ResolvedColumn id = new ResolvedColumn("concept.id", table, "entity_id", ColumnType.STRING, false);
ResolvedConnector connector = new ResolvedConnector(
diff --git a/sql-connector/src/test/java/com/bakdata/conquery/sql/query/ValidationTestSupport.java b/sql-connector/src/test/java/com/bakdata/conquery/sql/query/ValidationTestSupport.java
new file mode 100644
index 0000000000..748861d6f0
--- /dev/null
+++ b/sql-connector/src/test/java/com/bakdata/conquery/sql/query/ValidationTestSupport.java
@@ -0,0 +1,28 @@
+package com.bakdata.conquery.sql.query;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Set;
+
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
+
+final class ValidationTestSupport {
+
+ static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
+
+ private ValidationTestSupport() {
+ }
+
+ static void assertValid(T value) {
+ Set> violations = VALIDATOR.validate(value);
+ assertTrue(violations.isEmpty(), () -> "Expected no constraint violations, but got: " + violations);
+ }
+
+ static void assertInvalid(T value) {
+ Set> violations = VALIDATOR.validate(value);
+ assertFalse(violations.isEmpty(), "Expected at least one constraint violation");
+ }
+}
From 9a4db7f892455f93aab4f0ca45b218c902457844 Mon Sep 17 00:00:00 2001
From: MT <12283268+thoniTUB@users.noreply.github.com>
Date: Fri, 28 Aug 2026 13:42:39 +0200
Subject: [PATCH 04/81] refactor(sql): separate model and validation packages
Co-authored-by: Codex GPT-5
---
sql-connector/README.md | 23 +++++++++----
.../sql/{query => model}/ExecutionTarget.java | 2 +-
.../sql/{query => model}/ResolvedQuery.java | 10 +++---
.../internal/ModelNormalization.java | 2 +-
.../sql/{query => model}/node/AndNode.java | 4 +--
.../{query => model}/node/ConceptNode.java | 8 ++---
.../node/DateAggregationAction.java | 2 +-
.../node/DateRestrictionNode.java | 4 +--
.../{query => model}/node/NegationNode.java | 2 +-
.../sql/{query => model}/node/OrNode.java | 4 +--
.../sql/{query => model}/node/QueryNode.java | 2 +-
.../{query => model}/node/package-info.java | 2 +-
.../operation/BuiltInAggregations.java | 10 +++---
.../operation/BuiltInConditions.java | 8 ++---
.../operation/BuiltInFilters.java | 12 +++----
.../operation/BuiltInSelects.java | 12 +++----
.../operation/ResolvedAggregation.java | 2 +-
.../operation/ResolvedCondition.java | 2 +-
.../operation/ResolvedFilter.java | 2 +-
.../operation/ResolvedSelect.java | 2 +-
.../operation/package-info.java | 2 +-
.../sql/{query => model}/package-info.java | 5 +--
.../sql/{query => model}/range/DateRange.java | 2 +-
.../{query => model}/range/NumberRange.java | 2 +-
.../range/SubstringRange.java | 2 +-
.../{query => model}/range/package-info.java | 2 +-
.../{query => model}/result/ResultColumn.java | 4 +--
.../{query => model}/result/ResultType.java | 2 +-
.../{query => model}/result/package-info.java | 2 +-
.../{query => model}/schema/DateColumns.java | 4 +--
.../{query => model}/schema/EntitySchema.java | 2 +-
.../schema/ResolvedColumn.java | 2 +-
.../schema/ResolvedConnector.java | 10 +++---
.../schema/ResolvedValidityDate.java | 4 +--
.../sql/{query => model}/schema/SqlTable.java | 4 +--
.../{query => model}/schema/package-info.java | 2 +-
.../bakdata/conquery/sql/package-info.java | 8 +++++
.../sql/query/validation/package-info.java | 2 --
.../validation/AllowedColumnTypes.java | 2 +-
.../AllowedColumnTypesValidator.java | 4 +--
.../validation/ResolvedQueryValidation.java | 4 +--
.../conquery/sql/validation/package-info.java | 6 ++++
.../BuiltInOperationsTest.java | 30 ++++++++---------
.../{query => model}/ResolvedQueryTest.java | 32 +++++++++----------
.../ValidationTestSupport.java | 2 +-
45 files changed, 139 insertions(+), 117 deletions(-)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/ExecutionTarget.java (94%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/ResolvedQuery.java (69%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/internal/ModelNormalization.java (93%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/node/AndNode.java (81%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/node/ConceptNode.java (74%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/node/DateAggregationAction.java (76%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/node/DateRestrictionNode.java (73%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/node/NegationNode.java (81%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/node/OrNode.java (81%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/node/QueryNode.java (79%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/node/package-info.java (55%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/operation/BuiltInAggregations.java (92%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/operation/BuiltInConditions.java (88%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/operation/BuiltInFilters.java (86%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/operation/BuiltInSelects.java (84%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/operation/ResolvedAggregation.java (74%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/operation/ResolvedCondition.java (66%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/operation/ResolvedFilter.java (83%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/operation/ResolvedSelect.java (80%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/operation/package-info.java (66%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/package-info.java (67%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/range/DateRange.java (94%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/range/NumberRange.java (96%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/range/SubstringRange.java (95%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/range/package-info.java (64%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/result/ResultColumn.java (88%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/result/ResultType.java (88%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/result/package-info.java (57%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/schema/DateColumns.java (91%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/schema/EntitySchema.java (92%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/schema/ResolvedColumn.java (90%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/schema/ResolvedConnector.java (86%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/schema/ResolvedValidityDate.java (89%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/schema/SqlTable.java (84%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => model}/schema/package-info.java (68%)
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/package-info.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/package-info.java
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => }/validation/AllowedColumnTypes.java (93%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => }/validation/AllowedColumnTypesValidator.java (85%)
rename sql-connector/src/main/java/com/bakdata/conquery/sql/{query => }/validation/ResolvedQueryValidation.java (87%)
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/validation/package-info.java
rename sql-connector/src/test/java/com/bakdata/conquery/sql/{query => model}/BuiltInOperationsTest.java (87%)
rename sql-connector/src/test/java/com/bakdata/conquery/sql/{query => model}/ResolvedQueryTest.java (81%)
rename sql-connector/src/test/java/com/bakdata/conquery/sql/{query => model}/ValidationTestSupport.java (95%)
diff --git a/sql-connector/README.md b/sql-connector/README.md
index 959b9358b4..c29418cf29 100644
--- a/sql-connector/README.md
+++ b/sql-connector/README.md
@@ -17,16 +17,21 @@ The SQL compiler may perform dialect capability checks, but it must not access a
identifiers. Connections, dialect implementations, compiler state, and execution services are intentionally not part of
the resolved model.
-The model is organized by responsibility:
+The module uses top-level packages as architectural boundaries:
-- `query.node` contains the normalized query tree;
-- `query.operation` contains the open operation interfaces and standard implementations;
-- `query.schema` contains resolved physical tables, columns, connectors, and entity metadata;
-- `query.range` and `query.result` contain shared value objects;
-- `query.validation` contains the validation boundary and reusable constraints.
+- `model` contains only the immutable, fully resolved compiler input;
+- `validation` contains Bean Validation constraints and the explicit validation boundary;
+- SQL business logic belongs in sibling packages such as `conversion`, `dialect`, and `execution`, not below `model`.
+
+The resolved model is organized by responsibility:
+
+- `model.node` contains the normalized query tree;
+- `model.operation` contains the open operation interfaces and standard implementations;
+- `model.schema` contains resolved physical tables, columns, connectors, and entity metadata;
+- `model.range` and `model.result` contain shared value objects.
Shared dataset vocabulary such as `ColumnType` comes from the dependency-free `dataset-model` module. SQL-specific
-physical metadata remains in `query.schema`.
+physical metadata remains in `model.schema`.
`ResolvedFilter`, `ResolvedSelect`, `ResolvedCondition`, and `ResolvedAggregation` are open extension points.
Implementations must be immutable and carry resolved columns and typed values. They must not carry unresolved repository
@@ -42,5 +47,9 @@ The resolved model uses Jakarta Bean Validation annotations. The application sup
Hibernate Validator, and calls `ResolvedQueryValidation` once before passing a query to the SQL compiler. Validation is
cascaded through the complete query graph, including extension-provided filters, selects, conditions, and aggregations.
+Model records may reference declarative constraints from `validation`, but validation services must never enrich or
+rewrite the model. Conversion and execution code consumes a successfully validated `ResolvedQuery` and must not perform
+logical identifier resolution.
+
Compact record constructors only create immutable copies of collections. Domain constraints such as compatible column
types, ordered ranges, and same-table requirements are declarative and are reported together as constraint violations.
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ExecutionTarget.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ExecutionTarget.java
similarity index 94%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/ExecutionTarget.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/ExecutionTarget.java
index 7b38bea7b2..c4eed00aa1 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ExecutionTarget.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ExecutionTarget.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query;
+package com.bakdata.conquery.sql.model;
import java.util.Optional;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedQuery.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ResolvedQuery.java
similarity index 69%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedQuery.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/ResolvedQuery.java
index f820a6db00..3216636ab8 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/ResolvedQuery.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ResolvedQuery.java
@@ -1,11 +1,11 @@
-package com.bakdata.conquery.sql.query;
+package com.bakdata.conquery.sql.model;
import java.util.List;
-import com.bakdata.conquery.sql.query.internal.ModelNormalization;
-import com.bakdata.conquery.sql.query.node.QueryNode;
-import com.bakdata.conquery.sql.query.result.ResultColumn;
-import com.bakdata.conquery.sql.query.schema.EntitySchema;
+import com.bakdata.conquery.sql.model.internal.ModelNormalization;
+import com.bakdata.conquery.sql.model.node.QueryNode;
+import com.bakdata.conquery.sql.model.result.ResultColumn;
+import com.bakdata.conquery.sql.model.schema.EntitySchema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/internal/ModelNormalization.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/internal/ModelNormalization.java
similarity index 93%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/internal/ModelNormalization.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/internal/ModelNormalization.java
index f004d0ab79..1f311ab6e8 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/internal/ModelNormalization.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/internal/ModelNormalization.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.internal;
+package com.bakdata.conquery.sql.model.internal;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/AndNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/AndNode.java
similarity index 81%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/AndNode.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/AndNode.java
index a3b614ce53..9e0de0c8dc 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/AndNode.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/AndNode.java
@@ -1,8 +1,8 @@
-package com.bakdata.conquery.sql.query.node;
+package com.bakdata.conquery.sql.model.node;
import java.util.List;
-import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import com.bakdata.conquery.sql.model.internal.ModelNormalization;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/ConceptNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/ConceptNode.java
similarity index 74%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/ConceptNode.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/ConceptNode.java
index 4e23392cef..9985c95874 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/ConceptNode.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/ConceptNode.java
@@ -1,10 +1,10 @@
-package com.bakdata.conquery.sql.query.node;
+package com.bakdata.conquery.sql.model.node;
import java.util.List;
-import com.bakdata.conquery.sql.query.internal.ModelNormalization;
-import com.bakdata.conquery.sql.query.operation.ResolvedSelect;
-import com.bakdata.conquery.sql.query.schema.ResolvedConnector;
+import com.bakdata.conquery.sql.model.internal.ModelNormalization;
+import com.bakdata.conquery.sql.model.operation.ResolvedSelect;
+import com.bakdata.conquery.sql.model.schema.ResolvedConnector;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/DateAggregationAction.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/DateAggregationAction.java
similarity index 76%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/DateAggregationAction.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/DateAggregationAction.java
index 17a56d680c..688608c2de 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/DateAggregationAction.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/DateAggregationAction.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.node;
+package com.bakdata.conquery.sql.model.node;
/** Defines how validity dates produced by child nodes are propagated. */
public enum DateAggregationAction {
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/DateRestrictionNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/DateRestrictionNode.java
similarity index 73%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/DateRestrictionNode.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/DateRestrictionNode.java
index 8a0dbe6367..168a574652 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/DateRestrictionNode.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/DateRestrictionNode.java
@@ -1,6 +1,6 @@
-package com.bakdata.conquery.sql.query.node;
+package com.bakdata.conquery.sql.model.node;
-import com.bakdata.conquery.sql.query.range.DateRange;
+import com.bakdata.conquery.sql.model.range.DateRange;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/NegationNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/NegationNode.java
similarity index 81%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/NegationNode.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/NegationNode.java
index 4fbe3100f8..27538a6d3f 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/NegationNode.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/NegationNode.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.node;
+package com.bakdata.conquery.sql.model.node;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/OrNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/OrNode.java
similarity index 81%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/OrNode.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/OrNode.java
index 14cdb6c9b6..9f0c98e74e 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/OrNode.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/OrNode.java
@@ -1,8 +1,8 @@
-package com.bakdata.conquery.sql.query.node;
+package com.bakdata.conquery.sql.model.node;
import java.util.List;
-import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import com.bakdata.conquery.sql.model.internal.ModelNormalization;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/QueryNode.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/QueryNode.java
similarity index 79%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/QueryNode.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/QueryNode.java
index 23f932c988..8aa8152bb0 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/QueryNode.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/QueryNode.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.node;
+package com.bakdata.conquery.sql.model.node;
/** A normalized query-tree node accepted by SQL compilation. */
public sealed interface QueryNode permits AndNode, OrNode, NegationNode, DateRestrictionNode, ConceptNode {
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/package-info.java
similarity index 55%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/package-info.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/package-info.java
index df44c5f5ee..b24d1e8cca 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/node/package-info.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/node/package-info.java
@@ -1,2 +1,2 @@
/** Normalized nodes of a fully resolved query tree. */
-package com.bakdata.conquery.sql.query.node;
+package com.bakdata.conquery.sql.model.node;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInAggregations.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/BuiltInAggregations.java
similarity index 92%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInAggregations.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/BuiltInAggregations.java
index e4b0fb50cb..849378f558 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInAggregations.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/BuiltInAggregations.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.operation;
+package com.bakdata.conquery.sql.model.operation;
import java.util.ArrayList;
import java.util.Collection;
@@ -7,10 +7,10 @@
import java.util.Optional;
import com.bakdata.conquery.models.datasets.ColumnType;
-import com.bakdata.conquery.sql.query.internal.ModelNormalization;
-import com.bakdata.conquery.sql.query.schema.DateColumns;
-import com.bakdata.conquery.sql.query.schema.ResolvedColumn;
-import com.bakdata.conquery.sql.query.validation.AllowedColumnTypes;
+import com.bakdata.conquery.sql.model.internal.ModelNormalization;
+import com.bakdata.conquery.sql.model.schema.DateColumns;
+import com.bakdata.conquery.sql.model.schema.ResolvedColumn;
+import com.bakdata.conquery.sql.validation.AllowedColumnTypes;
import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotBlank;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInConditions.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/BuiltInConditions.java
similarity index 88%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInConditions.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/BuiltInConditions.java
index 10e7215273..af4e34591b 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInConditions.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/BuiltInConditions.java
@@ -1,12 +1,12 @@
-package com.bakdata.conquery.sql.query.operation;
+package com.bakdata.conquery.sql.model.operation;
import java.util.List;
import java.util.Set;
import com.bakdata.conquery.models.datasets.ColumnType;
-import com.bakdata.conquery.sql.query.internal.ModelNormalization;
-import com.bakdata.conquery.sql.query.schema.ResolvedColumn;
-import com.bakdata.conquery.sql.query.validation.AllowedColumnTypes;
+import com.bakdata.conquery.sql.model.internal.ModelNormalization;
+import com.bakdata.conquery.sql.model.schema.ResolvedColumn;
+import com.bakdata.conquery.sql.validation.AllowedColumnTypes;
import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotBlank;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInFilters.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/BuiltInFilters.java
similarity index 86%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInFilters.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/BuiltInFilters.java
index c44cbcb4d4..6c4a3c6cea 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInFilters.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/BuiltInFilters.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.operation;
+package com.bakdata.conquery.sql.model.operation;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
@@ -6,11 +6,11 @@
import java.util.Set;
import com.bakdata.conquery.models.datasets.ColumnType;
-import com.bakdata.conquery.sql.query.internal.ModelNormalization;
-import com.bakdata.conquery.sql.query.range.NumberRange;
-import com.bakdata.conquery.sql.query.range.SubstringRange;
-import com.bakdata.conquery.sql.query.schema.ResolvedColumn;
-import com.bakdata.conquery.sql.query.validation.AllowedColumnTypes;
+import com.bakdata.conquery.sql.model.internal.ModelNormalization;
+import com.bakdata.conquery.sql.model.range.NumberRange;
+import com.bakdata.conquery.sql.model.range.SubstringRange;
+import com.bakdata.conquery.sql.model.schema.ResolvedColumn;
+import com.bakdata.conquery.sql.validation.AllowedColumnTypes;
import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotEmpty;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInSelects.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/BuiltInSelects.java
similarity index 84%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInSelects.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/BuiltInSelects.java
index b06d8f4b00..6bf44ff043 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/BuiltInSelects.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/BuiltInSelects.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.operation;
+package com.bakdata.conquery.sql.model.operation;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
@@ -6,11 +6,11 @@
import java.util.Optional;
import com.bakdata.conquery.models.datasets.ColumnType;
-import com.bakdata.conquery.sql.query.internal.ModelNormalization;
-import com.bakdata.conquery.sql.query.range.SubstringRange;
-import com.bakdata.conquery.sql.query.schema.DateColumns;
-import com.bakdata.conquery.sql.query.schema.ResolvedColumn;
-import com.bakdata.conquery.sql.query.validation.AllowedColumnTypes;
+import com.bakdata.conquery.sql.model.internal.ModelNormalization;
+import com.bakdata.conquery.sql.model.range.SubstringRange;
+import com.bakdata.conquery.sql.model.schema.DateColumns;
+import com.bakdata.conquery.sql.model.schema.ResolvedColumn;
+import com.bakdata.conquery.sql.validation.AllowedColumnTypes;
import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotEmpty;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedAggregation.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/ResolvedAggregation.java
similarity index 74%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedAggregation.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/ResolvedAggregation.java
index b33eff1500..ca477dcd11 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedAggregation.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/ResolvedAggregation.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.operation;
+package com.bakdata.conquery.sql.model.operation;
/** A resolved aggregation that can be exposed as a result or constrained by an aggregation filter. */
public interface ResolvedAggregation {
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedCondition.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/ResolvedCondition.java
similarity index 66%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedCondition.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/ResolvedCondition.java
index 7ce1db4d1b..8662889588 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedCondition.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/ResolvedCondition.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.operation;
+package com.bakdata.conquery.sql.model.operation;
/** A resolved connector or concept-element condition. */
public interface ResolvedCondition {
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedFilter.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/ResolvedFilter.java
similarity index 83%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedFilter.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/ResolvedFilter.java
index 5a2eef35fa..8b42def1ff 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedFilter.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/ResolvedFilter.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.operation;
+package com.bakdata.conquery.sql.model.operation;
/**
* Marker for an immutable, validated filter operation.
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedSelect.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/ResolvedSelect.java
similarity index 80%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedSelect.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/ResolvedSelect.java
index c9210791dd..20ff85d949 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/ResolvedSelect.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/ResolvedSelect.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.operation;
+package com.bakdata.conquery.sql.model.operation;
/**
* Marker for an immutable, validated select operation.
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/package-info.java
similarity index 66%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/package-info.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/package-info.java
index 381f90f0fd..a2b0015f72 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/operation/package-info.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/operation/package-info.java
@@ -1,2 +1,2 @@
/** Extensible filter, select, condition, and aggregation operations accepted by SQL compilation. */
-package com.bakdata.conquery.sql.query.operation;
+package com.bakdata.conquery.sql.model.operation;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/package-info.java
similarity index 67%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/package-info.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/package-info.java
index bd3cf6dbf2..9944bce082 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/package-info.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/package-info.java
@@ -6,6 +6,7 @@
* compiler state.
*
* The root query aggregate is complemented by dedicated packages for query nodes, operations, ranges, result
- * metadata, physical schema references, and validation.
+ * metadata, and physical schema references. Validation and SQL business logic live in sibling packages so this package
+ * remains the connector's immutable input boundary.
*/
-package com.bakdata.conquery.sql.query;
+package com.bakdata.conquery.sql.model;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/DateRange.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/range/DateRange.java
similarity index 94%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/DateRange.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/range/DateRange.java
index 5251e45b95..5df5120293 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/DateRange.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/range/DateRange.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.range;
+package com.bakdata.conquery.sql.model.range;
import java.time.LocalDate;
import java.util.Optional;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/NumberRange.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/range/NumberRange.java
similarity index 96%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/NumberRange.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/range/NumberRange.java
index 82c91aef72..040be4dd72 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/NumberRange.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/range/NumberRange.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.range;
+package com.bakdata.conquery.sql.model.range;
import java.math.BigDecimal;
import java.util.Objects;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/SubstringRange.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/range/SubstringRange.java
similarity index 95%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/SubstringRange.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/range/SubstringRange.java
index ce45606115..f087350e41 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/SubstringRange.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/range/SubstringRange.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.range;
+package com.bakdata.conquery.sql.model.range;
import java.util.Optional;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/range/package-info.java
similarity index 64%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/package-info.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/range/package-info.java
index a9ec695bf5..778528444f 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/range/package-info.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/range/package-info.java
@@ -1,2 +1,2 @@
/** Typed, framework-neutral range values used by resolved nodes and operations. */
-package com.bakdata.conquery.sql.query.range;
+package com.bakdata.conquery.sql.model.range;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/ResultColumn.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/ResultColumn.java
similarity index 88%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/ResultColumn.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/ResultColumn.java
index 9d7a3619d5..d67dff0702 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/ResultColumn.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/ResultColumn.java
@@ -1,9 +1,9 @@
-package com.bakdata.conquery.sql.query.result;
+package com.bakdata.conquery.sql.model.result;
import java.util.Optional;
import java.util.Set;
-import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import com.bakdata.conquery.sql.model.internal.ModelNormalization;
import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotBlank;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/ResultType.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/ResultType.java
similarity index 88%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/ResultType.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/ResultType.java
index a1bb6826af..56f10b365d 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/ResultType.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/ResultType.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.result;
+package com.bakdata.conquery.sql.model.result;
import jakarta.validation.constraints.NotNull;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/package-info.java
similarity index 57%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/package-info.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/package-info.java
index 54870eda8a..f20ad2f630 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/result/package-info.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/package-info.java
@@ -1,2 +1,2 @@
/** Ordered result-column metadata and logical result types. */
-package com.bakdata.conquery.sql.query.result;
+package com.bakdata.conquery.sql.model.result;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/DateColumns.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/DateColumns.java
similarity index 91%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/DateColumns.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/DateColumns.java
index ea566c006c..1c6fa99a0d 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/DateColumns.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/DateColumns.java
@@ -1,10 +1,10 @@
-package com.bakdata.conquery.sql.query.schema;
+package com.bakdata.conquery.sql.model.schema;
import java.util.List;
import java.util.Objects;
import com.bakdata.conquery.models.datasets.ColumnType;
-import com.bakdata.conquery.sql.query.validation.AllowedColumnTypes;
+import com.bakdata.conquery.sql.validation.AllowedColumnTypes;
import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/EntitySchema.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/EntitySchema.java
similarity index 92%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/EntitySchema.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/EntitySchema.java
index 212e1ab712..4b0d51e4b6 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/EntitySchema.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/EntitySchema.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.schema;
+package com.bakdata.conquery.sql.model.schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedColumn.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/ResolvedColumn.java
similarity index 90%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedColumn.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/ResolvedColumn.java
index ff285f38b9..560a9a0d70 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedColumn.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/ResolvedColumn.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.schema;
+package com.bakdata.conquery.sql.model.schema;
import com.bakdata.conquery.models.datasets.ColumnType;
import jakarta.validation.Valid;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedConnector.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/ResolvedConnector.java
similarity index 86%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedConnector.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/ResolvedConnector.java
index d23a9ec1af..4201426902 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedConnector.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/ResolvedConnector.java
@@ -1,12 +1,12 @@
-package com.bakdata.conquery.sql.query.schema;
+package com.bakdata.conquery.sql.model.schema;
import java.util.List;
import java.util.Optional;
-import com.bakdata.conquery.sql.query.internal.ModelNormalization;
-import com.bakdata.conquery.sql.query.operation.ResolvedCondition;
-import com.bakdata.conquery.sql.query.operation.ResolvedFilter;
-import com.bakdata.conquery.sql.query.operation.ResolvedSelect;
+import com.bakdata.conquery.sql.model.internal.ModelNormalization;
+import com.bakdata.conquery.sql.model.operation.ResolvedCondition;
+import com.bakdata.conquery.sql.model.operation.ResolvedFilter;
+import com.bakdata.conquery.sql.model.operation.ResolvedSelect;
import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotBlank;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedValidityDate.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/ResolvedValidityDate.java
similarity index 89%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedValidityDate.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/ResolvedValidityDate.java
index 73d71f6179..2fc8a30041 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/ResolvedValidityDate.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/ResolvedValidityDate.java
@@ -1,9 +1,9 @@
-package com.bakdata.conquery.sql.query.schema;
+package com.bakdata.conquery.sql.model.schema;
import java.util.Objects;
import com.bakdata.conquery.models.datasets.ColumnType;
-import com.bakdata.conquery.sql.query.validation.AllowedColumnTypes;
+import com.bakdata.conquery.sql.validation.AllowedColumnTypes;
import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/SqlTable.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/SqlTable.java
similarity index 84%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/SqlTable.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/SqlTable.java
index e927aaaa57..16e59d6b4d 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/SqlTable.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/SqlTable.java
@@ -1,9 +1,9 @@
-package com.bakdata.conquery.sql.query.schema;
+package com.bakdata.conquery.sql.model.schema;
import java.util.Arrays;
import java.util.List;
-import com.bakdata.conquery.sql.query.internal.ModelNormalization;
+import com.bakdata.conquery.sql.model.internal.ModelNormalization;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/package-info.java
similarity index 68%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/package-info.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/package-info.java
index 1b56e9adc2..79d98246e4 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/schema/package-info.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/schema/package-info.java
@@ -1,2 +1,2 @@
/** Resolved physical tables, columns, connectors, and entity schema used during SQL compilation. */
-package com.bakdata.conquery.sql.query.schema;
+package com.bakdata.conquery.sql.model.schema;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/package-info.java
new file mode 100644
index 0000000000..92d232da46
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/package-info.java
@@ -0,0 +1,8 @@
+/**
+ * Framework-neutral SQL connector contracts and services.
+ *
+ * {@code model} defines the fully resolved input, {@code validation} verifies that input, and sibling packages such
+ * as {@code conversion}, {@code dialect}, and {@code execution} contain SQL business logic. Business logic may depend on
+ * the model and validation boundary; model code must not depend on compiler or execution services.
+ */
+package com.bakdata.conquery.sql;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/package-info.java
deleted file mode 100644
index 06434d8420..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/package-info.java
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Jakarta Bean Validation constraints and the validation boundary for resolved queries. */
-package com.bakdata.conquery.sql.query.validation;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/AllowedColumnTypes.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/validation/AllowedColumnTypes.java
similarity index 93%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/AllowedColumnTypes.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/validation/AllowedColumnTypes.java
index 9f7b5beeba..f5a9c004d0 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/AllowedColumnTypes.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/validation/AllowedColumnTypes.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query.validation;
+package com.bakdata.conquery.sql.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/AllowedColumnTypesValidator.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/validation/AllowedColumnTypesValidator.java
similarity index 85%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/AllowedColumnTypesValidator.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/validation/AllowedColumnTypesValidator.java
index d79d3703be..6b1c9524f1 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/AllowedColumnTypesValidator.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/validation/AllowedColumnTypesValidator.java
@@ -1,9 +1,9 @@
-package com.bakdata.conquery.sql.query.validation;
+package com.bakdata.conquery.sql.validation;
import java.util.EnumSet;
import com.bakdata.conquery.models.datasets.ColumnType;
-import com.bakdata.conquery.sql.query.schema.ResolvedColumn;
+import com.bakdata.conquery.sql.model.schema.ResolvedColumn;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/ResolvedQueryValidation.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/validation/ResolvedQueryValidation.java
similarity index 87%
rename from sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/ResolvedQueryValidation.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/validation/ResolvedQueryValidation.java
index 5991b5e759..2032de20cf 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/query/validation/ResolvedQueryValidation.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/validation/ResolvedQueryValidation.java
@@ -1,9 +1,9 @@
-package com.bakdata.conquery.sql.query.validation;
+package com.bakdata.conquery.sql.validation;
import java.util.Objects;
import java.util.Set;
-import com.bakdata.conquery.sql.query.ResolvedQuery;
+import com.bakdata.conquery.sql.model.ResolvedQuery;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Validator;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/validation/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/validation/package-info.java
new file mode 100644
index 0000000000..c33adf3ff6
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/validation/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Jakarta Bean Validation constraints and the validation boundary for resolved SQL models.
+ *
+ * Validation reports violations but never resolves identifiers, supplies defaults, or mutates model instances.
+ */
+package com.bakdata.conquery.sql.validation;
diff --git a/sql-connector/src/test/java/com/bakdata/conquery/sql/query/BuiltInOperationsTest.java b/sql-connector/src/test/java/com/bakdata/conquery/sql/model/BuiltInOperationsTest.java
similarity index 87%
rename from sql-connector/src/test/java/com/bakdata/conquery/sql/query/BuiltInOperationsTest.java
rename to sql-connector/src/test/java/com/bakdata/conquery/sql/model/BuiltInOperationsTest.java
index 4dcc56e333..5826a63192 100644
--- a/sql-connector/src/test/java/com/bakdata/conquery/sql/query/BuiltInOperationsTest.java
+++ b/sql-connector/src/test/java/com/bakdata/conquery/sql/model/BuiltInOperationsTest.java
@@ -1,10 +1,10 @@
-package com.bakdata.conquery.sql.query;
+package com.bakdata.conquery.sql.model;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static com.bakdata.conquery.sql.query.ValidationTestSupport.assertInvalid;
-import static com.bakdata.conquery.sql.query.ValidationTestSupport.assertValid;
+import static com.bakdata.conquery.sql.model.ValidationTestSupport.assertInvalid;
+import static com.bakdata.conquery.sql.model.ValidationTestSupport.assertValid;
import java.math.BigDecimal;
import java.time.LocalDate;
@@ -17,18 +17,18 @@
import java.util.Set;
import com.bakdata.conquery.models.datasets.ColumnType;
-import com.bakdata.conquery.sql.query.operation.BuiltInAggregations;
-import com.bakdata.conquery.sql.query.operation.BuiltInConditions;
-import com.bakdata.conquery.sql.query.operation.BuiltInFilters;
-import com.bakdata.conquery.sql.query.operation.BuiltInSelects;
-import com.bakdata.conquery.sql.query.operation.ResolvedCondition;
-import com.bakdata.conquery.sql.query.operation.ResolvedFilter;
-import com.bakdata.conquery.sql.query.operation.ResolvedSelect;
-import com.bakdata.conquery.sql.query.range.NumberRange;
-import com.bakdata.conquery.sql.query.range.SubstringRange;
-import com.bakdata.conquery.sql.query.schema.DateColumns;
-import com.bakdata.conquery.sql.query.schema.ResolvedColumn;
-import com.bakdata.conquery.sql.query.schema.SqlTable;
+import com.bakdata.conquery.sql.model.operation.BuiltInAggregations;
+import com.bakdata.conquery.sql.model.operation.BuiltInConditions;
+import com.bakdata.conquery.sql.model.operation.BuiltInFilters;
+import com.bakdata.conquery.sql.model.operation.BuiltInSelects;
+import com.bakdata.conquery.sql.model.operation.ResolvedCondition;
+import com.bakdata.conquery.sql.model.operation.ResolvedFilter;
+import com.bakdata.conquery.sql.model.operation.ResolvedSelect;
+import com.bakdata.conquery.sql.model.range.NumberRange;
+import com.bakdata.conquery.sql.model.range.SubstringRange;
+import com.bakdata.conquery.sql.model.schema.DateColumns;
+import com.bakdata.conquery.sql.model.schema.ResolvedColumn;
+import com.bakdata.conquery.sql.model.schema.SqlTable;
import org.junit.jupiter.api.Test;
class BuiltInOperationsTest {
diff --git a/sql-connector/src/test/java/com/bakdata/conquery/sql/query/ResolvedQueryTest.java b/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ResolvedQueryTest.java
similarity index 81%
rename from sql-connector/src/test/java/com/bakdata/conquery/sql/query/ResolvedQueryTest.java
rename to sql-connector/src/test/java/com/bakdata/conquery/sql/model/ResolvedQueryTest.java
index 1f4831370f..4bcb23d39e 100644
--- a/sql-connector/src/test/java/com/bakdata/conquery/sql/query/ResolvedQueryTest.java
+++ b/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ResolvedQueryTest.java
@@ -1,9 +1,9 @@
-package com.bakdata.conquery.sql.query;
+package com.bakdata.conquery.sql.model;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static com.bakdata.conquery.sql.query.ValidationTestSupport.assertInvalid;
-import static com.bakdata.conquery.sql.query.ValidationTestSupport.assertValid;
+import static com.bakdata.conquery.sql.model.ValidationTestSupport.assertInvalid;
+import static com.bakdata.conquery.sql.model.ValidationTestSupport.assertValid;
import java.time.LocalDate;
import java.util.ArrayList;
@@ -12,19 +12,19 @@
import java.util.Set;
import com.bakdata.conquery.models.datasets.ColumnType;
-import com.bakdata.conquery.sql.query.node.AndNode;
-import com.bakdata.conquery.sql.query.node.ConceptNode;
-import com.bakdata.conquery.sql.query.node.DateAggregationAction;
-import com.bakdata.conquery.sql.query.node.QueryNode;
-import com.bakdata.conquery.sql.query.range.DateRange;
-import com.bakdata.conquery.sql.query.result.ResultColumn;
-import com.bakdata.conquery.sql.query.result.ResultType;
-import com.bakdata.conquery.sql.query.schema.EntitySchema;
-import com.bakdata.conquery.sql.query.schema.ResolvedColumn;
-import com.bakdata.conquery.sql.query.schema.ResolvedConnector;
-import com.bakdata.conquery.sql.query.schema.ResolvedValidityDate;
-import com.bakdata.conquery.sql.query.schema.SqlTable;
-import com.bakdata.conquery.sql.query.validation.ResolvedQueryValidation;
+import com.bakdata.conquery.sql.model.node.AndNode;
+import com.bakdata.conquery.sql.model.node.ConceptNode;
+import com.bakdata.conquery.sql.model.node.DateAggregationAction;
+import com.bakdata.conquery.sql.model.node.QueryNode;
+import com.bakdata.conquery.sql.model.range.DateRange;
+import com.bakdata.conquery.sql.model.result.ResultColumn;
+import com.bakdata.conquery.sql.model.result.ResultType;
+import com.bakdata.conquery.sql.model.schema.EntitySchema;
+import com.bakdata.conquery.sql.model.schema.ResolvedColumn;
+import com.bakdata.conquery.sql.model.schema.ResolvedConnector;
+import com.bakdata.conquery.sql.model.schema.ResolvedValidityDate;
+import com.bakdata.conquery.sql.model.schema.SqlTable;
+import com.bakdata.conquery.sql.validation.ResolvedQueryValidation;
import jakarta.validation.ConstraintViolationException;
import org.junit.jupiter.api.Test;
diff --git a/sql-connector/src/test/java/com/bakdata/conquery/sql/query/ValidationTestSupport.java b/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ValidationTestSupport.java
similarity index 95%
rename from sql-connector/src/test/java/com/bakdata/conquery/sql/query/ValidationTestSupport.java
rename to sql-connector/src/test/java/com/bakdata/conquery/sql/model/ValidationTestSupport.java
index 748861d6f0..9b57aeae8a 100644
--- a/sql-connector/src/test/java/com/bakdata/conquery/sql/query/ValidationTestSupport.java
+++ b/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ValidationTestSupport.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.query;
+package com.bakdata.conquery.sql.model;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
From 05f5488d7037c611846879cb2e18976a77af8e7d Mon Sep 17 00:00:00 2001
From: MT <12283268+thoniTUB@users.noreply.github.com>
Date: Fri, 28 Aug 2026 14:39:48 +0200
Subject: [PATCH 05/81] refactor(sql): separate compiler runtime dependencies
Co-authored-by: Codex GPT-5
---
backend/pom.xml | 5 +
.../mode/local/LocalNamespaceHandler.java | 2 +-
.../conquery/sql/conversion/Context.java | 6 +-
.../sql/conversion/NodeConversions.java | 19 ++-
.../cqelement/CQExternalConverter.java | 2 +-
.../cqelement/ConversionContext.java | 6 +-
.../aggregation/DateAggregationContext.java | 6 +-
.../cqelement/concept/CQConceptConverter.java | 6 +-
.../cqelement/concept/CQTableContext.java | 6 +-
.../cqelement/concept/FilterContext.java | 8 +-
.../concept/IntervalPackingSelectsCte.java | 8 +-
.../cqelement/concept/JoinBranchesCte.java | 2 +-
.../cqelement/concept/TablePath.java | 6 +-
.../IntervalPackingContext.java | 6 +-
.../IntervalPackingCteStep.java | 8 +-
.../sql/conversion/dialect/DialectBundle.java | 112 ++----------------
.../dialect/LegacyCompilerDialect.java | 86 ++++++++++++++
.../sql/conversion/model/QueryStepJoiner.java | 8 +-
.../CountQuartersSqlAggregator.java | 4 +-
.../model/aggregator/FlagSqlAggregator.java | 2 +-
.../model/select/DaterangeSelectUtil.java | 18 +--
.../model/select/SelectContext.java | 8 +-
.../query/AbsoluteFormQueryConverter.java | 2 +-
.../query/ConceptQueryConverter.java | 2 +-
sql-connector/README.md | 3 +-
.../sql/compiler/dialect/CompilerDialect.java | 22 ++++
.../sql/compiler/dialect/package-info.java | 2 +
.../conquery/sql/compiler/package-info.java | 2 +
.../bakdata/conquery/sql/package-info.java | 6 +-
29 files changed, 198 insertions(+), 175 deletions(-)
create mode 100644 backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/LegacyCompilerDialect.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/dialect/CompilerDialect.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/dialect/package-info.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/package-info.java
diff --git a/backend/pom.xml b/backend/pom.xml
index 27a433b345..6bfa236947 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -112,6 +112,11 @@
+
+ com.bakdata.conquery
+ sql-connector
+ ${project.version}
+
org.apache.logging.log4j
log4j-to-slf4j
diff --git a/backend/src/main/java/com/bakdata/conquery/mode/local/LocalNamespaceHandler.java b/backend/src/main/java/com/bakdata/conquery/mode/local/LocalNamespaceHandler.java
index 8a4a292b61..e458522d30 100644
--- a/backend/src/main/java/com/bakdata/conquery/mode/local/LocalNamespaceHandler.java
+++ b/backend/src/main/java/com/bakdata/conquery/mode/local/LocalNamespaceHandler.java
@@ -49,7 +49,7 @@ public LocalNamespace createNamespace(
ResultSetProcessor resultSetProcessor = dialectBundle.getResultSetProcessor(config);
SqlExecutionService sqlExecutionService = new SqlExecutionService(dslContext, resultSetProcessor);
- NodeConversions nodeConversions = new NodeConversions(config.getIdColumns(), dialectBundle, dslContext, sqlExecutionService, clock, connection.getConnection().getPrimaryColumn());
+ NodeConversions nodeConversions = new NodeConversions(config.getIdColumns(), dialectBundle, dslContext, clock, connection.getConnection().getPrimaryColumn());
SqlConverter sqlConverter = new SqlConverter(nodeConversions, config);
ExecutionManager executionManager = new SqlExecutionManager(sqlConverter, sqlExecutionService, metaStorage, datasetRegistry, config);
SqlStorageHandler sqlStorageHandler = new SqlStorageHandler(sqlExecutionService);
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/Context.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/Context.java
index 4bc8338bd1..9d679eb311 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/Context.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/Context.java
@@ -1,7 +1,7 @@
package com.bakdata.conquery.sql.conversion;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
-import com.bakdata.conquery.sql.conversion.dialect.DialectBundle;
+import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.model.NameGenerator;
@@ -12,14 +12,14 @@ public interface Context {
ConversionContext getConversionContext();
- DialectBundle getDialectBundle();
+ LegacyCompilerDialect getCompilerDialect();
default NameGenerator getNameGenerator() {
return getConversionContext().getNameGenerator();
}
default SqlFunctionProvider getFunctionProvider(){
- return getConversionContext().getDialectBundle().getFunctionProvider();
+ return getConversionContext().getCompilerDialect().getFunctionProvider();
}
}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConversions.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConversions.java
index c61a301d8f..6ec5be439f 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConversions.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConversions.java
@@ -5,9 +5,8 @@
import com.bakdata.conquery.models.config.IdColumnConfig;
import com.bakdata.conquery.models.query.PrintSettings;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
-import com.bakdata.conquery.sql.conversion.dialect.DialectBundle;
+import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.model.NameGenerator;
-import com.bakdata.conquery.sql.execution.SqlExecutionService;
import lombok.NonNull;
import org.jooq.DSLContext;
@@ -21,26 +20,23 @@ public class NodeConversions implements NodeConversionDispatcher {
private final Conversions conversions;
private final IdColumnConfig idColumns;
- private final DialectBundle dialect;
+ private final LegacyCompilerDialect dialect;
private final NameGenerator nameGenerator;
- private final SqlExecutionService executionService;
private final Clock clock;
@NonNull
private final String defaultPrimaryColumn;
public NodeConversions(
IdColumnConfig idColumns,
- DialectBundle dialectBundle,
+ LegacyCompilerDialect compilerDialect,
DSLContext dslContext,
- SqlExecutionService executionService,
Clock clock,
String defaultPrimaryColumn
) {
- this.conversions = new Conversions<>(dialectBundle.getNodeConverters(dslContext));
+ this.conversions = new Conversions<>(compilerDialect.getNodeConverters(dslContext));
this.idColumns = idColumns;
- this.dialect = dialectBundle;
- this.nameGenerator = new NameGenerator(dialectBundle.getNameMaxLength());
- this.executionService = executionService;
+ this.dialect = compilerDialect;
+ this.nameGenerator = new NameGenerator(compilerDialect.getNameMaxLength());
this.clock = clock;
this.defaultPrimaryColumn = defaultPrimaryColumn;
}
@@ -59,8 +55,7 @@ public ConversionContext convert(QueryDescription queryDescription, ConqueryConf
.clock(clock)
.defaultPrimaryColumn(this.defaultPrimaryColumn)
.stratificationFunctions(dialect.getStratificationFunctions())
- .dialectBundle(dialect)
- .executionService(executionService)
+ .compilerDialect(dialect)
.build();
return convert(queryDescription, initialCtx);
}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
index dd9e63cc36..0b70eb2435 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
@@ -152,7 +152,7 @@ public Class extends CQExternal> getConversionClass() {
@Override
public ConversionContext convert(CQExternal external, ConversionContext context) {
- SqlFunctionProvider functionProvider = context.getDialectBundle().getFunctionProvider();
+ SqlFunctionProvider functionProvider = context.getCompilerDialect().getFunctionProvider();
QueryStep externalIdsCte = createExternalIdsCte(external, functionProvider, context);
ConversionContext withExternalIdCte = context.withQueryStep(externalIdsCte);
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/ConversionContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/ConversionContext.java
index 235ba767f9..49612303ef 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/ConversionContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/ConversionContext.java
@@ -12,12 +12,11 @@
import com.bakdata.conquery.models.query.PrintSettings;
import com.bakdata.conquery.sql.conversion.Context;
import com.bakdata.conquery.sql.conversion.NodeConversionDispatcher;
-import com.bakdata.conquery.sql.conversion.dialect.DialectBundle;
+import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.forms.StratificationFunctions;
import com.bakdata.conquery.sql.conversion.model.NameGenerator;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlQuery;
-import com.bakdata.conquery.sql.execution.SqlExecutionService;
import lombok.Builder;
import lombok.NonNull;
import lombok.Singular;
@@ -38,9 +37,8 @@ public class ConversionContext implements Context {
String defaultPrimaryColumn;
PrintSettings sqlPrintSettings;
NodeConversionDispatcher nodeConversions;
- DialectBundle dialectBundle;
+ LegacyCompilerDialect compilerDialect;
NameGenerator nameGenerator;
- SqlExecutionService executionService;
@Singular
List querySteps;
@Nullable
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java
index 7eae7cb528..f00fe6d120 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java
@@ -7,7 +7,7 @@
import com.bakdata.conquery.sql.conversion.Context;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
-import com.bakdata.conquery.sql.conversion.dialect.DialectBundle;
+import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.model.Qualifiable;
import com.bakdata.conquery.sql.conversion.model.QualifyingUtil;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
@@ -63,8 +63,8 @@ public DateAggregationContext qualify(String qualifier) {
}
@Override
- public DialectBundle getDialectBundle() {
- return getConversionContext().getDialectBundle();
+ public LegacyCompilerDialect getCompilerDialect() {
+ return getConversionContext().getCompilerDialect();
}
}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java
index 1c879805a3..e43c830d81 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java
@@ -65,7 +65,7 @@ private static QueryStep finishConceptConversion(QueryStep predecessor, CQConcep
List converted = cqConcept.getSelects().stream()
.map(selectId -> {
Select select = selectId.resolve();
- return context.getDialectBundle().getSelectConverter(select).conceptSelect(select, selectContext);
+ return context.getCompilerDialect().getSelectConverter(select).conceptSelect(select, selectContext);
})
.toList();
@@ -220,7 +220,7 @@ private static ConnectorSqlSelects createConceptColumnConnectorSqlSelects(CQConc
.map(SelectId::resolve)
.filter(select -> select instanceof ConceptColumnSelect)
.findFirst()
- .map(select -> selectContext.getDialectBundle().getSelectConverter(select).connectorSelect(select, selectContext))
+ .map(select -> selectContext.getCompilerDialect().getSelectConverter(select).connectorSelect(select, selectContext))
.orElse(ConnectorSqlSelects.none());
}
@@ -290,7 +290,7 @@ private CQTableContext createTableContext(TablePath tablePath, CQConcept cqConce
cqTable.getSelects()
.stream()
.map(SelectId::resolve)
- .map(select -> selectContext.getDialectBundle().getSelectConverter(select).connectorSelect(select, selectContext))
+ .map(select -> selectContext.getCompilerDialect().getSelectConverter(select).connectorSelect(select, selectContext))
.forEach(allSelectsForTable::add);
return CQTableContext.builder()
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java
index cf2c51a974..71199d2e0c 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java
@@ -8,7 +8,7 @@
import com.bakdata.conquery.models.datasets.concepts.select.Select;
import com.bakdata.conquery.sql.conversion.Context;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
-import com.bakdata.conquery.sql.conversion.dialect.DialectBundle;
+import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
@@ -51,7 +51,7 @@ public List allSqlSelects() {
}
@Override
- public DialectBundle getDialectBundle() {
- return getConversionContext().getDialectBundle();
+ public LegacyCompilerDialect getCompilerDialect() {
+ return getConversionContext().getCompilerDialect();
}
}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/FilterContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/FilterContext.java
index 6aa498302a..ac0a1eeb5d 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/FilterContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/FilterContext.java
@@ -5,7 +5,7 @@
import com.bakdata.conquery.apiv1.query.concept.filter.FilterValue;
import com.bakdata.conquery.sql.conversion.Context;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
-import com.bakdata.conquery.sql.conversion.dialect.DialectBundle;
+import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
import lombok.AccessLevel;
@@ -40,12 +40,12 @@ public static FilterContext forTableExport(SqlIdColumns ids, V value, Con
}
@Override
- public DialectBundle getDialectBundle() {
- return getConversionContext().getDialectBundle();
+ public LegacyCompilerDialect getCompilerDialect() {
+ return getConversionContext().getCompilerDialect();
}
public SqlFunctionProvider getFunctionProvider() {
- return getDialectBundle().getFunctionProvider();
+ return getCompilerDialect().getFunctionProvider();
}
}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java
index 45e03332c0..77572d97b2 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java
@@ -6,7 +6,7 @@
import com.bakdata.conquery.models.datasets.concepts.select.concept.specific.EventDateUnionSelect;
import com.bakdata.conquery.models.datasets.concepts.select.concept.specific.EventDurationSumSelect;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
-import com.bakdata.conquery.sql.conversion.dialect.DialectBundle;
+import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
@@ -23,15 +23,15 @@ public static QueryStep forSelect(
ColumnDateRange daterange,
SqlSelect select,
SqlTables tables,
- DialectBundle dialectBundle
+ LegacyCompilerDialect compilerDialect
) {
List predecessors = List.of(withAggregatedDaterange);
QueryStep directPredecessor = withAggregatedDaterange;
// we need an additional predecessor to unnest the validity date if it is a single column range
- if (dialectBundle.supportsSingleColumnRanges()) {
+ if (compilerDialect.supportsSingleColumnRanges()) {
String unnestCteName = tables.cteName(ConceptCteStep.UNNEST_DATE);
- directPredecessor = dialectBundle.getFunctionProvider().unnestDaterange(daterange, withAggregatedDaterange, unnestCteName);
+ directPredecessor = compilerDialect.getFunctionProvider().unnestDaterange(daterange, withAggregatedDaterange, unnestCteName);
predecessors = List.of(withAggregatedDaterange, directPredecessor);
}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java
index b2f6718dfd..8a78d1dbd1 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java
@@ -63,7 +63,7 @@ protected QueryStep.QueryStepBuilder convertStep(CQTableContext tableContext) {
}
else {
IntervalPackingContext intervalPackingContext = createIntervalPackingContext(tableContext);
- IntervalPacker intervalPacker = tableContext.getConversionContext().getDialectBundle().getIntervalPacker();
+ IntervalPacker intervalPacker = tableContext.getConversionContext().getCompilerDialect().getIntervalPacker();
QueryStep lastIntervalPackingStep = intervalPacker.aggregateAsValidityDate(intervalPackingContext);
queriesToJoin.add(lastIntervalPackingStep);
validityDate = lastIntervalPackingStep.getQualifiedSelects().getValidityDate();
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/TablePath.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/TablePath.java
index 0ccbe64151..a021c2db28 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/TablePath.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/TablePath.java
@@ -91,7 +91,7 @@ private static TablePathInfo collectConnectorTables(CQConcept cqConcept, CQTable
// interval packing requiredw
tableInfo.setContainsIntervalPacking(true);
- tableInfo.addMappings(IntervalPackingCteStep.getMappings(PREPROCESSING, context.getDialectBundle()));
+ tableInfo.addMappings(IntervalPackingCteStep.getMappings(PREPROCESSING, context.getCompilerDialect()));
// validity date propagation not necessary
if (!cqConcept.isAggregateEventDates()) {
@@ -103,7 +103,7 @@ private static TablePathInfo collectConnectorTables(CQConcept cqConcept, CQTable
}
// interval packing selects required with optional unnest step
- if (context.getDialectBundle().supportsSingleColumnRanges()) {
+ if (context.getCompilerDialect().supportsSingleColumnRanges()) {
tableInfo.addMappings(Map.of(
UNNEST_DATE, INTERVAL_COMPLETE,
INTERVAL_PACKING_SELECTS, UNNEST_DATE
@@ -135,7 +135,7 @@ private TablePathInfo collectConceptTables(QueryStep predecessor) {
);
// universal event date selects required with optional additional unnest step
- if (context.getDialectBundle().supportsSingleColumnRanges()) {
+ if (context.getCompilerDialect().supportsSingleColumnRanges()) {
tableInfo.addRootTableMapping(UNNEST_DATE);
tableInfo.addMappings(Map.of(INTERVAL_PACKING_SELECTS, UNNEST_DATE));
}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java
index d644a635e9..ee0f7e45ab 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java
@@ -7,7 +7,7 @@
import com.bakdata.conquery.sql.conversion.Context;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
-import com.bakdata.conquery.sql.conversion.dialect.DialectBundle;
+import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
@@ -45,8 +45,8 @@ public class IntervalPackingContext implements Context {
ConversionContext conversionContext;
@Override
- public DialectBundle getDialectBundle() {
- return getConversionContext().getDialectBundle();
+ public LegacyCompilerDialect getCompilerDialect() {
+ return getConversionContext().getCompilerDialect();
}
}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingCteStep.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingCteStep.java
index ef5d068653..640e6b789d 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingCteStep.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingCteStep.java
@@ -5,7 +5,7 @@
import java.util.Set;
import com.bakdata.conquery.sql.conversion.Context;
-import com.bakdata.conquery.sql.conversion.dialect.DialectBundle;
+import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.model.CteStep;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
@@ -31,7 +31,7 @@ public enum IntervalPackingCteStep implements CteStep {
public static SqlTables createTables(QueryStep predecessor, Context context) {
String rootTable = predecessor.getCteName();
- Set requiredSteps = context.getDialectBundle().supportsSingleColumnRanges()
+ Set requiredSteps = context.getCompilerDialect().supportsSingleColumnRanges()
? Set.of(INTERVAL_COMPLETE)
: Set.of(values());
@@ -45,7 +45,7 @@ public static SqlTables createTables(QueryStep predecessor, Context context) {
* Create predecessor mappings for these interval packing {@link CteStep}s based on a preceding root step that must contain a validity date which
* shall be interval-packed.
*/
- public static Map getMappings(CteStep root, DialectBundle dialect) {
+ public static Map getMappings(CteStep root, LegacyCompilerDialect dialect) {
if (dialect.supportsSingleColumnRanges()) {
return Map.of(INTERVAL_COMPLETE, root);
}
@@ -57,7 +57,7 @@ public static Map getMappings(CteStep root, DialectBundle dial
/**
* Create predecessor mappings for these interval packing {@link CteStep}s based on the default mapping.
*/
- public static Map getMappings(DialectBundle dialect) {
+ public static Map getMappings(LegacyCompilerDialect dialect) {
if (dialect.supportsSingleColumnRanges()) {
Map mappings = new HashMap<>();
mappings.put(INTERVAL_COMPLETE, null); // final step directly mapped onto root table
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/DialectBundle.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/DialectBundle.java
index d0596962c3..1e38a3ab88 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/DialectBundle.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/DialectBundle.java
@@ -1,120 +1,32 @@
package com.bakdata.conquery.sql.conversion.dialect;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
import com.bakdata.conquery.models.config.ConqueryConfig;
import com.bakdata.conquery.models.config.Dialect;
-import com.bakdata.conquery.models.datasets.concepts.select.Select;
import com.bakdata.conquery.models.events.MajorTypeId;
-import com.bakdata.conquery.models.query.Visitable;
-import com.bakdata.conquery.sql.conversion.Converter;
-import com.bakdata.conquery.sql.conversion.NodeConverter;
-import com.bakdata.conquery.sql.conversion.cqelement.CQAndConverter;
-import com.bakdata.conquery.sql.conversion.cqelement.CQDateRestrictionConverter;
-import com.bakdata.conquery.sql.conversion.cqelement.CQExternalConverter;
-import com.bakdata.conquery.sql.conversion.cqelement.CQNegationConverter;
-import com.bakdata.conquery.sql.conversion.cqelement.CQOrConverter;
-import com.bakdata.conquery.sql.conversion.cqelement.CQYesConverter;
-import com.bakdata.conquery.sql.conversion.cqelement.concept.CQConceptConverter;
-import com.bakdata.conquery.sql.conversion.forms.StratificationFunctions;
-import com.bakdata.conquery.sql.conversion.model.QueryStepTransformer;
-import com.bakdata.conquery.sql.conversion.model.select.SelectConverter;
-import com.bakdata.conquery.sql.conversion.query.AbsoluteFormQueryConverter;
-import com.bakdata.conquery.sql.conversion.query.CQReusedQueryConverter;
-import com.bakdata.conquery.sql.conversion.query.ConceptQueryConverter;
-import com.bakdata.conquery.sql.conversion.query.EntityDateQueryConverter;
-import com.bakdata.conquery.sql.conversion.query.FormConversionHelper;
-import com.bakdata.conquery.sql.conversion.query.RelativFormQueryConverter;
-import com.bakdata.conquery.sql.conversion.query.SecondaryIdQueryConverter;
-import com.bakdata.conquery.sql.conversion.query.TableExportQueryConverter;
import com.bakdata.conquery.sql.execution.ResultSetProcessor;
-import com.bakdata.conquery.sql.execution.SqlCDateSetParser;
-import org.jooq.DSLContext;
import org.jooq.Field;
import org.jooq.SQLDialect;
-public interface DialectBundle {
-
- private static > List customize(List defaults, List substitutes) {
- Map, C> substituteMap = getSubstituteMap(substitutes);
- return defaults.stream()
- .map(converter -> substituteMap.getOrDefault(converter.getConversionClass(), converter))
- .toList();
- }
-
- private static > Map, C> getSubstituteMap(List substitutes) {
- return substitutes.stream()
- .collect(Collectors.toMap(
- Converter::getConversionClass,
- Function.identity()
- ));
- }
+/**
+ * Compatibility aggregate used when wiring all database-specific services for a backend namespace.
+ *
+ * The bundle exposes both the legacy compiler adapter inherited from {@link LegacyCompilerDialect} and the runtime
+ * methods declared here. Legacy compiler code must depend on {@code LegacyCompilerDialect}, not on this aggregate.
+ * Runtime wiring may retain the bundle when it needs to construct both compiler and execution services for the same
+ * database.
+ *
+ * This interface keeps the existing HANA and ClickHouse integrations source-compatible while their compiler and
+ * execution capabilities are separated. It is an application composition boundary, not a compiler dependency.
+ */
+public interface DialectBundle extends LegacyCompilerDialect {
ResultSetProcessor getResultSetProcessor(ConqueryConfig config);
Dialect getDialect();
- int getNameMaxLength();
-
String getConnectionTestString();
SQLDialect getJooqDialect();
-
- StratificationFunctions getStratificationFunctions();
-
boolean isTypeCompatible(Field> field, MajorTypeId type);
-
- SqlFunctionProvider getFunctionProvider();
-
- IntervalPacker getIntervalPacker();
-
- SqlDateAggregator getDateAggregator();
-
- List> getNodeConverters(DSLContext context);
-
- default boolean supportsSingleColumnRanges() {
- return false;
- }
-
- default List> getDefaultNodeConverters(DSLContext dslContext) {
-
- QueryStepTransformer queryStepTransformer = new QueryStepTransformer(dslContext);
- FormConversionHelper formConversionUtil = new FormConversionHelper(queryStepTransformer);
-
- return List.of(
- new CQDateRestrictionConverter(),
- new CQAndConverter(),
- new CQOrConverter(),
- new CQNegationConverter(),
- new CQYesConverter(),
- new CQConceptConverter(),
- new CQExternalConverter(),
- new CQReusedQueryConverter(),
- new ConceptQueryConverter(queryStepTransformer),
- new SecondaryIdQueryConverter(),
- new AbsoluteFormQueryConverter(formConversionUtil),
- new EntityDateQueryConverter(formConversionUtil),
- new RelativFormQueryConverter(formConversionUtil),
- new TableExportQueryConverter(queryStepTransformer)
- );
- }
-
- default Map, ? extends SelectConverter extends Select>> getSelectConverterOverrides(){
- return Collections.emptyMap();
- }
-
- default SelectConverter getSelectConverter(Select select){
- SelectConverter maybeOverride = (SelectConverter) getSelectConverterOverrides().get(select.getClass());
-
- if (maybeOverride != null){
- return maybeOverride;
- }
-
- return select.createConverter();
- }
}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/LegacyCompilerDialect.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/LegacyCompilerDialect.java
new file mode 100644
index 0000000000..4aed1ff758
--- /dev/null
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/LegacyCompilerDialect.java
@@ -0,0 +1,86 @@
+package com.bakdata.conquery.sql.conversion.dialect;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import com.bakdata.conquery.models.datasets.concepts.select.Select;
+import com.bakdata.conquery.models.query.Visitable;
+import com.bakdata.conquery.sql.compiler.dialect.CompilerDialect;
+import com.bakdata.conquery.sql.conversion.NodeConverter;
+import com.bakdata.conquery.sql.conversion.cqelement.CQAndConverter;
+import com.bakdata.conquery.sql.conversion.cqelement.CQDateRestrictionConverter;
+import com.bakdata.conquery.sql.conversion.cqelement.CQExternalConverter;
+import com.bakdata.conquery.sql.conversion.cqelement.CQNegationConverter;
+import com.bakdata.conquery.sql.conversion.cqelement.CQOrConverter;
+import com.bakdata.conquery.sql.conversion.cqelement.CQYesConverter;
+import com.bakdata.conquery.sql.conversion.cqelement.concept.CQConceptConverter;
+import com.bakdata.conquery.sql.conversion.forms.StratificationFunctions;
+import com.bakdata.conquery.sql.conversion.model.QueryStepTransformer;
+import com.bakdata.conquery.sql.conversion.model.select.SelectConverter;
+import com.bakdata.conquery.sql.conversion.query.AbsoluteFormQueryConverter;
+import com.bakdata.conquery.sql.conversion.query.CQReusedQueryConverter;
+import com.bakdata.conquery.sql.conversion.query.ConceptQueryConverter;
+import com.bakdata.conquery.sql.conversion.query.EntityDateQueryConverter;
+import com.bakdata.conquery.sql.conversion.query.FormConversionHelper;
+import com.bakdata.conquery.sql.conversion.query.RelativFormQueryConverter;
+import com.bakdata.conquery.sql.conversion.query.SecondaryIdQueryConverter;
+import com.bakdata.conquery.sql.conversion.query.TableExportQueryConverter;
+import org.jooq.DSLContext;
+
+/**
+ * Temporary backend adapter exposing services required by the legacy SQL compiler.
+ *
+ * The framework-neutral dialect contract lives in {@link CompilerDialect}. This adapter retains dependencies on
+ * backend query DTOs, converter registries, and legacy compiler services until those implementations move into the SQL
+ * connector. New connector code must not depend on this interface.
+ */
+public interface LegacyCompilerDialect extends CompilerDialect {
+
+ StratificationFunctions getStratificationFunctions();
+
+ SqlFunctionProvider getFunctionProvider();
+
+ IntervalPacker getIntervalPacker();
+
+ SqlDateAggregator getDateAggregator();
+
+ List> getNodeConverters(DSLContext context);
+
+ default List> getDefaultNodeConverters(DSLContext dslContext) {
+
+ QueryStepTransformer queryStepTransformer = new QueryStepTransformer(dslContext);
+ FormConversionHelper formConversionUtil = new FormConversionHelper(queryStepTransformer);
+
+ return List.of(
+ new CQDateRestrictionConverter(),
+ new CQAndConverter(),
+ new CQOrConverter(),
+ new CQNegationConverter(),
+ new CQYesConverter(),
+ new CQConceptConverter(),
+ new CQExternalConverter(),
+ new CQReusedQueryConverter(),
+ new ConceptQueryConverter(queryStepTransformer),
+ new SecondaryIdQueryConverter(),
+ new AbsoluteFormQueryConverter(formConversionUtil),
+ new EntityDateQueryConverter(formConversionUtil),
+ new RelativFormQueryConverter(formConversionUtil),
+ new TableExportQueryConverter(queryStepTransformer)
+ );
+ }
+
+ default Map, ? extends SelectConverter extends Select>> getSelectConverterOverrides() {
+ return Collections.emptyMap();
+ }
+
+ default SelectConverter getSelectConverter(Select select) {
+ SelectConverter maybeOverride = (SelectConverter) getSelectConverterOverrides().get(select.getClass());
+
+ if (maybeOverride != null) {
+ return maybeOverride;
+ }
+
+ return select.createConverter();
+ }
+}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
index 79f1e6532e..6ab0db36ed 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
@@ -38,7 +38,7 @@ public class QueryStepJoiner {
*/
public static QueryStep antiJoinWithAllIdsTable(QueryStep queryStep, ConversionContext context, DateAggregationAction dateAggregationAction) {
- SqlFunctionProvider functionProvider = context.getConversionContext().getDialectBundle().getFunctionProvider();
+ SqlFunctionProvider functionProvider = context.getConversionContext().getCompilerDialect().getFunctionProvider();
Field queryStepPrimaryColumn = queryStep.getQualifiedSelects().getIds().getPrimaryColumn();
ColumnConfig idColumnConfig = context.getIdColumns().findPrimaryIdColumn();
@@ -190,7 +190,7 @@ private static QueryStep joinStepsContainingNegation(
}
// first, invert dates of negated step
- SqlDateAggregator dateAggregator = context.getDialectBundle().getDateAggregator();
+ SqlDateAggregator dateAggregator = context.getCompilerDialect().getDateAggregator();
negateJoined = dateAggregator.invertAggregatedIntervals(negateJoined, context);
// join with all-ids table necessary
@@ -204,7 +204,7 @@ private static QueryStep joinStepsContainingNegation(
DateAggregationDates aggregationDates = DateAggregationDates.forValidityDates(List.of(
nonNegateJoined.getQualifiedSelects().getValidityDate(),
negateJoined.getQualifiedSelects().getValidityDate(),
- Optional.of(context.getDialectBundle().getFunctionProvider().allRangeIf(infinityRangeCondition))
+ Optional.of(context.getCompilerDialect().getFunctionProvider().allRangeIf(infinityRangeCondition))
));
ColumnDateRange merged =
dateAggregator.getAggregatedValidityDate(aggregationDates, DateAggregationAction.MERGE)
@@ -326,7 +326,7 @@ private static QueryStep buildStepAndAggregateDates(
withAllValidityDates.addAll(dateAggregationDates.allStartsAndEnds());
QueryStep joinedStep = buildJoinedStep(ids, withAllValidityDates, Optional.empty(), Optional.empty(), builder);
- SqlDateAggregator sqlDateAggregator = context.getDialectBundle().getDateAggregator();
+ SqlDateAggregator sqlDateAggregator = context.getCompilerDialect().getDateAggregator();
return sqlDateAggregator.apply(
joinedStep,
mergedSelects,
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountQuartersSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountQuartersSqlAggregator.java
index e0ebf555de..07548d1ce6 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountQuartersSqlAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountQuartersSqlAggregator.java
@@ -111,7 +111,7 @@ public ConnectorSqlSelects connectorSelect(CountQuartersSelect countQuartersSele
selectContext.getNameGenerator().selectName(countQuartersSelect),
selectContext.getTables(),
selectContext.getFunctionProvider(),
- selectContext.getDialectBundle().getStratificationFunctions()
+ selectContext.getCompilerDialect().getStratificationFunctions()
);
String finalPredecessor = selectContext.getTables().getPredecessor(ConceptCteStep.AGGREGATION_FILTER);
@@ -134,7 +134,7 @@ public SqlFilters convertToSqlFilter(CountQuartersFilter countQuartersFilter, Fi
filterContext.getNameGenerator().selectName(countQuartersFilter),
filterContext.getTables(),
filterContext.getFunctionProvider(),
- filterContext.getDialectBundle().getStratificationFunctions()
+ filterContext.getCompilerDialect().getStratificationFunctions()
);
ConnectorSqlSelects selects = ConnectorSqlSelects.builder()
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java
index a40af4eeda..5f975b3cbf 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java
@@ -137,7 +137,7 @@ private static List getRequiredColumns(Map flags, Set<
@Override
public ConnectorSqlSelects connectorSelect(FlagSelect flagSelect, SelectContext selectContext) {
- SqlFunctionProvider functionProvider = selectContext.getConversionContext().getDialectBundle().getFunctionProvider();
+ SqlFunctionProvider functionProvider = selectContext.getConversionContext().getCompilerDialect().getFunctionProvider();
SqlTables connectorTables = selectContext.getTables();
Map rootSelects = createFlagRootSelectMap(flagSelect, connectorTables.getRootTable());
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
index 005be1cd75..c16a91dc76 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
@@ -24,7 +24,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.concept.IntervalPackingSelectsCte;
import com.bakdata.conquery.sql.conversion.cqelement.intervalpacking.IntervalPackingContext;
import com.bakdata.conquery.sql.conversion.cqelement.intervalpacking.IntervalPackingCteStep;
-import com.bakdata.conquery.sql.conversion.dialect.DialectBundle;
+import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.CteStep;
@@ -57,7 +57,7 @@ public static ConnectorSqlSelects c
.collect(Collectors.toList());
SqlTables daterangeSelectTables = createTables(alias, context.getTables(), context);
- QueryStep lastIntervalPackingStep = applyIntervalPacking(daterange, daterangeSelectTables, context.getIds(), context.getTables(), context.getDialectBundle());
+ QueryStep lastIntervalPackingStep = applyIntervalPacking(daterange, daterangeSelectTables, context.getIds(), context.getTables(), context.getCompilerDialect());
ColumnDateRange qualified = daterange.qualify(daterangeSelectTables.getPredecessor(INTERVAL_PACKING_SELECTS));
FieldWrapper> aggregationField = aggregationFunction.apply(qualified, alias, functionProvider);
@@ -67,7 +67,7 @@ public static ConnectorSqlSelects c
qualified,
aggregationField,
daterangeSelectTables,
- context.getDialectBundle()
+ context.getCompilerDialect()
);
ConnectorSqlTables tables = context.getTables();
@@ -91,7 +91,7 @@ public static SqlFilters createForFilter(
FilterContext> context
) {
String alias = context.getNameGenerator().selectName((LabeledNamespaceIdentifiable>) filter);
- SqlFunctionProvider functionProvider = context.getDialectBundle().getFunctionProvider();
+ SqlFunctionProvider functionProvider = context.getCompilerDialect().getFunctionProvider();
ColumnDateRange daterange = functionProvider.forArbitraryDateRange(filter).as(alias);
List rootSelects = daterange.toFields().stream()
@@ -99,7 +99,7 @@ public static SqlFilters createForFilter(
.collect(Collectors.toList());
SqlTables daterangeSelectTables = createTables(alias, context.getTables(), context);
- QueryStep lastIntervalPackingStep = applyIntervalPacking(daterange, daterangeSelectTables, context.getIds(), context.getTables(), context.getDialectBundle());
+ QueryStep lastIntervalPackingStep = applyIntervalPacking(daterange, daterangeSelectTables, context.getIds(), context.getTables(), context.getCompilerDialect());
ColumnDateRange qualified = daterange.qualify(daterangeSelectTables.getPredecessor(INTERVAL_PACKING_SELECTS));
FieldWrapper> aggregationField = aggregationFunction.apply(qualified, alias, functionProvider);
@@ -109,7 +109,7 @@ public static SqlFilters createForFilter(
qualified,
aggregationField,
daterangeSelectTables,
- context.getDialectBundle()
+ context.getCompilerDialect()
);
ConnectorSqlSelects sqlSelects = ConnectorSqlSelects.builder()
@@ -143,8 +143,8 @@ private static Condition containsInfinityDate(ColumnDateRange validityDate, SqlF
private static SqlTables createTables(String alias, ConnectorSqlTables connectorTables, Context context) {
Map predecessorMapping = new HashMap<>();
String preprocessingCteName = connectorTables.cteName(PREPROCESSING);
- predecessorMapping.putAll(IntervalPackingCteStep.getMappings(context.getDialectBundle()));
- if (context.getDialectBundle().supportsSingleColumnRanges()) {
+ predecessorMapping.putAll(IntervalPackingCteStep.getMappings(context.getCompilerDialect()));
+ if (context.getCompilerDialect().supportsSingleColumnRanges()) {
predecessorMapping.put(UNNEST_DATE, INTERVAL_COMPLETE);
predecessorMapping.put(INTERVAL_PACKING_SELECTS, UNNEST_DATE);
}
@@ -160,7 +160,7 @@ private static QueryStep applyIntervalPacking(
SqlTables dateUnionTables,
SqlIdColumns idColumns,
ConnectorSqlTables connectorSqlTables,
- DialectBundle sqlDialect
+ LegacyCompilerDialect sqlDialect
) {
String preprocessingCteName = connectorSqlTables.cteName(PREPROCESSING);
IntervalPackingContext intervalPackingContext = IntervalPackingContext.builder()
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java
index 1239ef8d87..446bc81397 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java
@@ -6,7 +6,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptSqlTables;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
-import com.bakdata.conquery.sql.conversion.dialect.DialectBundle;
+import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
@@ -25,8 +25,8 @@ public class SelectContext implements Context {
ConversionContext conversionContext;
@Override
- public DialectBundle getDialectBundle() {
- return getConversionContext().getDialectBundle();
+ public LegacyCompilerDialect getCompilerDialect() {
+ return getConversionContext().getCompilerDialect();
}
public static SelectContext create(
@@ -48,7 +48,7 @@ public static SelectContext create(
}
public SqlFunctionProvider getFunctionProvider() {
- return getDialectBundle().getFunctionProvider();
+ return getCompilerDialect().getFunctionProvider();
}
public SqlIdColumns getIds() {
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/AbsoluteFormQueryConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/AbsoluteFormQueryConverter.java
index e63356c199..7afb49e42a 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/AbsoluteFormQueryConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/AbsoluteFormQueryConverter.java
@@ -55,7 +55,7 @@ private static QueryStep convertPrerequisite(AbsoluteFormQuery absoluteForm, Con
Preconditions.checkArgument(withConvertedPrerequisite.getQuerySteps().size() == 1, "Base query conversion should produce exactly 1 QueryStep");
QueryStep convertedPrerequisite = withConvertedPrerequisite.getLastConvertedStep();
- ColumnDateRange bounds = context.getDialectBundle()
+ ColumnDateRange bounds = context.getCompilerDialect()
.getFunctionProvider()
.forCDateRange(CDateRange.of(absoluteForm.getDateRange())).as(SharedAliases.STRATIFICATION_BOUNDS.getAlias());
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java
index 343b70a2ae..5aeff1a381 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java
@@ -56,7 +56,7 @@ public Class getConversionClass() {
@Override
public ConversionContext convert(ConceptQuery conceptQuery, ConversionContext context) {
- SqlFunctionProvider functionProvider = context.getDialectBundle().getFunctionProvider();
+ SqlFunctionProvider functionProvider = context.getCompilerDialect().getFunctionProvider();
ConversionContext contextAfterConversion = context.getNodeConversions().convert(conceptQuery.getRoot(), context);
QueryStep preFinalStep = contextAfterConversion.getLastConvertedStep();
diff --git a/sql-connector/README.md b/sql-connector/README.md
index c29418cf29..62d49ee2eb 100644
--- a/sql-connector/README.md
+++ b/sql-connector/README.md
@@ -21,7 +21,8 @@ The module uses top-level packages as architectural boundaries:
- `model` contains only the immutable, fully resolved compiler input;
- `validation` contains Bean Validation constraints and the explicit validation boundary;
-- SQL business logic belongs in sibling packages such as `conversion`, `dialect`, and `execution`, not below `model`.
+- `compiler` contains framework-neutral compilation contracts, including the public dialect capabilities;
+- SQL conversion and execution implementations belong in sibling packages, not below `model`.
The resolved model is organized by responsibility:
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/dialect/CompilerDialect.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/dialect/CompilerDialect.java
new file mode 100644
index 0000000000..f22d696aa0
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/dialect/CompilerDialect.java
@@ -0,0 +1,22 @@
+package com.bakdata.conquery.sql.compiler.dialect;
+
+/**
+ * Database-specific capabilities exposed to the framework-neutral SQL compiler.
+ *
+ * This contract belongs to the SQL connector and must only use types owned by the connector or its public model
+ * dependencies. Backend query DTOs, converter registries, runtime connections, and result processing are deliberately
+ * excluded.
+ *
+ * Concrete backend integrations may extend this contract with temporary adapter interfaces while the legacy compiler
+ * is migrated. Those adapters must not become dependencies of the framework-neutral compiler.
+ */
+public interface CompilerDialect {
+
+ /** Maximum identifier length supported by the target database. */
+ int getNameMaxLength();
+
+ /** Whether the dialect represents date ranges in one database column instead of separate start and end columns. */
+ default boolean supportsSingleColumnRanges() {
+ return false;
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/dialect/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/dialect/package-info.java
new file mode 100644
index 0000000000..fbdeb27198
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/dialect/package-info.java
@@ -0,0 +1,2 @@
+/** Database-specific capabilities exposed through the SQL compiler API. */
+package com.bakdata.conquery.sql.compiler.dialect;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/package-info.java
new file mode 100644
index 0000000000..b9027865d6
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/package-info.java
@@ -0,0 +1,2 @@
+/** Framework-neutral contracts for compiling resolved queries into executable SQL. */
+package com.bakdata.conquery.sql.compiler;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/package-info.java
index 92d232da46..f3fb3af679 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/package-info.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/package-info.java
@@ -1,8 +1,8 @@
/**
* Framework-neutral SQL connector contracts and services.
*
- * {@code model} defines the fully resolved input, {@code validation} verifies that input, and sibling packages such
- * as {@code conversion}, {@code dialect}, and {@code execution} contain SQL business logic. Business logic may depend on
- * the model and validation boundary; model code must not depend on compiler or execution services.
+ * {@code model} defines the fully resolved input, {@code validation} verifies that input, and {@code compiler}
+ * defines framework-neutral compilation contracts. Conversion and execution implementations may depend on these
+ * contracts; model code must not depend on compiler or execution services.
*/
package com.bakdata.conquery.sql;
From 16061d4ac491c4f249fef53bcb0b1aeaf94a4251 Mon Sep 17 00:00:00 2001
From: MT <12283268+thoniTUB@users.noreply.github.com>
Date: Fri, 28 Aug 2026 15:09:20 +0200
Subject: [PATCH 06/81] refactor(sql): decouple query steps from dialect
Remove the unused SQL function provider from union-step construction so the
compiler IR no longer carries a false runtime dependency. Also replace
Javadoc-only links to select DTOs with IR terminology.
Co-authored-by: Codex GPT-5
---
.../sql/conversion/cqelement/CQExternalConverter.java | 6 +++---
.../sql/conversion/forms/RelativeStratification.java | 4 ++--
.../conversion/forms/StratificationTableFactory.java | 2 +-
.../conquery/sql/conversion/model/QueryStep.java | 11 +++++------
.../conquery/sql/conversion/model/Selects.java | 5 ++---
.../model/select/ConceptColumnSelectConverter.java | 2 +-
.../sql/conversion/model/select/SqlSelect.java | 5 ++---
.../conversion/query/TableExportQueryConverter.java | 2 +-
8 files changed, 17 insertions(+), 20 deletions(-)
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
index 0b70eb2435..e06ed92761 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
@@ -41,7 +41,7 @@ private static QueryStep createExternalIdsCte(CQExternal external, SqlFunctionPr
unions.addAll(rowSelects);
}
Preconditions.checkArgument(!unions.isEmpty(), "Expecting at least 1 resolved row when converting a CQExternal");
- QueryStep allStep = QueryStep.createUnionAllStep(unions, CQ_EXTERNAL_IDS_CTE_NAME, Collections.emptyList(), context.isNegation(), functionProvider);
+ QueryStep allStep = QueryStep.createUnionAllStep(unions, CQ_EXTERNAL_IDS_CTE_NAME, Collections.emptyList(), context.isNegation());
Optional maybeValidityDate = allStep.getSelects().getValidityDate();
@@ -173,8 +173,8 @@ private QueryStep createExternalExtrasCte(CQExternal external, SqlFunctionProvid
unions.add(rowSelects);
}
Preconditions.checkArgument(!unions.isEmpty(), "Expecting at least 1 converted resolved row when converting a CQExternal");
- return QueryStep.createUnionAllStep(unions, CQ_EXTERNAL_EXTRAS_CTE_NAME, Collections.emptyList(), context.isNegation(), functionProvider);
+ return QueryStep.createUnionAllStep(unions, CQ_EXTERNAL_EXTRAS_CTE_NAME, Collections.emptyList(), context.isNegation());
}
-}
\ No newline at end of file
+}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java
index d06755cbdc..8731afabb7 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java
@@ -163,7 +163,7 @@ private QueryStep createCompleteTable(QueryStep totalBoundsStep, RelativeFormQue
Stream.concat(Stream.ofNullable(outcomeTable), Stream.ofNullable(featureTable)).toList(),
FormCteStep.COMPLETE.getSuffix(),
Collections.emptyList(),
- context.isNegation(), functionProvider
+ context.isNegation()
);
}
@@ -196,7 +196,7 @@ private QueryStep createIntervalTable(QueryStep totalBoundsStep, Resolution reso
List.of(timeBeforeStep, timeAfterStep),
FormCteStep.stratificationCte(resolution).getSuffix(),
Collections.emptyList(),
- context.isNegation(), functionProvider
+ context.isNegation()
);
}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/StratificationTableFactory.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/StratificationTableFactory.java
index 4287a5ace8..046f300e3d 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/StratificationTableFactory.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/StratificationTableFactory.java
@@ -55,7 +55,7 @@ protected static QueryStep unionResolutionTables(List unionSteps, Lis
withQualifiedSelects,
FormCteStep.FULL_STRATIFICATION.getSuffix(),
Stream.concat(predecessors.stream(), unionSteps.stream()).toList(),
- context.isNegation(), context.getFunctionProvider()
+ context.isNegation()
);
}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStep.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStep.java
index ee3d96bcde..70d9d81279 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStep.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStep.java
@@ -1,6 +1,5 @@
package com.bakdata.conquery.sql.conversion.model;
-import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
import lombok.Builder;
import lombok.Singular;
@@ -62,15 +61,15 @@ public class QueryStep {
@Singular
List predecessors;
- public static QueryStep createUnionAllStep(List unionSteps, String cteName, List predecessors, boolean negation, SqlFunctionProvider functionProvider) {
- return createUnionStep(unionSteps, cteName, predecessors, true, negation, functionProvider);
+ public static QueryStep createUnionAllStep(List unionSteps, String cteName, List predecessors, boolean negation) {
+ return createUnionStep(unionSteps, cteName, predecessors, true, negation);
}
- public static QueryStep createUnionStep(List unionSteps, String cteName, List predecessors, boolean negation, SqlFunctionProvider functionProvider) {
- return createUnionStep(unionSteps, cteName, predecessors, false, negation, functionProvider);
+ public static QueryStep createUnionStep(List unionSteps, String cteName, List predecessors, boolean negation) {
+ return createUnionStep(unionSteps, cteName, predecessors, false, negation);
}
- private static QueryStep createUnionStep(List unionSteps, String cteName, List predecessors, boolean unionAll, boolean negation, SqlFunctionProvider functionProvider) {
+ private static QueryStep createUnionStep(List unionSteps, String cteName, List predecessors, boolean unionAll, boolean negation) {
QueryStep first = unionSteps.getFirst();
return first.toBuilder()
.cteName(cteName)
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java
index 160bae7711..a13db9b221 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java
@@ -1,6 +1,5 @@
package com.bakdata.conquery.sql.conversion.model;
-import com.bakdata.conquery.models.datasets.concepts.select.Select;
import com.bakdata.conquery.sql.conversion.SharedAliases;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
@@ -87,7 +86,7 @@ public List> all() {
}
/**
- * All {@link Select}s that have not been explicitly selected (IDs, validity/stratification dates).
+ * All fields that have not been explicitly selected (IDs, validity/stratification dates).
*/
public List> nonExplicitSelects() {
return Stream.of(
@@ -102,7 +101,7 @@ public List> nonExplicitSelects() {
}
/**
- * All explicitly selected and converted {@link Select}s.
+ * All explicitly selected and converted fields.
*/
public List> explicitSelects() {
return this.sqlSelects.stream()
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptColumnSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptColumnSelectConverter.java
index 56b5301cbe..ff06d0116e 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptColumnSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptColumnSelectConverter.java
@@ -115,7 +115,7 @@ private static QueryStep createUnionConnectorConnectorsStep(
) {
List unionSteps = connectors.stream().map(connector -> createConnectorColumnSelectQuery(connector, select, alias, selectContext)).toList();
String unionedColumnsCteName = selectContext.getNameGenerator().cteStepName(CONCEPT_COLUMN_STEPS.UNIONED_COLUMNS, alias);
- return QueryStep.createUnionStep(unionSteps, unionedColumnsCteName, Collections.emptyList(), false, selectContext.getFunctionProvider()); //TODO is false correct here?
+ return QueryStep.createUnionStep(unionSteps, unionedColumnsCteName, Collections.emptyList(), false); //TODO is false correct here?
}
private static QueryStep createConnectorColumnSelectQuery(
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelect.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelect.java
index 5ca54b46c4..cfb17e6cf2 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelect.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelect.java
@@ -1,6 +1,5 @@
package com.bakdata.conquery.sql.conversion.model.select;
-import com.bakdata.conquery.models.datasets.concepts.select.concept.specific.ExistsSelect;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.model.Qualifiable;
import org.jooq.Field;
@@ -24,14 +23,14 @@ default boolean isUniversal() {
}
/**
- * Special selects like {@link ExistsSelect} require an extra aggregation when joining them with other connectors.
+ * Special selects like {@link ExistsSqlSelect} require an extra aggregation when joining them with other connectors.
*/
default SqlSelect connectorAggregate() {
return this;
}
/**
- * Special selects like {@link ExistsSelect} require to be converted into a specific format before executing the
+ * Special selects like {@link ExistsSqlSelect} require to be converted into a specific format before executing the
* final query.
*/
default SqlSelect toFinalRepresentation() {
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java
index 5fe2b44e41..7c0c643e45 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java
@@ -206,7 +206,7 @@ public ConversionContext convert(TableExportQuery tableExportQuery, ConversionCo
convertedTables,
null, // no CTE name required as this step will be the final select
List.of(convertedPrerequisite),
- context.isNegation(), context.getFunctionProvider()
+ context.isNegation()
);
final Select selectQuery = queryStepTransformer.toSelectQuery(unionedTables, context.getFunctionProvider());
From f50349cdc06b7ea7e113fc6ebe30b695e02c5998 Mon Sep 17 00:00:00 2001
From: MT <12283268+thoniTUB@users.noreply.github.com>
Date: Fri, 28 Aug 2026 15:26:51 +0200
Subject: [PATCH 07/81] refactor(sql): expose aggregation through compiler
dialect
Make dialect-specific final select aggregation part of CompilerDialect and
bridge the legacy backend provider to the new contract. Add jOOQ to the
connector module so compiler IR can depend directly on SQL expression types.
Co-authored-by: Codex GPT-5
---
backend/pom.xml | 2 +-
.../conversion/dialect/LegacyCompilerDialect.java | 6 ++++++
.../conversion/model/select/ExistsSqlSelect.java | 4 ++--
.../sql/conversion/model/select/SqlSelect.java | 6 +++---
.../sql/conversion/query/ConceptQueryConverter.java | 13 ++++++++-----
pom.xml | 1 +
sql-connector/pom.xml | 5 +++++
.../sql/compiler/dialect/CompilerDialect.java | 10 ++++++++++
8 files changed, 36 insertions(+), 11 deletions(-)
diff --git a/backend/pom.xml b/backend/pom.xml
index 6bfa236947..835a22ff89 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -351,7 +351,7 @@
org.jooq
jooq
- 3.20.2
+ ${jooq.version}
com.zaxxer
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/LegacyCompilerDialect.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/LegacyCompilerDialect.java
index 4aed1ff758..38db1fd994 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/LegacyCompilerDialect.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/LegacyCompilerDialect.java
@@ -27,6 +27,7 @@
import com.bakdata.conquery.sql.conversion.query.SecondaryIdQueryConverter;
import com.bakdata.conquery.sql.conversion.query.TableExportQueryConverter;
import org.jooq.DSLContext;
+import org.jooq.Field;
/**
* Temporary backend adapter exposing services required by the legacy SQL compiler.
@@ -37,6 +38,11 @@
*/
public interface LegacyCompilerDialect extends CompilerDialect {
+ @Override
+ default Field anyValue(Field field) {
+ return getFunctionProvider().anyValue(field);
+ }
+
StratificationFunctions getStratificationFunctions();
SqlFunctionProvider getFunctionProvider();
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSqlSelect.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSqlSelect.java
index b6a15eaab5..2b7c01ef2b 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSqlSelect.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSqlSelect.java
@@ -1,6 +1,6 @@
package com.bakdata.conquery.sql.conversion.model.select;
-import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
+import com.bakdata.conquery.sql.compiler.dialect.CompilerDialect;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.jooq.Field;
@@ -61,7 +61,7 @@ public SqlSelect toFinalRepresentation() {
}
@Override
- public List> aggregateForFinalQuery(SqlFunctionProvider functionProvider) {
+ public List> aggregateForFinalQuery(CompilerDialect dialect) {
// We have to coalesce at the end of the query because full-outer-joins will create null values, which we want to avoid.
Field coalesced = coalesce(max(select()), inline(0));
return List.of(coalesced.as(alias));
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelect.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelect.java
index cfb17e6cf2..08738c3d50 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelect.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelect.java
@@ -1,6 +1,6 @@
package com.bakdata.conquery.sql.conversion.model.select;
-import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
+import com.bakdata.conquery.sql.compiler.dialect.CompilerDialect;
import com.bakdata.conquery.sql.conversion.model.Qualifiable;
import org.jooq.Field;
@@ -40,9 +40,9 @@ default SqlSelect toFinalRepresentation() {
/**
* Aggregate this select to one value per ID group in the final concept query.
*/
- default List> aggregateForFinalQuery(SqlFunctionProvider functionProvider) {
+ default List> aggregateForFinalQuery(CompilerDialect dialect) {
return toFinalRepresentation().toFields().stream()
- .>map(field -> functionProvider.anyValue(field).as(field.getName()))
+ .>map(field -> dialect.anyValue(field).as(field.getName()))
.toList();
}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java
index 5aeff1a381..bcd28f93c7 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java
@@ -3,8 +3,10 @@
import com.bakdata.conquery.apiv1.query.ConceptQuery;
import com.bakdata.conquery.apiv1.query.concept.specific.CQNegation;
import com.bakdata.conquery.models.query.DateAggregationMode;
+import com.bakdata.conquery.sql.compiler.dialect.CompilerDialect;
import com.bakdata.conquery.sql.conversion.NodeConverter;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
+import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.model.*;
import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
@@ -70,7 +72,7 @@ public ConversionContext convert(ConceptQuery conceptQuery, ConversionContext co
QueryStep finalStep = QueryStep.builder()
.cteName(null) // the final QueryStep won't be converted to a CTE
- .selects(getFinalSelects(conceptQuery, preFinalSelects, functionProvider))
+ .selects(getFinalSelects(conceptQuery, preFinalSelects, context.getCompilerDialect()))
.fromTable(getFinalTable(preFinalStep, contextAfterConversion))
.groupBy(getFinalGroupBySelects(preFinalSelects))
.predecessors(predecessors)
@@ -80,7 +82,8 @@ public ConversionContext convert(ConceptQuery conceptQuery, ConversionContext co
return contextAfterConversion.withFinalQuery(new SqlQuery(finalQuery, conceptQuery.getResultInfos()));
}
- private Selects getFinalSelects(ConceptQuery conceptQuery, Selects preFinalSelects, SqlFunctionProvider functionProvider) {
+ private Selects getFinalSelects(ConceptQuery conceptQuery, Selects preFinalSelects, LegacyCompilerDialect dialect) {
+ SqlFunctionProvider functionProvider = dialect.getFunctionProvider();
Selects finalSelects = preFinalSelects;
if (conceptQuery.getDateAggregationMode() == DateAggregationMode.NONE) {
finalSelects = preFinalSelects.blockValidityDate();
@@ -96,13 +99,13 @@ private Selects getFinalSelects(ConceptQuery conceptQuery, Selects preFinalSelec
.ids(finalSelects.getIds())
.validityDate(finalSelects.getValidityDate())
.stratificationDate(finalSelects.getStratificationDate())
- .sqlSelects(getFinalAggregatedSelects(finalSelects, functionProvider))
+ .sqlSelects(getFinalAggregatedSelects(finalSelects, dialect))
.build();
}
- private List extends FieldWrapper>> getFinalAggregatedSelects(Selects finalSelects, SqlFunctionProvider functionProvider) {
+ private List extends FieldWrapper>> getFinalAggregatedSelects(Selects finalSelects, CompilerDialect dialect) {
return finalSelects.getSqlSelects().stream()
- .flatMap(sqlSelect -> sqlSelect.aggregateForFinalQuery(functionProvider).stream())
+ .flatMap(sqlSelect -> sqlSelect.aggregateForFinalQuery(dialect).stream())
.map(this::toFieldWrapper)
.toList();
}
diff --git a/pom.xml b/pom.xml
index 5e2cd7311e..814b239874 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,6 +18,7 @@
21
+ 3.20.2
1.18.36
UTF-8
${java.required}
diff --git a/sql-connector/pom.xml b/sql-connector/pom.xml
index 073afc8767..aa3fee0be6 100644
--- a/sql-connector/pom.xml
+++ b/sql-connector/pom.xml
@@ -24,6 +24,11 @@
jakarta.validation
jakarta.validation-api
+
+ org.jooq
+ jooq
+ ${jooq.version}
+
org.junit.jupiter
junit-jupiter-api
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/dialect/CompilerDialect.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/dialect/CompilerDialect.java
index f22d696aa0..caa03b93b0 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/dialect/CompilerDialect.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/dialect/CompilerDialect.java
@@ -1,5 +1,7 @@
package com.bakdata.conquery.sql.compiler.dialect;
+import org.jooq.Field;
+
/**
* Database-specific capabilities exposed to the framework-neutral SQL compiler.
*
@@ -12,6 +14,14 @@
*/
public interface CompilerDialect {
+ /**
+ * Aggregate a field to an arbitrary value from its group.
+ *
+ * The exact expression is dialect-specific. For example, a database without an {@code ANY_VALUE} aggregate may
+ * use {@code MIN} as an equivalent deterministic representative where the compiler only requires one value.
+ */
+ Field anyValue(Field field);
+
/** Maximum identifier length supported by the target database. */
int getNameMaxLength();
From ab1b6e8ad1295db812eeafa85f700c21f6f30e52 Mon Sep 17 00:00:00 2001
From: MT <12283268+thoniTUB@users.noreply.github.com>
Date: Tue, 1 Sep 2026 08:17:58 +0200
Subject: [PATCH 08/81] feat(sql): define compiled query output contract
Add a self-contained compiled query with an ordered physical column layout so
backends can execute SQL and correlate results without compiler-owned result
processing. Replace presentation metadata in resolved result columns with stable
output identifiers and validate identifiers and SQL aliases for uniqueness.
Co-authored-by: Codex GPT-5
---
.../conquery/sql/compiler/ColumnRole.java | 11 ++++
.../conquery/sql/compiler/CompiledColumn.java | 22 +++++++
.../conquery/sql/compiler/CompiledQuery.java | 42 +++++++++++++
.../conquery/sql/compiler/package-info.java | 5 +-
.../conquery/sql/model/ResolvedQuery.java | 9 +++
.../sql/model/result/ResultColumn.java | 28 +++------
.../sql/model/result/package-info.java | 2 +-
.../sql/compiler/CompiledQueryTest.java | 61 +++++++++++++++++++
.../conquery/sql/model/ResolvedQueryTest.java | 21 +++++--
.../sql/model/ValidationTestSupport.java | 8 +--
10 files changed, 179 insertions(+), 30 deletions(-)
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ColumnRole.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/CompiledColumn.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/CompiledQuery.java
create mode 100644 sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/CompiledQueryTest.java
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ColumnRole.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ColumnRole.java
new file mode 100644
index 0000000000..b95a879037
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ColumnRole.java
@@ -0,0 +1,11 @@
+package com.bakdata.conquery.sql.compiler;
+
+/** Describes how a compiled output column contributes to a result row. */
+public enum ColumnRole {
+
+ /** Column containing an entity identifier. Multiple columns may form a composite identifier. */
+ ENTITY_ID,
+
+ /** Query result value decoded and post-processed by the backend. */
+ RESULT
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/CompiledColumn.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/CompiledColumn.java
new file mode 100644
index 0000000000..88e368f327
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/CompiledColumn.java
@@ -0,0 +1,22 @@
+package com.bakdata.conquery.sql.compiler;
+
+import com.bakdata.conquery.sql.model.result.ResultType;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+/**
+ * Ordered description of one column produced by a {@link CompiledQuery}.
+ *
+ * @param outputId stable, backend-independent key used to correlate the column with backend metadata
+ * @param sqlAlias alias rendered into the SQL statement
+ * @param type logical value type expected by backend result decoding
+ * @param role role of the column in the result row
+ */
+public record CompiledColumn(
+ @NotBlank String outputId,
+ @NotBlank String sqlAlias,
+ @NotNull @Valid ResultType type,
+ @NotNull ColumnRole role
+) {
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/CompiledQuery.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/CompiledQuery.java
new file mode 100644
index 0000000000..1e8faac0c0
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/CompiledQuery.java
@@ -0,0 +1,42 @@
+package com.bakdata.conquery.sql.compiler;
+
+import java.util.List;
+
+import com.bakdata.conquery.sql.model.internal.ModelNormalization;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.AssertTrue;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+
+/**
+ * Self-contained result of SQL compilation.
+ *
+ * The backend executes {@link #sql()} and uses the ordered {@link #columns()} to correlate and decode the JDBC
+ * result. Datasource selection, execution, and result post-processing are deliberately excluded.
+ */
+public record CompiledQuery(
+ @NotBlank String sql,
+ @NotEmpty List<@NotNull @Valid CompiledColumn> columns
+) {
+
+ public CompiledQuery {
+ columns = ModelNormalization.immutableCopy(columns);
+ }
+
+ @AssertTrue(message = "compiled column outputIds must be unique")
+ public boolean isOutputIdsUnique() {
+ return columns == null || columns.stream()
+ .map(CompiledColumn::outputId)
+ .distinct()
+ .count() == columns.size();
+ }
+
+ @AssertTrue(message = "compiled column SQL aliases must be unique")
+ public boolean isSqlAliasesUnique() {
+ return columns == null || columns.stream()
+ .map(CompiledColumn::sqlAlias)
+ .distinct()
+ .count() == columns.size();
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/package-info.java
index b9027865d6..9bf4ac5306 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/package-info.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/package-info.java
@@ -1,2 +1,5 @@
-/** Framework-neutral contracts for compiling resolved queries into executable SQL. */
+/**
+ * Framework-neutral contracts for compiling resolved queries into self-contained SQL and an ordered physical result
+ * layout. Query execution and result post-processing are backend responsibilities.
+ */
package com.bakdata.conquery.sql.compiler;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ResolvedQuery.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ResolvedQuery.java
index 3216636ab8..61022bc442 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ResolvedQuery.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ResolvedQuery.java
@@ -7,6 +7,7 @@
import com.bakdata.conquery.sql.model.result.ResultColumn;
import com.bakdata.conquery.sql.model.schema.EntitySchema;
import jakarta.validation.Valid;
+import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;
/**
@@ -25,4 +26,12 @@ public record ResolvedQuery(
public ResolvedQuery {
resultColumns = ModelNormalization.immutableCopy(resultColumns);
}
+
+ @AssertTrue(message = "result column outputIds must be unique")
+ public boolean isResultColumnOutputIdsUnique() {
+ return resultColumns == null || resultColumns.stream()
+ .map(ResultColumn::outputId)
+ .distinct()
+ .count() == resultColumns.size();
+ }
}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/ResultColumn.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/ResultColumn.java
index d67dff0702..f2af05a985 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/ResultColumn.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/ResultColumn.java
@@ -1,29 +1,17 @@
package com.bakdata.conquery.sql.model.result;
-import java.util.Optional;
-import java.util.Set;
-
-import com.bakdata.conquery.sql.model.internal.ModelNormalization;
import jakarta.validation.Valid;
-import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
-/** Ordered output-column metadata, with presentation names already resolved for the query locale. */
+/**
+ * Backend-independent identity and logical type of a requested result column.
+ *
+ * Labels, descriptions, semantics, and formatting instructions are deliberately excluded. Backends retain that
+ * presentation metadata and correlate it with compiled columns through {@link #outputId()}.
+ */
public record ResultColumn(
- @NotBlank String label,
- @NotNull Optional defaultLabel,
- @NotNull Optional description,
- @NotNull @Valid ResultType type,
- @NotNull Set<@NotBlank String> semantics
+ @NotBlank String outputId,
+ @NotNull @Valid ResultType type
) {
-
- public ResultColumn {
- semantics = ModelNormalization.immutableCopy(semantics);
- }
-
- @AssertTrue(message = "defaultLabel must not be blank when present")
- public boolean isDefaultLabelValid() {
- return defaultLabel == null || defaultLabel.isEmpty() || !defaultLabel.get().isBlank();
- }
}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/package-info.java
index f20ad2f630..32eca5222f 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/package-info.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/result/package-info.java
@@ -1,2 +1,2 @@
-/** Ordered result-column metadata and logical result types. */
+/** Backend-independent identities and logical types of requested result columns. */
package com.bakdata.conquery.sql.model.result;
diff --git a/sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/CompiledQueryTest.java b/sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/CompiledQueryTest.java
new file mode 100644
index 0000000000..feaedaaf16
--- /dev/null
+++ b/sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/CompiledQueryTest.java
@@ -0,0 +1,61 @@
+package com.bakdata.conquery.sql.compiler;
+
+import static com.bakdata.conquery.sql.model.ValidationTestSupport.assertInvalid;
+import static com.bakdata.conquery.sql.model.ValidationTestSupport.assertValid;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.bakdata.conquery.sql.model.result.ResultType;
+import org.junit.jupiter.api.Test;
+
+class CompiledQueryTest {
+
+ private static final CompiledColumn ENTITY_ID = new CompiledColumn(
+ "entity-id", "entity_id", ResultType.Primitive.STRING, ColumnRole.ENTITY_ID
+ );
+ private static final CompiledColumn RESULT = new CompiledColumn(
+ "concept.exists", "concept_exists", ResultType.Primitive.BOOLEAN, ColumnRole.RESULT
+ );
+
+ @Test
+ void shouldDescribeTheOrderedPhysicalResult() {
+ CompiledQuery query = new CompiledQuery("select entity_id, concept_exists from result", List.of(ENTITY_ID, RESULT));
+
+ assertValid(query);
+ assertEquals(List.of(ENTITY_ID, RESULT), query.columns());
+ }
+
+ @Test
+ void shouldDefensivelyCopyColumns() {
+ List columns = new ArrayList<>(List.of(ENTITY_ID));
+ CompiledQuery query = new CompiledQuery("select entity_id from result", columns);
+
+ columns.clear();
+
+ assertEquals(List.of(ENTITY_ID), query.columns());
+ assertThrows(UnsupportedOperationException.class, () -> query.columns().clear());
+ }
+
+ @Test
+ void shouldRejectIncompleteOutputContracts() {
+ assertInvalid(new CompiledColumn("", "entity_id", ResultType.Primitive.STRING, ColumnRole.ENTITY_ID));
+ assertInvalid(new CompiledQuery("", List.of(ENTITY_ID)));
+ assertInvalid(new CompiledQuery("select 1", List.of()));
+ }
+
+ @Test
+ void shouldRejectAmbiguousColumnMappings() {
+ CompiledColumn duplicateOutputId = new CompiledColumn(
+ ENTITY_ID.outputId(), "other_id", ResultType.Primitive.STRING, ColumnRole.RESULT
+ );
+ CompiledColumn duplicateAlias = new CompiledColumn(
+ "other-id", ENTITY_ID.sqlAlias(), ResultType.Primitive.STRING, ColumnRole.RESULT
+ );
+
+ assertInvalid(new CompiledQuery("select entity_id, other_id from result", List.of(ENTITY_ID, duplicateOutputId)));
+ assertInvalid(new CompiledQuery("select entity_id, entity_id from result", List.of(ENTITY_ID, duplicateAlias)));
+ }
+}
diff --git a/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ResolvedQueryTest.java b/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ResolvedQueryTest.java
index 4bcb23d39e..b191fd0676 100644
--- a/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ResolvedQueryTest.java
+++ b/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ResolvedQueryTest.java
@@ -9,7 +9,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
-import java.util.Set;
import com.bakdata.conquery.models.datasets.ColumnType;
import com.bakdata.conquery.sql.model.node.AndNode;
@@ -56,9 +55,7 @@ void shouldCreateResolvedQueryWithoutFrameworkTypes() {
new EntitySchema(EVENTS, ENTITY_ID),
concept,
true,
- List.of(new ResultColumn(
- "Entity", Optional.of("Entity"), Optional.empty(), ResultType.Primitive.STRING, Set.of("ID")
- ))
+ List.of(new ResultColumn("entity", ResultType.Primitive.STRING))
);
assertValid(query);
@@ -139,6 +136,22 @@ void shouldValidateTheCompleteQueryGraphAtTheCompilerBoundary() {
);
}
+ @Test
+ void shouldRejectAmbiguousResultColumnIds() {
+ ResolvedQuery query = new ResolvedQuery(
+ new ExecutionTarget("dataset", "warehouse"),
+ new EntitySchema(EVENTS, ENTITY_ID),
+ conceptNode(EVENTS),
+ false,
+ List.of(
+ new ResultColumn("duplicate", ResultType.Primitive.STRING),
+ new ResultColumn("duplicate", ResultType.Primitive.BOOLEAN)
+ )
+ );
+
+ assertInvalid(query);
+ }
+
private static ConceptNode conceptNode(SqlTable table) {
ResolvedColumn id = new ResolvedColumn("concept.id", table, "entity_id", ColumnType.STRING, false);
ResolvedConnector connector = new ResolvedConnector(
diff --git a/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ValidationTestSupport.java b/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ValidationTestSupport.java
index 9b57aeae8a..f15fb5ddcd 100644
--- a/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ValidationTestSupport.java
+++ b/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ValidationTestSupport.java
@@ -9,19 +9,19 @@
import jakarta.validation.Validation;
import jakarta.validation.Validator;
-final class ValidationTestSupport {
+public final class ValidationTestSupport {
- static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
+ public static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
private ValidationTestSupport() {
}
- static void assertValid(T value) {
+ public static void assertValid(T value) {
Set> violations = VALIDATOR.validate(value);
assertTrue(violations.isEmpty(), () -> "Expected no constraint violations, but got: " + violations);
}
- static void assertInvalid(T value) {
+ public static void assertInvalid(T value) {
Set> violations = VALIDATOR.validate(value);
assertFalse(violations.isEmpty(), "Expected at least one constraint violation");
}
From dbffb7beff5d01b3f08a2f4b3d863b2fac9fda34 Mon Sep 17 00:00:00 2001
From: MT <12283268+thoniTUB@users.noreply.github.com>
Date: Tue, 1 Sep 2026 08:33:57 +0200
Subject: [PATCH 09/81] feat(sql): define compiler invocation boundary
Add a backend-neutral SqlCompiler that compiles a resolved query for an
explicitly selected dialect. Remove execution routing from ResolvedQuery so
dataset revisions and datasource selection remain backend responsibilities.
Co-authored-by: Codex GPT-5
---
.../conquery/sql/compiler/SqlCompiler.java | 17 +++++++++++++
.../conquery/sql/compiler/package-info.java | 2 +-
.../conquery/sql/model/ExecutionTarget.java | 24 -------------------
.../conquery/sql/model/ResolvedQuery.java | 1 -
.../conquery/sql/model/package-info.java | 4 ++--
.../conquery/sql/model/ResolvedQueryTest.java | 4 ----
6 files changed, 20 insertions(+), 32 deletions(-)
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/SqlCompiler.java
delete mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/model/ExecutionTarget.java
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/SqlCompiler.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/SqlCompiler.java
new file mode 100644
index 0000000000..1c6bc06076
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/SqlCompiler.java
@@ -0,0 +1,17 @@
+package com.bakdata.conquery.sql.compiler;
+
+import com.bakdata.conquery.sql.compiler.dialect.CompilerDialect;
+import com.bakdata.conquery.sql.model.ResolvedQuery;
+
+/**
+ * Compiles a fully resolved query into self-contained SQL for a selected database dialect.
+ *
+ * The caller owns dataset and datasource selection, query execution, and result post-processing. Implementations
+ * must compile exclusively from the supplied query and dialect and must not resolve identifiers or access backend
+ * repositories.
+ */
+@FunctionalInterface
+public interface SqlCompiler {
+
+ CompiledQuery compile(ResolvedQuery query, CompilerDialect dialect);
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/package-info.java
index 9bf4ac5306..86869b1af8 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/package-info.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/package-info.java
@@ -1,5 +1,5 @@
/**
* Framework-neutral contracts for compiling resolved queries into self-contained SQL and an ordered physical result
- * layout. Query execution and result post-processing are backend responsibilities.
+ * layout. Dataset and datasource selection, query execution, and result post-processing are backend responsibilities.
*/
package com.bakdata.conquery.sql.compiler;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ExecutionTarget.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ExecutionTarget.java
deleted file mode 100644
index c4eed00aa1..0000000000
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ExecutionTarget.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.bakdata.conquery.sql.model;
-
-import java.util.Optional;
-
-import jakarta.validation.constraints.AssertTrue;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
-
-/** Selects the dataset snapshot and configured datasource used to execute a query. */
-public record ExecutionTarget(
- @NotBlank String datasetId,
- @NotBlank String dataSource,
- @NotNull Optional catalogRevision
-) {
-
- @AssertTrue(message = "catalogRevision must not be blank when present")
- public boolean isCatalogRevisionValid() {
- return catalogRevision == null || catalogRevision.isEmpty() || !catalogRevision.get().isBlank();
- }
-
- public ExecutionTarget(String datasetId, String dataSource) {
- this(datasetId, dataSource, Optional.empty());
- }
-}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ResolvedQuery.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ResolvedQuery.java
index 61022bc442..e4db885fcd 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ResolvedQuery.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/ResolvedQuery.java
@@ -16,7 +16,6 @@
* @param includeValidityDate whether the final result exposes an aggregated validity-date column
*/
public record ResolvedQuery(
- @NotNull @Valid ExecutionTarget target,
@NotNull @Valid EntitySchema entitySchema,
@NotNull @Valid QueryNode root,
boolean includeValidityDate,
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/model/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/package-info.java
index 9944bce082..247a51dd68 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/model/package-info.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/model/package-info.java
@@ -2,8 +2,8 @@
* Framework-neutral, fully resolved input model for SQL query compilation.
*
* Instances in this package must not require repository access or identifier resolution during compilation. They
- * describe query semantics and physical database objects, but deliberately exclude connections, dialects, and SQL
- * compiler state.
+ * describe query semantics and physical database objects, but deliberately exclude dataset routing, datasource
+ * selection, connections, dialects, and SQL compiler state.
*
* The root query aggregate is complemented by dedicated packages for query nodes, operations, ranges, result
* metadata, and physical schema references. Validation and SQL business logic live in sibling packages so this package
diff --git a/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ResolvedQueryTest.java b/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ResolvedQueryTest.java
index b191fd0676..aeea03f5e2 100644
--- a/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ResolvedQueryTest.java
+++ b/sql-connector/src/test/java/com/bakdata/conquery/sql/model/ResolvedQueryTest.java
@@ -51,7 +51,6 @@ void shouldCreateResolvedQueryWithoutFrameworkTypes() {
);
ResolvedQuery query = new ResolvedQuery(
- new ExecutionTarget("dataset", "warehouse"),
new EntitySchema(EVENTS, ENTITY_ID),
concept,
true,
@@ -61,7 +60,6 @@ void shouldCreateResolvedQueryWithoutFrameworkTypes() {
assertValid(query);
new ResolvedQueryValidation(ValidationTestSupport.VALIDATOR).validate(query);
assertEquals(concept, query.root());
- assertEquals("warehouse", query.target().dataSource());
}
@Test
@@ -123,7 +121,6 @@ void shouldValidateTheCompleteQueryGraphAtTheCompilerBoundary() {
List.of()
);
ResolvedQuery query = new ResolvedQuery(
- new ExecutionTarget("dataset", "warehouse"),
new EntitySchema(EVENTS, ENTITY_ID),
new ConceptNode("concept", List.of(invalidConnector), List.of(), DateAggregationAction.MERGE),
true,
@@ -139,7 +136,6 @@ void shouldValidateTheCompleteQueryGraphAtTheCompilerBoundary() {
@Test
void shouldRejectAmbiguousResultColumnIds() {
ResolvedQuery query = new ResolvedQuery(
- new ExecutionTarget("dataset", "warehouse"),
new EntitySchema(EVENTS, ENTITY_ID),
conceptNode(EVENTS),
false,
From 2f0fba78fa51567a4667451c789584e7f6e5ecde Mon Sep 17 00:00:00 2001
From: MT <12283268+thoniTUB@users.noreply.github.com>
Date: Tue, 1 Sep 2026 10:48:47 +0200
Subject: [PATCH 10/81] refactor(sql): move select IR contracts into connector
Move the qualification and select intermediate-representation contracts into
sql-connector so compiler transformations no longer depend on backend-owned
types. Update the legacy backend to consume the connector contracts.
Co-authored-by: Codex GPT-5.6-sol
---
.../specific/MappableSingleColumnSelect.java | 2 +-
.../cqelement/CQExternalConverter.java | 2 +-
.../aggregation/AnsiSqlDateAggregator.java | 2 +-
.../aggregation/DateAggregationContext.java | 4 +-
.../aggregation/DateAggregationDates.java | 2 +-
.../aggregation/IntermediateTableCte.java | 2 +-
.../IntersectAggregationAction.java | 2 +-
.../aggregation/MergeAggregateAction.java | 2 +-
.../cqelement/aggregation/RowNumberCte.java | 2 +-
.../aggregation/SqlAggregationAction.java | 2 +-
.../concept/AggregationFilterCte.java | 2 +-
.../concept/AggregationSelectCte.java | 2 +-
.../cqelement/concept/CQConceptConverter.java | 2 +-
.../concept/IntervalPackingSelectsCte.java | 2 +-
.../cqelement/concept/JoinBranchesCte.java | 2 +-
.../cqelement/concept/PreprocessingCte.java | 2 +-
.../intervalpacking/AggregationMode.java | 2 +-
.../AnsiSqlIntervalPacker.java | 2 +-
.../IntervalPackingContext.java | 2 +-
.../conversion/dialect/IntervalPacker.java | 2 +-
.../conversion/dialect/SqlDateAggregator.java | 2 +-
.../ClickhouseDistinctSelectConverter.java | 2 +-
.../sql/conversion/model/ColumnDateRange.java | 2 +-
.../sql/conversion/model/Qualifiable.java | 7 ---
.../sql/conversion/model/QualifyingUtil.java | 2 +-
.../sql/conversion/model/QueryStep.java | 2 +-
.../sql/conversion/model/QueryStepJoiner.java | 2 +-
.../sql/conversion/model/Selects.java | 2 +-
.../sql/conversion/model/SqlIdColumns.java | 1 +
.../aggregator/CommonAggregationSelect.java | 4 +-
.../model/aggregator/FlagSqlAggregator.java | 2 +-
.../model/aggregator/SumSqlAggregator.java | 2 +-
.../model/select/ConceptSqlSelects.java | 1 +
.../model/select/ConnectorSqlSelects.java | 1 +
.../model/select/DaterangeSelectUtil.java | 1 +
.../model/select/DistinctSelectConverter.java | 1 +
.../model/select/ExistsSqlSelect.java | 2 +
.../model/select/ExtractingSqlSelect.java | 1 +
.../conversion/model/select/FieldWrapper.java | 1 +
.../model/select/SingleColumnSqlSelect.java | 29 -----------
.../conversion/model/select/SqlSelect.java | 49 -------------------
.../model/select/ValueSelectUtil.java | 2 +
.../query/ConceptQueryConverter.java | 2 +-
.../conquery/sql/compiler/ir/Qualifiable.java | 7 +++
.../sql/compiler/ir/package-info.java | 2 +
.../ir/select/SingleColumnSqlSelect.java | 28 +++++++++++
.../sql/compiler/ir/select/SqlSelect.java | 38 ++++++++++++++
.../sql/compiler/ir/select/package-info.java | 2 +
48 files changed, 121 insertions(+), 118 deletions(-)
delete mode 100644 backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Qualifiable.java
delete mode 100644 backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SingleColumnSqlSelect.java
delete mode 100644 backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelect.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/Qualifiable.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/package-info.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/SingleColumnSqlSelect.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/SqlSelect.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/package-info.java
diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/MappableSingleColumnSelect.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/MappableSingleColumnSelect.java
index 8900fa8d2f..5f4dd5e91f 100644
--- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/MappableSingleColumnSelect.java
+++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/MappableSingleColumnSelect.java
@@ -28,7 +28,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.model.select.SelectContext;
-import com.bakdata.conquery.sql.conversion.model.select.SingleColumnSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
import com.bakdata.conquery.sql.execution.ResultSetProcessor;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.dropwizard.validation.ValidationMethod;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
index e06ed92761..bb6b95bd30 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
@@ -19,7 +19,7 @@
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.google.common.base.Preconditions;
import org.jooq.Field;
import org.jooq.Name;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/AnsiSqlDateAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/AnsiSqlDateAggregator.java
index 8656cd9bf4..98581b0e2c 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/AnsiSqlDateAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/AnsiSqlDateAggregator.java
@@ -17,7 +17,7 @@
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Data;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java
index f00fe6d120..3add526456 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java
@@ -8,12 +8,12 @@
import com.bakdata.conquery.sql.conversion.Context;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
-import com.bakdata.conquery.sql.conversion.model.Qualifiable;
+import com.bakdata.conquery.sql.compiler.ir.Qualifiable;
import com.bakdata.conquery.sql.conversion.model.QualifyingUtil;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Builder;
import lombok.Value;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationDates.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationDates.java
index c3c7e708e6..fc24b5d182 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationDates.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationDates.java
@@ -10,7 +10,7 @@
import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntermediateTableCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntermediateTableCte.java
index d7ccea9966..0aeb233c68 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntermediateTableCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntermediateTableCte.java
@@ -7,7 +7,7 @@
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Getter;
import org.jooq.Condition;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntersectAggregationAction.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntersectAggregationAction.java
index 70ee2023fb..bb0c0f650d 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntersectAggregationAction.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntersectAggregationAction.java
@@ -12,7 +12,7 @@
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.RequiredArgsConstructor;
import org.jooq.Field;
import org.jooq.impl.DSL;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/MergeAggregateAction.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/MergeAggregateAction.java
index d469015d41..8a22291e27 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/MergeAggregateAction.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/MergeAggregateAction.java
@@ -11,7 +11,7 @@
import com.bakdata.conquery.sql.conversion.model.NameGenerator;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.RequiredArgsConstructor;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java
index 01c1dd5299..b9131625e2 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java
@@ -8,7 +8,7 @@
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Getter;
import org.jooq.Field;
import org.jooq.impl.DSL;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/SqlAggregationAction.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/SqlAggregationAction.java
index 5b98c52122..2825aeb2f4 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/SqlAggregationAction.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/SqlAggregationAction.java
@@ -8,7 +8,7 @@
import com.bakdata.conquery.sql.conversion.model.NameGenerator;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
/**
* Represents a subset of {@link DateAggregationAction}.
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationFilterCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationFilterCte.java
index 85179b7043..9187b6c2f8 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationFilterCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationFilterCte.java
@@ -6,7 +6,7 @@
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.filter.WhereCondition;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import org.jooq.Condition;
class AggregationFilterCte extends ConnectorCte {
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationSelectCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationSelectCte.java
index 26ae3c6772..5d89475328 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationSelectCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationSelectCte.java
@@ -8,7 +8,7 @@
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import org.jooq.Field;
class AggregationSelectCte extends ConnectorCte {
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java
index e43c830d81..302b2d09d0 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java
@@ -30,7 +30,7 @@
import com.bakdata.conquery.sql.conversion.model.select.ConceptSqlSelects;
import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects;
import com.bakdata.conquery.sql.conversion.model.select.SelectContext;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.bakdata.conquery.util.TablePrimaryColumnUtil;
import com.google.common.base.Preconditions;
import org.jooq.Condition;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java
index 77572d97b2..8f2d508069 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java
@@ -13,7 +13,7 @@
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
import com.bakdata.conquery.sql.conversion.model.select.ConceptSqlSelects;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.google.common.base.Preconditions;
public class IntervalPackingSelectsCte {
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java
index 8a78d1dbd1..886826cb4b 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java
@@ -13,7 +13,7 @@
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.aggregator.SumSqlAggregator;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import org.jooq.Record;
import org.jooq.TableLike;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/PreprocessingCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/PreprocessingCte.java
index c31d3e6b51..5434578289 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/PreprocessingCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/PreprocessingCte.java
@@ -12,7 +12,7 @@
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters;
import com.bakdata.conquery.sql.conversion.model.filter.WhereCondition;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import org.jooq.Condition;
import org.jooq.Record;
import org.jooq.Table;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AggregationMode.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AggregationMode.java
index 45e6f3b03f..d1797b23dd 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AggregationMode.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AggregationMode.java
@@ -1,7 +1,7 @@
package com.bakdata.conquery.sql.conversion.cqelement.intervalpacking;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
/**
* Determines if the date that is getting interval-packed is stored as validity date of {@link Selects} or as arbitrary {@link SqlSelect}.
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java
index 178136ea1a..c9944bdcd6 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java
@@ -13,7 +13,7 @@
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.RequiredArgsConstructor;
import org.jooq.Field;
import org.jooq.impl.DSL;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java
index ee0f7e45ab..6fad39405d 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java
@@ -12,7 +12,7 @@
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Builder;
import lombok.Value;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/IntervalPacker.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/IntervalPacker.java
index 84652c01a6..508487ba34 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/IntervalPacker.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/IntervalPacker.java
@@ -3,7 +3,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.intervalpacking.IntervalPackingContext;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
/**
* Packing intervals involves packing groups of intersecting validity date intervals into their respective continuous intervals.
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlDateAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlDateAggregator.java
index 175ca894a5..225453ce14 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlDateAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlDateAggregator.java
@@ -7,7 +7,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.aggregation.DateAggregationDates;
import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
public interface SqlDateAggregator {
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseDistinctSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseDistinctSelectConverter.java
index fb6ddc89a4..5959d5c6f0 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseDistinctSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseDistinctSelectConverter.java
@@ -10,7 +10,7 @@
import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.model.select.SelectContext;
import com.bakdata.conquery.sql.conversion.model.select.SelectConverter;
-import com.bakdata.conquery.sql.conversion.model.select.SingleColumnSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
public class ClickhouseDistinctSelectConverter implements SelectConverter {
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/ColumnDateRange.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/ColumnDateRange.java
index f8679840ce..f2bb27ad46 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/ColumnDateRange.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/ColumnDateRange.java
@@ -5,7 +5,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Getter;
import lombok.ToString;
import org.jooq.Condition;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Qualifiable.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Qualifiable.java
deleted file mode 100644
index a5c54fa5e2..0000000000
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Qualifiable.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.bakdata.conquery.sql.conversion.model;
-
-public interface Qualifiable {
-
- T qualify(String qualifier);
-
-}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QualifyingUtil.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QualifyingUtil.java
index a4665c4957..599c81faf6 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QualifyingUtil.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QualifyingUtil.java
@@ -3,7 +3,7 @@
import java.util.List;
import java.util.stream.Collectors;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import org.jooq.Field;
import org.jooq.impl.DSL;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStep.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStep.java
index 70d9d81279..7f1161fba3 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStep.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStep.java
@@ -1,6 +1,6 @@
package com.bakdata.conquery.sql.conversion.model;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Builder;
import lombok.Singular;
import lombok.Value;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
index 6ab0db36ed..bbde527966 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
@@ -18,7 +18,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.aggregation.DateAggregationDates;
import com.bakdata.conquery.sql.conversion.dialect.SqlDateAggregator;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.google.common.base.Preconditions;
import org.jooq.Condition;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java
index a13db9b221..22bce87cdb 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java
@@ -2,7 +2,7 @@
import com.bakdata.conquery.sql.conversion.SharedAliases;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Builder;
import lombok.Singular;
import lombok.Value;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/SqlIdColumns.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/SqlIdColumns.java
index 99af726ccb..3b1b195393 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/SqlIdColumns.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/SqlIdColumns.java
@@ -10,6 +10,7 @@
import javax.annotation.Nullable;
import com.bakdata.conquery.models.forms.util.Resolution;
+import com.bakdata.conquery.sql.compiler.ir.Qualifiable;
import com.bakdata.conquery.sql.conversion.SharedAliases;
import lombok.AllArgsConstructor;
import lombok.Getter;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CommonAggregationSelect.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CommonAggregationSelect.java
index 20661511b9..74860a3a44 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CommonAggregationSelect.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CommonAggregationSelect.java
@@ -8,8 +8,8 @@
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects;
import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
-import com.bakdata.conquery.sql.conversion.model.select.SingleColumnSqlSelect;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Builder;
import lombok.Singular;
import lombok.Value;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java
index 5f975b3cbf..0d9fab1a42 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java
@@ -26,7 +26,7 @@
import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.model.select.SelectContext;
import com.bakdata.conquery.sql.conversion.model.select.SelectConverter;
-import com.bakdata.conquery.sql.conversion.model.select.SingleColumnSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
import org.jooq.Condition;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java
index 80c74df4fa..2e9e851d56 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java
@@ -30,7 +30,7 @@
import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.model.select.SelectContext;
import com.bakdata.conquery.sql.conversion.model.select.SelectConverter;
-import com.bakdata.conquery.sql.conversion.model.select.SingleColumnSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.jooq.Condition;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptSqlSelects.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptSqlSelects.java
index df250611e7..65efbf98b1 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptSqlSelects.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptSqlSelects.java
@@ -3,6 +3,7 @@
import java.util.List;
import java.util.Optional;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import lombok.Builder;
import lombok.Singular;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConnectorSqlSelects.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConnectorSqlSelects.java
index 4d360d62ec..22fe539410 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConnectorSqlSelects.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConnectorSqlSelects.java
@@ -3,6 +3,7 @@
import java.util.List;
import java.util.Optional;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import lombok.Builder;
import lombok.Singular;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
index c16a91dc76..8afe907ba9 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
@@ -17,6 +17,7 @@
import com.bakdata.conquery.models.datasets.concepts.DaterangeSelectOrFilter;
import com.bakdata.conquery.models.datasets.concepts.select.Select;
import com.bakdata.conquery.models.identifiable.LabeledNamespaceIdentifiable;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.bakdata.conquery.sql.conversion.Context;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java
index c875bc4b32..6219fe229e 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java
@@ -8,6 +8,7 @@
import com.bakdata.conquery.models.datasets.concepts.select.connector.DistinctSelect;
import com.bakdata.conquery.models.datasets.concepts.select.connector.specific.MappableSingleColumnSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSqlSelect.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSqlSelect.java
index 2b7c01ef2b..1f95b87568 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSqlSelect.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSqlSelect.java
@@ -1,6 +1,8 @@
package com.bakdata.conquery.sql.conversion.model.select;
import com.bakdata.conquery.sql.compiler.dialect.CompilerDialect;
+import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExtractingSqlSelect.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExtractingSqlSelect.java
index 620c84efda..5968c71d41 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExtractingSqlSelect.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExtractingSqlSelect.java
@@ -2,6 +2,7 @@
import java.util.List;
+import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
import lombok.Value;
import org.jooq.Field;
import org.jooq.impl.DSL;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/FieldWrapper.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/FieldWrapper.java
index 42a877a2e0..99f243c0f7 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/FieldWrapper.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/FieldWrapper.java
@@ -2,6 +2,7 @@
import java.util.List;
+import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
import lombok.EqualsAndHashCode;
import org.jooq.Field;
import org.jooq.impl.DSL;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SingleColumnSqlSelect.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SingleColumnSqlSelect.java
deleted file mode 100644
index 6004b196ee..0000000000
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SingleColumnSqlSelect.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.bakdata.conquery.sql.conversion.model.select;
-
-import java.util.List;
-
-import org.jooq.Field;
-
-public interface SingleColumnSqlSelect extends SqlSelect {
-
- /**
- * @return The whole (aliased) SQL expression of this {@link SqlSelect}.
- * For example, {@code DSL.firstValue(DSL.field(DSL.name("foo", "bar"))).as("foobar")}.
- */
- Field> select();
-
- /**
- * @return Aliased column name that can be used to reference the created select.
- * For example, {@code DSL.field("foobar")}.
- */
- Field> aliased();
-
- @Override
- SingleColumnSqlSelect qualify(String qualifier);
-
- @Override
- default List> toFields() {
- return List.of(select());
- }
-
-}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelect.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelect.java
deleted file mode 100644
index 08738c3d50..0000000000
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelect.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.bakdata.conquery.sql.conversion.model.select;
-
-import com.bakdata.conquery.sql.compiler.dialect.CompilerDialect;
-import com.bakdata.conquery.sql.conversion.model.Qualifiable;
-import org.jooq.Field;
-
-import java.util.List;
-
-public interface SqlSelect extends Qualifiable {
-
- List> toFields();
-
- /**
- * All column names this {@link SqlSelect} requires.
- */
- List requiredColumns();
-
- /**
- * @return Determines if this SqlSelect is only part of the final concept conversion CTE and has no predeceasing selects.
- */
- default boolean isUniversal() {
- return false;
- }
-
- /**
- * Special selects like {@link ExistsSqlSelect} require an extra aggregation when joining them with other connectors.
- */
- default SqlSelect connectorAggregate() {
- return this;
- }
-
- /**
- * Special selects like {@link ExistsSqlSelect} require to be converted into a specific format before executing the
- * final query.
- */
- default SqlSelect toFinalRepresentation() {
- return this;
- }
-
- /**
- * Aggregate this select to one value per ID group in the final concept query.
- */
- default List> aggregateForFinalQuery(CompilerDialect dialect) {
- return toFinalRepresentation().toFields().stream()
- .>map(field -> dialect.anyValue(field).as(field.getName()))
- .toList();
- }
-
-}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java
index e37d70b2a8..3d3405ffbd 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java
@@ -9,6 +9,8 @@
import com.bakdata.conquery.models.common.Range;
import com.bakdata.conquery.models.datasets.Column;
import com.bakdata.conquery.models.datasets.concepts.select.connector.specific.MappableSingleColumnSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java
index bcd28f93c7..6c5c65674c 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java
@@ -10,7 +10,7 @@
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.model.*;
import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.RequiredArgsConstructor;
import org.jooq.*;
import org.jooq.Record;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/Qualifiable.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/Qualifiable.java
new file mode 100644
index 0000000000..092dce642a
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/Qualifiable.java
@@ -0,0 +1,7 @@
+package com.bakdata.conquery.sql.compiler.ir;
+
+/** An intermediate representation that can be rebound to a qualified SQL table or CTE name. */
+public interface Qualifiable {
+
+ T qualify(String qualifier);
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/package-info.java
new file mode 100644
index 0000000000..33e6fa2b60
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/package-info.java
@@ -0,0 +1,2 @@
+/** Internal representations produced and transformed while compiling a resolved query. */
+package com.bakdata.conquery.sql.compiler.ir;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/SingleColumnSqlSelect.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/SingleColumnSqlSelect.java
new file mode 100644
index 0000000000..b75c2ece99
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/SingleColumnSqlSelect.java
@@ -0,0 +1,28 @@
+package com.bakdata.conquery.sql.compiler.ir.select;
+
+import java.util.List;
+
+import org.jooq.Field;
+
+/** Intermediate select represented by one SQL field. */
+public interface SingleColumnSqlSelect extends SqlSelect {
+
+ /**
+ * @return the complete aliased SQL expression, for example
+ * {@code DSL.firstValue(DSL.field(DSL.name("foo", "bar"))).as("foobar")}
+ */
+ Field> select();
+
+ /**
+ * @return the aliased column reference, for example {@code DSL.field("foobar")}
+ */
+ Field> aliased();
+
+ @Override
+ SingleColumnSqlSelect qualify(String qualifier);
+
+ @Override
+ default List> toFields() {
+ return List.of(select());
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/SqlSelect.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/SqlSelect.java
new file mode 100644
index 0000000000..6c7f1a47c0
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/SqlSelect.java
@@ -0,0 +1,38 @@
+package com.bakdata.conquery.sql.compiler.ir.select;
+
+import java.util.List;
+
+import com.bakdata.conquery.sql.compiler.dialect.CompilerDialect;
+import com.bakdata.conquery.sql.compiler.ir.Qualifiable;
+import org.jooq.Field;
+
+/** Intermediate representation of one or more SQL select expressions. */
+public interface SqlSelect extends Qualifiable {
+
+ List> toFields();
+
+ /** All column names this select requires. */
+ List requiredColumns();
+
+ /** Whether this select only occurs in the final concept CTE and has no preceding select. */
+ default boolean isUniversal() {
+ return false;
+ }
+
+ /** Apply an extra aggregation required when joining this select across connectors. */
+ default SqlSelect connectorAggregate() {
+ return this;
+ }
+
+ /** Convert this select into the representation returned by the final SQL query. */
+ default SqlSelect toFinalRepresentation() {
+ return this;
+ }
+
+ /** Aggregate this select to one value per ID group in the final concept query. */
+ default List> aggregateForFinalQuery(CompilerDialect dialect) {
+ return toFinalRepresentation().toFields().stream()
+ .>map(field -> dialect.anyValue(field).as(field.getName()))
+ .toList();
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/package-info.java
new file mode 100644
index 0000000000..65d930f995
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/package-info.java
@@ -0,0 +1,2 @@
+/** SQL select expressions used by the compiler intermediate representation. */
+package com.bakdata.conquery.sql.compiler.ir.select;
From 3bd0bad5c5fd1ead9beb7741cbb2a82c04d1ed58 Mon Sep 17 00:00:00 2001
From: MT <12283268+thoniTUB@users.noreply.github.com>
Date: Tue, 1 Sep 2026 12:48:10 +0200
Subject: [PATCH 11/81] refactor(sql): move select IR implementations into
connector
Move the field-backed, extracting, and existence select representations into
the connector-owned compiler IR. Use plain Java implementations, preserve the
legacy equality behavior, and cover the relocated transformations with tests.
Co-authored-by: Codex GPT-5.6-sol
---
.../specific/MappableSingleColumnSelect.java | 2 +-
.../conversion/cqelement/CQAndConverter.java | 2 +-
.../cqelement/CQExternalConverter.java | 2 +-
.../conversion/cqelement/CQOrConverter.java | 2 +-
.../aggregation/DateAggregationDates.java | 2 +-
.../IntersectAggregationAction.java | 2 +-
.../cqelement/aggregation/RowNumberCte.java | 2 +-
.../AnsiSqlIntervalPacker.java | 2 +-
.../ClickhouseDistinctSelectConverter.java | 2 +-
.../forms/AbsoluteStratification.java | 2 +-
.../forms/RelativeStratification.java | 2 +-
.../aggregator/CommonAggregationSelect.java | 2 +-
.../CountQuartersSqlAggregator.java | 4 +-
.../model/aggregator/CountSqlAggregator.java | 4 +-
.../aggregator/DateDistanceSqlAggregator.java | 2 +
.../model/aggregator/FlagSqlAggregator.java | 4 +-
.../model/aggregator/SumSqlAggregator.java | 4 +-
.../filter/AbstractSelectFilterConverter.java | 2 +-
.../model/filter/NumberFilterConverter.java | 2 +-
.../select/ConceptColumnSelectConverter.java | 2 +
.../select/DateUnionSelectConverter.java | 1 +
.../model/select/DaterangeSelectUtil.java | 2 +
.../model/select/DistinctSelectConverter.java | 1 +
.../select/EventDateUnionSelectConverter.java | 2 +
.../EventDurationSumSelectConverter.java | 2 +
.../model/select/ExistsSelectConverter.java | 1 +
.../model/select/ExtractingSqlSelect.java | 49 --------------
.../conversion/model/select/FieldWrapper.java | 63 ------------------
.../select/RandomValueSelectConverter.java | 2 +
.../model/select/ValueSelectUtil.java | 1 +
.../query/ConceptQueryConverter.java | 2 +-
.../query/FormConversionHelper.java | 2 +-
.../query/TableExportQueryConverter.java | 2 +-
.../sql/compiler/ir/package-info.java | 7 +-
.../compiler/ir}/select/ExistsSqlSelect.java | 37 ++++++-----
.../ir/select/ExtractingSqlSelect.java | 31 +++++++++
.../sql/compiler/ir/select/FieldWrapper.java | 66 +++++++++++++++++++
.../sql/compiler/ir/select/package-info.java | 2 +-
.../select/SqlSelectImplementationsTest.java | 55 ++++++++++++++++
39 files changed, 220 insertions(+), 156 deletions(-)
delete mode 100644 backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExtractingSqlSelect.java
delete mode 100644 backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/FieldWrapper.java
rename {backend/src/main/java/com/bakdata/conquery/sql/conversion/model => sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir}/select/ExistsSqlSelect.java (63%)
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/ExtractingSqlSelect.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/FieldWrapper.java
create mode 100644 sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/ir/select/SqlSelectImplementationsTest.java
diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/MappableSingleColumnSelect.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/MappableSingleColumnSelect.java
index 5f4dd5e91f..18bf21b494 100644
--- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/MappableSingleColumnSelect.java
+++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/MappableSingleColumnSelect.java
@@ -26,7 +26,7 @@
import com.bakdata.conquery.models.types.ResultType;
import com.bakdata.conquery.models.types.SemanticType;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.model.select.SelectContext;
import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
import com.bakdata.conquery.sql.execution.ResultSetProcessor;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQAndConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQAndConverter.java
index 6c8f98ced3..d70deb0efc 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQAndConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQAndConverter.java
@@ -5,7 +5,7 @@
import com.bakdata.conquery.sql.conversion.model.ConqueryJoinType;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.QueryStepJoiner;
-import com.bakdata.conquery.sql.conversion.model.select.ExistsSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.ExistsSqlSelect;
public class CQAndConverter implements NodeConverter {
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
index bb6b95bd30..05f47c7df0 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
@@ -18,7 +18,7 @@
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.google.common.base.Preconditions;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQOrConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQOrConverter.java
index d77d52a207..594de48460 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQOrConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQOrConverter.java
@@ -5,7 +5,7 @@
import com.bakdata.conquery.sql.conversion.model.ConqueryJoinType;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.QueryStepJoiner;
-import com.bakdata.conquery.sql.conversion.model.select.ExistsSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.ExistsSqlSelect;
public class CQOrConverter implements NodeConverter {
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationDates.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationDates.java
index fc24b5d182..3b71007745 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationDates.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationDates.java
@@ -9,7 +9,7 @@
import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntersectAggregationAction.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntersectAggregationAction.java
index bb0c0f650d..f54bee36cb 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntersectAggregationAction.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntersectAggregationAction.java
@@ -11,7 +11,7 @@
import com.bakdata.conquery.sql.conversion.model.NameGenerator;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.RequiredArgsConstructor;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java
index b9131625e2..e159bafe0e 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java
@@ -7,7 +7,7 @@
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Getter;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java
index c9944bdcd6..81230a0d05 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java
@@ -12,7 +12,7 @@
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.RequiredArgsConstructor;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseDistinctSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseDistinctSelectConverter.java
index 5959d5c6f0..1c6c77cf4d 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseDistinctSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseDistinctSelectConverter.java
@@ -7,7 +7,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.model.select.SelectContext;
import com.bakdata.conquery.sql.conversion.model.select.SelectConverter;
import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/AbsoluteStratification.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/AbsoluteStratification.java
index 190aa2c512..4d4b8b7127 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/AbsoluteStratification.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/AbsoluteStratification.java
@@ -15,7 +15,7 @@
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.google.common.base.Preconditions;
import lombok.RequiredArgsConstructor;
import org.jooq.Condition;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java
index 8731afabb7..3e75139cd9 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java
@@ -26,7 +26,7 @@
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.google.common.base.Preconditions;
import lombok.RequiredArgsConstructor;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CommonAggregationSelect.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CommonAggregationSelect.java
index 74860a3a44..c3d379b8fb 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CommonAggregationSelect.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CommonAggregationSelect.java
@@ -7,7 +7,7 @@
import com.bakdata.conquery.models.datasets.concepts.select.Select;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Builder;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountQuartersSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountQuartersSqlAggregator.java
index 07548d1ce6..cc530f2d81 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountQuartersSqlAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountQuartersSqlAggregator.java
@@ -20,8 +20,8 @@
import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters;
import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses;
import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects;
-import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.ExtractingSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.model.select.SelectContext;
import com.bakdata.conquery.sql.conversion.model.select.SelectConverter;
import org.jooq.Condition;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountSqlAggregator.java
index b1bc5222e8..59bf61c842 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountSqlAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountSqlAggregator.java
@@ -12,8 +12,8 @@
import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters;
import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses;
import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects;
-import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.ExtractingSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.model.select.SelectContext;
import com.bakdata.conquery.sql.conversion.model.select.SelectConverter;
import lombok.NoArgsConstructor;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DateDistanceSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DateDistanceSqlAggregator.java
index f540b01e41..ac66d083ed 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DateDistanceSqlAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DateDistanceSqlAggregator.java
@@ -5,6 +5,8 @@
import com.bakdata.conquery.models.datasets.Column;
import com.bakdata.conquery.models.datasets.concepts.filters.specific.DateDistanceFilter;
import com.bakdata.conquery.models.datasets.concepts.select.connector.specific.DateDistanceSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.ExtractingSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java
index 0d9fab1a42..056f8c3ebb 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java
@@ -22,8 +22,8 @@
import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters;
import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses;
import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects;
-import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.ExtractingSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.model.select.SelectContext;
import com.bakdata.conquery.sql.conversion.model.select.SelectConverter;
import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java
index 2e9e851d56..df20b8a43f 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java
@@ -26,8 +26,8 @@
import com.bakdata.conquery.sql.conversion.model.filter.SumCondition;
import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses;
import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects;
-import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.ExtractingSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.model.select.SelectContext;
import com.bakdata.conquery.sql.conversion.model.select.SelectConverter;
import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/AbstractSelectFilterConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/AbstractSelectFilterConverter.java
index c43ae52ae9..ceae7497cf 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/AbstractSelectFilterConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/AbstractSelectFilterConverter.java
@@ -5,7 +5,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext;
import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects;
-import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.ExtractingSqlSelect;
import org.jooq.Condition;
import org.jooq.Field;
import org.jooq.impl.DSL;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/NumberFilterConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/NumberFilterConverter.java
index f4e24084a2..5de7b72ab3 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/NumberFilterConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/NumberFilterConverter.java
@@ -7,7 +7,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext;
import com.bakdata.conquery.sql.conversion.model.NumberMapUtil;
import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects;
-import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.ExtractingSqlSelect;
import org.jooq.Condition;
import org.jooq.Field;
import org.jooq.impl.DSL;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptColumnSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptColumnSelectConverter.java
index ff06d0116e..9c305f2e21 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptColumnSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptColumnSelectConverter.java
@@ -8,6 +8,8 @@
import com.bakdata.conquery.models.datasets.concepts.Connector;
import com.bakdata.conquery.models.datasets.concepts.select.concept.ConceptColumnSelect;
import com.bakdata.conquery.models.datasets.concepts.tree.TreeConcept;
+import com.bakdata.conquery.sql.compiler.ir.select.ExtractingSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptIdMapping;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptSqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DateUnionSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DateUnionSelectConverter.java
index a03f853483..31f0c94bdb 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DateUnionSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DateUnionSelectConverter.java
@@ -1,6 +1,7 @@
package com.bakdata.conquery.sql.conversion.model.select;
import com.bakdata.conquery.models.datasets.concepts.select.connector.specific.DateUnionSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
public class DateUnionSelectConverter implements SelectConverter {
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
index 8afe907ba9..fc6f9be593 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
@@ -17,6 +17,8 @@
import com.bakdata.conquery.models.datasets.concepts.DaterangeSelectOrFilter;
import com.bakdata.conquery.models.datasets.concepts.select.Select;
import com.bakdata.conquery.models.identifiable.LabeledNamespaceIdentifiable;
+import com.bakdata.conquery.sql.compiler.ir.select.ExtractingSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.bakdata.conquery.sql.conversion.Context;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java
index 6219fe229e..f77223db97 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java
@@ -8,6 +8,7 @@
import com.bakdata.conquery.models.datasets.concepts.select.connector.DistinctSelect;
import com.bakdata.conquery.models.datasets.concepts.select.connector.specific.MappableSingleColumnSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDateUnionSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDateUnionSelectConverter.java
index 73cd607ae0..c356537fcb 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDateUnionSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDateUnionSelectConverter.java
@@ -1,6 +1,8 @@
package com.bakdata.conquery.sql.conversion.model.select;
import com.bakdata.conquery.models.datasets.concepts.select.concept.specific.EventDateUnionSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.ExtractingSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptSqlTables;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDurationSumSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDurationSumSelectConverter.java
index 7a9b21307b..0c60624d4e 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDurationSumSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDurationSumSelectConverter.java
@@ -5,6 +5,8 @@
import java.time.temporal.ChronoUnit;
import com.bakdata.conquery.models.datasets.concepts.select.concept.specific.EventDurationSumSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.ExtractingSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptSqlTables;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSelectConverter.java
index f79444f66e..f4c0fbecd7 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSelectConverter.java
@@ -1,6 +1,7 @@
package com.bakdata.conquery.sql.conversion.model.select;
import com.bakdata.conquery.models.datasets.concepts.select.concept.specific.ExistsSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.ExistsSqlSelect;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptSqlTables;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExtractingSqlSelect.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExtractingSqlSelect.java
deleted file mode 100644
index 5968c71d41..0000000000
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExtractingSqlSelect.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.bakdata.conquery.sql.conversion.model.select;
-
-import java.util.List;
-
-import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
-import lombok.Value;
-import org.jooq.Field;
-import org.jooq.impl.DSL;
-
-/**
- * Select that does nothing but reference an existing column.
- *
- * This can be used if another select requires a column in a later step.
- *
- * @param type of column
- */
-@Value
-public class ExtractingSqlSelect implements SingleColumnSqlSelect {
-
- String table;
- String column;
- Class columnClass;
-
- @Override
- public Field select() {
- return DSL.field(DSL.name(table, column), columnClass);
- }
-
- @Override
- public Field aliased() {
- return DSL.field(DSL.name(column), columnClass);
- }
-
- @Override
- public List requiredColumns() {
- return List.of(column);
- }
-
- @Override
- public ExtractingSqlSelect qualify(String qualifier) {
- Field aliased = aliased();
- return new ExtractingSqlSelect<>(
- qualifier,
- aliased.getName(),
- aliased.getType()
- );
- }
-
-}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/FieldWrapper.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/FieldWrapper.java
deleted file mode 100644
index 99f243c0f7..0000000000
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/FieldWrapper.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.bakdata.conquery.sql.conversion.model.select;
-
-import java.util.List;
-
-import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
-import lombok.EqualsAndHashCode;
-import org.jooq.Field;
-import org.jooq.impl.DSL;
-
-/**
- * Wrapper for a {@link Field}.
- */
-@EqualsAndHashCode
-public class FieldWrapper implements SingleColumnSqlSelect {
-
- private final Field field;
- @EqualsAndHashCode.Exclude
- private final List requiredColumns;
-
- /**
- * @param field The field to wrap, e.g. {@code DSL.sum(DSL.field(DSL.name("foo", "bar"))).as("foo_bar")};
- * @param requiredColumns All columns this {@link FieldWrapper} requires in the previous CTE/table to be present.
- */
- public FieldWrapper(Field field, String... requiredColumns) {
- this.field = field;
- this.requiredColumns = List.of(requiredColumns);
- }
-
- /**
- * @param field @param field The field to wrap, e.g. {@code DSL.field(DSL.name("fizz", "buzz"))).as("fizz_buzz")}; The column for
- * {@link FieldWrapper#requiredColumns()} will be taken from the alias of the wrapped field.
- */
- public FieldWrapper(Field field) {
- this.field = field;
- this.requiredColumns = List.of(field.getName());
- }
-
- @Override
- public Field select() {
- return field;
- }
-
- @Override
- public Field aliased() {
- return DSL.field(DSL.name(field.getName()), field.getType());
- }
-
- @Override
- public List requiredColumns() {
- return this.requiredColumns;
- }
-
- @Override
- public ExtractingSqlSelect qualify(String qualifier) {
- Field aliased = aliased();
- return new ExtractingSqlSelect<>(
- qualifier,
- aliased.getName(),
- aliased.getType()
- );
- }
-
-}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/RandomValueSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/RandomValueSelectConverter.java
index 100c74327d..0356879936 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/RandomValueSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/RandomValueSelectConverter.java
@@ -1,6 +1,8 @@
package com.bakdata.conquery.sql.conversion.model.select;
import com.bakdata.conquery.models.datasets.concepts.select.connector.RandomValueSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.ExtractingSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java
index 3d3405ffbd..8bcea25e6d 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java
@@ -9,6 +9,7 @@
import com.bakdata.conquery.models.common.Range;
import com.bakdata.conquery.models.datasets.Column;
import com.bakdata.conquery.models.datasets.concepts.select.connector.specific.MappableSingleColumnSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java
index 6c5c65674c..7171395693 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/ConceptQueryConverter.java
@@ -9,7 +9,7 @@
import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.model.*;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.RequiredArgsConstructor;
import org.jooq.*;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/FormConversionHelper.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/FormConversionHelper.java
index 75b32f027b..16ffecf616 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/FormConversionHelper.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/FormConversionHelper.java
@@ -12,7 +12,7 @@
import com.bakdata.conquery.sql.conversion.forms.FormCteStep;
import com.bakdata.conquery.sql.conversion.forms.FormType;
import com.bakdata.conquery.sql.conversion.model.*;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.google.common.base.Preconditions;
import lombok.RequiredArgsConstructor;
import org.jooq.*;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java
index 7c0c643e45..a6c764c706 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java
@@ -13,7 +13,7 @@
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.forms.FormCteStep;
import com.bakdata.conquery.sql.conversion.model.*;
-import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.util.TablePrimaryColumnUtil;
import com.google.common.base.Preconditions;
import lombok.RequiredArgsConstructor;
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/package-info.java
index 33e6fa2b60..416eb14035 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/package-info.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/package-info.java
@@ -1,2 +1,7 @@
-/** Internal representations produced and transformed while compiling a resolved query. */
+/**
+ * Internal representations produced and transformed while compiling a resolved query.
+ *
+ * Types in this package and its subpackages are implementation details of {@code sql-connector}, not integration
+ * contracts for backends or plugins. Backend references may exist temporarily while the legacy compiler is migrated.
+ */
package com.bakdata.conquery.sql.compiler.ir;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSqlSelect.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/ExistsSqlSelect.java
similarity index 63%
rename from backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSqlSelect.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/ExistsSqlSelect.java
index 1f95b87568..a09b60e79a 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSqlSelect.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/ExistsSqlSelect.java
@@ -1,20 +1,18 @@
-package com.bakdata.conquery.sql.conversion.model.select;
+package com.bakdata.conquery.sql.compiler.ir.select;
-import com.bakdata.conquery.sql.compiler.dialect.CompilerDialect;
-import com.bakdata.conquery.sql.compiler.ir.select.SingleColumnSqlSelect;
-import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
-import lombok.AccessLevel;
-import lombok.RequiredArgsConstructor;
-import org.jooq.Field;
-import org.jooq.Name;
+import static org.jooq.impl.DSL.coalesce;
+import static org.jooq.impl.DSL.field;
+import static org.jooq.impl.DSL.inline;
+import static org.jooq.impl.DSL.max;
+import static org.jooq.impl.DSL.name;
-import java.util.Collections;
import java.util.List;
-import static org.jooq.impl.DSL.*;
-import static org.jooq.impl.DSL.field;
+import com.bakdata.conquery.sql.compiler.dialect.CompilerDialect;
+import org.jooq.Field;
+import org.jooq.Name;
-@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+/** A single-column marker select indicating whether a row exists. */
public class ExistsSqlSelect implements SingleColumnSqlSelect {
private static final Field EXISTS = inline(1);
@@ -22,13 +20,18 @@ public class ExistsSqlSelect implements SingleColumnSqlSelect {
private final Field exists;
private final Name alias;
- public static ExistsSqlSelect withAlias(final String alias) {
+ private ExistsSqlSelect(Field exists, Name alias) {
+ this.exists = exists;
+ this.alias = alias;
+ }
+
+ public static ExistsSqlSelect withAlias(String alias) {
return new ExistsSqlSelect(EXISTS.as(alias), name(alias));
}
@Override
public List requiredColumns() {
- return Collections.emptyList();
+ return List.of();
}
@Override
@@ -47,8 +50,8 @@ public Field aliased() {
}
@Override
- public SingleColumnSqlSelect qualify(final String qualifier) {
- final Field qualified = field(name(name(qualifier), alias), exists.getType());
+ public SingleColumnSqlSelect qualify(String qualifier) {
+ Field qualified = field(name(name(qualifier), alias), exists.getType());
return new ExistsSqlSelect(qualified, alias);
}
@@ -64,7 +67,7 @@ public SqlSelect toFinalRepresentation() {
@Override
public List> aggregateForFinalQuery(CompilerDialect dialect) {
- // We have to coalesce at the end of the query because full-outer-joins will create null values, which we want to avoid.
+ // Full outer joins can introduce null values, which an existence marker represents as zero.
Field coalesced = coalesce(max(select()), inline(0));
return List.of(coalesced.as(alias));
}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/ExtractingSqlSelect.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/ExtractingSqlSelect.java
new file mode 100644
index 0000000000..15719254e1
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/ExtractingSqlSelect.java
@@ -0,0 +1,31 @@
+package com.bakdata.conquery.sql.compiler.ir.select;
+
+import java.util.List;
+
+import org.jooq.Field;
+import org.jooq.impl.DSL;
+
+/** A single-column select that references an existing column. */
+public record ExtractingSqlSelect(String table, String column, Class columnClass) implements SingleColumnSqlSelect {
+
+ @Override
+ public Field select() {
+ return DSL.field(DSL.name(table, column), columnClass);
+ }
+
+ @Override
+ public Field aliased() {
+ return DSL.field(DSL.name(column), columnClass);
+ }
+
+ @Override
+ public List requiredColumns() {
+ return List.of(column);
+ }
+
+ @Override
+ public ExtractingSqlSelect qualify(String qualifier) {
+ Field aliased = aliased();
+ return new ExtractingSqlSelect<>(qualifier, aliased.getName(), aliased.getType());
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/FieldWrapper.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/FieldWrapper.java
new file mode 100644
index 0000000000..c7cc871f02
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/FieldWrapper.java
@@ -0,0 +1,66 @@
+package com.bakdata.conquery.sql.compiler.ir.select;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.jooq.Field;
+import org.jooq.impl.DSL;
+
+/** A single-column select backed by an arbitrary jOOQ field expression. */
+public class FieldWrapper implements SingleColumnSqlSelect {
+
+ private final Field field;
+ private final List requiredColumns;
+
+ /**
+ * @param field field to wrap
+ * @param requiredColumns columns that must be present in the preceding table or CTE
+ */
+ public FieldWrapper(Field field, String... requiredColumns) {
+ this.field = field;
+ this.requiredColumns = List.of(requiredColumns);
+ }
+
+ /** Create a wrapper whose required column is derived from the field alias. */
+ public FieldWrapper(Field field) {
+ this.field = field;
+ this.requiredColumns = List.of(field.getName());
+ }
+
+ @Override
+ public Field select() {
+ return field;
+ }
+
+ @Override
+ public Field aliased() {
+ return DSL.field(DSL.name(field.getName()), field.getType());
+ }
+
+ @Override
+ public List requiredColumns() {
+ return requiredColumns;
+ }
+
+ @Override
+ public ExtractingSqlSelect qualify(String qualifier) {
+ Field aliased = aliased();
+ return new ExtractingSqlSelect<>(qualifier, aliased.getName(), aliased.getType());
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (!(object instanceof FieldWrapper> that)) {
+ return false;
+ }
+ return Objects.equals(field, that.field);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(field);
+ }
+}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/package-info.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/package-info.java
index 65d930f995..b61bb2c072 100644
--- a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/package-info.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/package-info.java
@@ -1,2 +1,2 @@
-/** SQL select expressions used by the compiler intermediate representation. */
+/** Internal SQL select expressions used while compiling a resolved query. */
package com.bakdata.conquery.sql.compiler.ir.select;
diff --git a/sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/ir/select/SqlSelectImplementationsTest.java b/sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/ir/select/SqlSelectImplementationsTest.java
new file mode 100644
index 0000000000..0aba547349
--- /dev/null
+++ b/sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/ir/select/SqlSelectImplementationsTest.java
@@ -0,0 +1,55 @@
+package com.bakdata.conquery.sql.compiler.ir.select;
+
+import static org.jooq.impl.DSL.field;
+import static org.jooq.impl.DSL.name;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.List;
+
+import org.jooq.Field;
+import org.junit.jupiter.api.Test;
+
+class SqlSelectImplementationsTest {
+
+ @Test
+ void shouldDeriveRequiredColumnFromFieldAlias() {
+ Field field = field(name("source"), Integer.class).as("result");
+
+ FieldWrapper select = new FieldWrapper<>(field);
+
+ assertEquals(List.of("result"), select.requiredColumns());
+ assertEquals("result", select.aliased().getName());
+ }
+
+ @Test
+ void shouldKeepFieldWrapperIdentityIndependentOfRequiredColumns() {
+ Field field = field(name("source"), Integer.class).as("result");
+
+ assertEquals(new FieldWrapper<>(field, "first"), new FieldWrapper<>(field, "second"));
+ }
+
+ @Test
+ void shouldQualifyExtractingSelects() {
+ ExtractingSqlSelect select = new ExtractingSqlSelect<>("source", "result", Integer.class);
+
+ ExtractingSqlSelect qualified = select.qualify("next_step");
+
+ assertEquals("next_step", qualified.table());
+ assertEquals("result", qualified.column());
+ assertEquals(Integer.class, qualified.columnClass());
+ assertEquals(name("next_step", "result"), qualified.select().getQualifiedName());
+ }
+
+ @Test
+ void shouldRepresentExistenceAsUniversalSelect() {
+ ExistsSqlSelect select = ExistsSqlSelect.withAlias("present");
+
+ assertTrue(select.isUniversal());
+ assertEquals(List.of(), select.requiredColumns());
+ assertEquals("present", select.select().getName());
+ assertEquals(name("next_step", "present"), select.qualify("next_step").select().getQualifiedName());
+ assertEquals("present", select.connectorAggregate().toFields().getFirst().getName());
+ assertEquals("present", select.toFinalRepresentation().toFields().getFirst().getName());
+ }
+}
From 2abae33bef7075ae4370820f13c1df08430006ee Mon Sep 17 00:00:00 2001
From: MT <12283268+thoniTUB@users.noreply.github.com>
Date: Tue, 1 Sep 2026 13:26:07 +0200
Subject: [PATCH 12/81] refactor(sql): move date range IR into connector
Move the two-column date range representation and field qualification utility
into the connector-owned compiler IR. Replace Lombok-generated accessors with
plain Java and cover qualification, aliasing, and coalescing behavior with tests.
Co-authored-by: Codex GPT-5.6-sol
---
.../cqelement/CQExternalConverter.java | 2 +-
.../conversion/cqelement/CQYesConverter.java | 2 +-
.../aggregation/AnsiSqlDateAggregator.java | 2 +-
.../aggregation/DateAggregationContext.java | 2 +-
.../aggregation/DateAggregationDates.java | 2 +-
.../IntersectAggregationAction.java | 2 +-
.../cqelement/aggregation/InvertCte.java | 4 +-
.../aggregation/MergeAggregateAction.java | 2 +-
.../aggregation/NodeNoOverlapCte.java | 2 +-
.../cqelement/aggregation/OverlapCte.java | 2 +-
.../cqelement/aggregation/RowNumberCte.java | 2 +-
.../aggregation/SqlAggregationAction.java | 2 +-
.../concept/AggregationSelectCte.java | 2 +-
.../cqelement/concept/CQConceptConverter.java | 3 +-
.../cqelement/concept/CQTableContext.java | 2 +-
.../concept/IntervalPackingSelectsCte.java | 2 +-
.../cqelement/concept/JoinBranchesCte.java | 2 +-
.../cqelement/concept/PreprocessingCte.java | 2 +-
.../AnsiSqlIntervalPacker.java | 4 +-
.../IntervalPackingContext.java | 2 +-
.../IntervalPackingResult.java | 2 +-
.../PostgreSqlIntervalPacker.java | 2 +-
.../conversion/dialect/SqlDateAggregator.java | 2 +-
.../dialect/SqlFunctionProvider.java | 2 +-
.../ClickhouseFunctionProvider.java | 2 +-
.../ClickhouseStratificationFunctions.java | 2 +-
.../dialect/hana/HanaSqlFunctionProvider.java | 2 +-
.../hana/HanaStratificationFunctions.java | 2 +-
.../forms/AbsoluteStratification.java | 2 +-
.../forms/RelativeStratification.java | 2 +-
.../forms/StratificationFunctions.java | 2 +-
.../sql/conversion/model/ColumnDateRange.java | 99 -------------------
.../sql/conversion/model/QueryStepJoiner.java | 3 +-
.../sql/conversion/model/Selects.java | 3 +-
.../sql/conversion/model/SqlIdColumns.java | 1 +
.../model/StratificationSqlIdColumns.java | 1 +
.../aggregator/DateDistanceSqlAggregator.java | 2 +-
.../aggregator/DurationSumSqlAggregator.java | 2 +-
.../filter/DateRestrictionCondition.java | 2 +-
.../model/select/DaterangeSelectUtil.java | 2 +-
.../model/select/DistinctSelectConverter.java | 2 +-
.../select/EventDateUnionSelectConverter.java | 2 +-
.../EventDurationSumSelectConverter.java | 2 +-
.../model/select/SelectContext.java | 2 +-
.../model/select/ValueSelectUtil.java | 2 +-
.../query/AbsoluteFormQueryConverter.java | 2 +-
.../query/EntityDateQueryConverter.java | 2 +-
.../query/FormConversionHelper.java | 3 +-
.../query/TableExportQueryConverter.java | 3 +-
.../sql/compiler/ir}/QualifyingUtil.java | 13 ++-
.../compiler/ir/select/ColumnDateRange.java | 98 ++++++++++++++++++
.../sql/compiler/ir/QualifyingUtilTest.java | 31 ++++++
.../ir/select/ColumnDateRangeTest.java | 45 +++++++++
53 files changed, 237 insertions(+), 152 deletions(-)
delete mode 100644 backend/src/main/java/com/bakdata/conquery/sql/conversion/model/ColumnDateRange.java
rename {backend/src/main/java/com/bakdata/conquery/sql/conversion/model => sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir}/QualifyingUtil.java (60%)
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/ColumnDateRange.java
create mode 100644 sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/ir/QualifyingUtilTest.java
create mode 100644 sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/ir/select/ColumnDateRangeTest.java
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
index 05f47c7df0..2355bd13bd 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
@@ -14,7 +14,7 @@
import com.bakdata.conquery.sql.conversion.NodeConverter;
import com.bakdata.conquery.sql.conversion.SharedAliases;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQYesConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQYesConverter.java
index f45cba7d1b..c5e36e3ee7 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQYesConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQYesConverter.java
@@ -7,7 +7,7 @@
import com.bakdata.conquery.apiv1.query.CQYes;
import com.bakdata.conquery.models.config.ColumnConfig;
import com.bakdata.conquery.sql.conversion.NodeConverter;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/AnsiSqlDateAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/AnsiSqlDateAggregator.java
index 98581b0e2c..6ff056e800 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/AnsiSqlDateAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/AnsiSqlDateAggregator.java
@@ -13,7 +13,7 @@
import com.bakdata.conquery.sql.conversion.dialect.IntervalPacker;
import com.bakdata.conquery.sql.conversion.dialect.SqlDateAggregator;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java
index 3add526456..3182e22641 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java
@@ -9,7 +9,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.compiler.ir.Qualifiable;
-import com.bakdata.conquery.sql.conversion.model.QualifyingUtil;
+import com.bakdata.conquery.sql.compiler.ir.QualifyingUtil;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationDates.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationDates.java
index 3b71007745..a53f27be61 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationDates.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationDates.java
@@ -7,7 +7,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntersectAggregationAction.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntersectAggregationAction.java
index f54bee36cb..1b0ff7c716 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntersectAggregationAction.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/IntersectAggregationAction.java
@@ -7,7 +7,7 @@
import java.util.stream.Stream;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.NameGenerator;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/InvertCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/InvertCte.java
index abd998364e..c62ad2596c 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/InvertCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/InvertCte.java
@@ -9,8 +9,8 @@
import java.util.stream.Stream;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
-import com.bakdata.conquery.sql.conversion.model.QualifyingUtil;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.QualifyingUtil;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/MergeAggregateAction.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/MergeAggregateAction.java
index 8a22291e27..67acb1d5e3 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/MergeAggregateAction.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/MergeAggregateAction.java
@@ -7,7 +7,7 @@
import java.util.stream.Stream;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.NameGenerator;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/NodeNoOverlapCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/NodeNoOverlapCte.java
index 0da7709148..97a8e052b2 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/NodeNoOverlapCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/NodeNoOverlapCte.java
@@ -5,7 +5,7 @@
import java.util.List;
import java.util.Optional;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/OverlapCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/OverlapCte.java
index 1d6790ea23..9ce61b8f23 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/OverlapCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/OverlapCte.java
@@ -5,7 +5,7 @@
import java.util.Optional;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import lombok.Getter;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java
index e159bafe0e..e945ae7214 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java
@@ -3,7 +3,7 @@
import java.util.ArrayList;
import java.util.Optional;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/SqlAggregationAction.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/SqlAggregationAction.java
index 2825aeb2f4..a9f7c4f172 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/SqlAggregationAction.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/SqlAggregationAction.java
@@ -4,7 +4,7 @@
import com.bakdata.conquery.models.query.queryplan.DateAggregationAction;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.NameGenerator;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationSelectCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationSelectCte.java
index 5d89475328..645939926b 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationSelectCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationSelectCte.java
@@ -4,7 +4,7 @@
import java.util.Optional;
import java.util.stream.Stream;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java
index 302b2d09d0..7765cce0a7 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java
@@ -19,6 +19,8 @@
import com.bakdata.conquery.models.identifiable.ids.specific.ConceptElementId;
import com.bakdata.conquery.models.identifiable.ids.specific.SelectId;
import com.bakdata.conquery.models.query.queryplan.DateAggregationAction;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.bakdata.conquery.sql.conversion.NodeConverter;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
@@ -30,7 +32,6 @@
import com.bakdata.conquery.sql.conversion.model.select.ConceptSqlSelects;
import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects;
import com.bakdata.conquery.sql.conversion.model.select.SelectContext;
-import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.bakdata.conquery.util.TablePrimaryColumnUtil;
import com.google.common.base.Preconditions;
import org.jooq.Condition;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java
index 71199d2e0c..18801d2d48 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java
@@ -9,7 +9,7 @@
import com.bakdata.conquery.sql.conversion.Context;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java
index 8f2d508069..107c286c31 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java
@@ -8,7 +8,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java
index 886826cb4b..e740eb70bd 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java
@@ -6,7 +6,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.intervalpacking.IntervalPackingContext;
import com.bakdata.conquery.sql.conversion.dialect.IntervalPacker;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.ConqueryJoinType;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.QueryStepJoiner;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/PreprocessingCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/PreprocessingCte.java
index 5434578289..c61d03465b 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/PreprocessingCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/PreprocessingCte.java
@@ -6,7 +6,7 @@
import com.bakdata.conquery.models.datasets.concepts.Connector;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java
index 81230a0d05..3664de8ec5 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java
@@ -7,8 +7,8 @@
import java.util.Optional;
import com.bakdata.conquery.sql.conversion.dialect.IntervalPacker;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
-import com.bakdata.conquery.sql.conversion.model.QualifyingUtil;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.QualifyingUtil;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java
index 6fad39405d..c0ae679ea1 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java
@@ -8,7 +8,7 @@
import com.bakdata.conquery.sql.conversion.Context;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingResult.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingResult.java
index c8e71a3166..68134c0dc7 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingResult.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingResult.java
@@ -1,6 +1,6 @@
package com.bakdata.conquery.sql.conversion.cqelement.intervalpacking;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import lombok.Value;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/PostgreSqlIntervalPacker.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/PostgreSqlIntervalPacker.java
index 8fae49bdb8..d7de1cc0b0 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/PostgreSqlIntervalPacker.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/PostgreSqlIntervalPacker.java
@@ -4,7 +4,7 @@
import com.bakdata.conquery.sql.conversion.dialect.IntervalPacker;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlDateAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlDateAggregator.java
index 225453ce14..07a4ab1334 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlDateAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlDateAggregator.java
@@ -5,7 +5,7 @@
import com.bakdata.conquery.models.query.queryplan.DateAggregationAction;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.cqelement.aggregation.DateAggregationDates;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlFunctionProvider.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlFunctionProvider.java
index 0a2b1552fd..980c7103fb 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlFunctionProvider.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlFunctionProvider.java
@@ -17,7 +17,7 @@
import com.bakdata.conquery.models.datasets.concepts.ValidityDate;
import com.bakdata.conquery.models.identifiable.ids.specific.ColumnId;
import com.bakdata.conquery.sql.conversion.SharedAliases;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.execution.ResultSetProcessor;
import org.jooq.Condition;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseFunctionProvider.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseFunctionProvider.java
index 0131145f01..d7dde0cb09 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseFunctionProvider.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseFunctionProvider.java
@@ -13,7 +13,7 @@
import com.bakdata.conquery.models.datasets.concepts.DaterangeSelectOrFilter;
import com.bakdata.conquery.models.datasets.concepts.ValidityDate;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import org.jetbrains.annotations.NotNull;
import org.jooq.Condition;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseStratificationFunctions.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseStratificationFunctions.java
index 3126dcae20..1066555a3f 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseStratificationFunctions.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/clickhouse/ClickhouseStratificationFunctions.java
@@ -13,7 +13,7 @@
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.forms.Offset;
import com.bakdata.conquery.sql.conversion.forms.StratificationFunctions;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/hana/HanaSqlFunctionProvider.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/hana/HanaSqlFunctionProvider.java
index cc2fdb4a7e..a885d8c478 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/hana/HanaSqlFunctionProvider.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/hana/HanaSqlFunctionProvider.java
@@ -16,7 +16,7 @@
import com.bakdata.conquery.models.datasets.concepts.DaterangeSelectOrFilter;
import com.bakdata.conquery.models.datasets.concepts.ValidityDate;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import org.jetbrains.annotations.NotNull;
import org.jooq.Condition;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/hana/HanaStratificationFunctions.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/hana/HanaStratificationFunctions.java
index 2bae80d371..cc82c62fee 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/hana/HanaStratificationFunctions.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/hana/HanaStratificationFunctions.java
@@ -9,7 +9,7 @@
import com.bakdata.conquery.sql.conversion.dialect.Interval;
import com.bakdata.conquery.sql.conversion.forms.Offset;
import com.bakdata.conquery.sql.conversion.forms.StratificationFunctions;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/AbsoluteStratification.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/AbsoluteStratification.java
index 4d4b8b7127..afdc10ab9f 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/AbsoluteStratification.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/AbsoluteStratification.java
@@ -11,7 +11,7 @@
import com.bakdata.conquery.models.forms.util.Resolution;
import com.bakdata.conquery.sql.conversion.SharedAliases;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java
index 3e75139cd9..4a64fe6d6c 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java
@@ -22,7 +22,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.Interval;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/StratificationFunctions.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/StratificationFunctions.java
index 1a2478b4f1..1cd9d390a1 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/StratificationFunctions.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/StratificationFunctions.java
@@ -29,7 +29,7 @@
import com.bakdata.conquery.sql.conversion.SharedAliases;
import com.bakdata.conquery.sql.conversion.dialect.Interval;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
import org.jooq.Condition;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/ColumnDateRange.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/ColumnDateRange.java
deleted file mode 100644
index f2bb27ad46..0000000000
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/ColumnDateRange.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package com.bakdata.conquery.sql.conversion.model;
-
-import java.sql.Date;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
-import lombok.Getter;
-import lombok.ToString;
-import org.jooq.Condition;
-import org.jooq.Field;
-import org.jooq.impl.DSL;
-
-//TODO split this class up into Dialect specific versions.
-@Getter
-@ToString(onlyExplicitlyIncluded = true)
-public class ColumnDateRange implements SqlSelect {
-
- private static final String VALIDITY_DATE_COLUMN_NAME_SUFFIX = "_validity_date";
- private static final String START_SUFFIX = "_start";
- private static final String END_SUFFIX = "_end";
-
- @ToString.Include
- private final Field start;
- @ToString.Include
- private final Field end;
- private final String alias;
-
- protected ColumnDateRange(Field startColumn, Field endColumn, String alias) {
- start = startColumn;
- end = endColumn;
- this.alias = alias;
- }
-
- public static ColumnDateRange of(Field startColumn, Field endColumn) {
- return new ColumnDateRange(startColumn, endColumn, "");
- }
-
- public static ColumnDateRange of(Field startColumn, Field endColumn, String alias) {
- return new ColumnDateRange(startColumn, endColumn, alias);
- }
-
-
- public ColumnDateRange asValidityDateRange(String alias) {
- return as(alias + VALIDITY_DATE_COLUMN_NAME_SUFFIX);
- }
-
- @Override
- public List> toFields() {
- return Stream.of(start, end)
- .collect(Collectors.toList());
- }
-
- @Override
- public ColumnDateRange qualify(String qualifier) {
- return new ColumnDateRange(
- QualifyingUtil.qualify(getStart(), qualifier),
- QualifyingUtil.qualify(getEnd(), qualifier),
- getAlias()
- );
- }
-
- @Override
- public List requiredColumns() {
- return toFields().stream()
- .map(Field::getName)
- .distinct()
- .toList();
- }
-
- public ColumnDateRange as(String alias) {
- return new ColumnDateRange(
- start.as(alias + START_SUFFIX),
- end.as(alias + END_SUFFIX),
- alias
- );
- }
-
- public ColumnDateRange coalesce(ColumnDateRange right) {
- return ColumnDateRange.of(
- DSL.coalesce(start, right.getStart()),
- DSL.coalesce(end, right.getEnd())
- ).as(alias);
- }
-
- public Condition join(ColumnDateRange right) {
- return start.eq(right.getStart()).and(end.eq(right.getEnd()));
- }
-
- public Condition isNotNull() {
- return start.isNotNull().and(end.isNotNull());
- }
-
- public static Condition isNotEmpty(ColumnDateRange columnDateRange) {
- return columnDateRange.getStart().isNotNull().and(columnDateRange.getEnd().isNotNull());
- }
-
-}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
index bbde527966..104f736772 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
@@ -13,12 +13,13 @@
import com.bakdata.conquery.models.config.ColumnConfig;
import com.bakdata.conquery.models.config.IdColumnConfig;
import com.bakdata.conquery.models.query.queryplan.DateAggregationAction;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.bakdata.conquery.sql.conversion.SharedAliases;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.cqelement.aggregation.DateAggregationDates;
import com.bakdata.conquery.sql.conversion.dialect.SqlDateAggregator;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.google.common.base.Preconditions;
import org.jooq.Condition;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java
index 22bce87cdb..f83f8399c2 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java
@@ -1,8 +1,9 @@
package com.bakdata.conquery.sql.conversion.model;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.bakdata.conquery.sql.conversion.SharedAliases;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Builder;
import lombok.Singular;
import lombok.Value;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/SqlIdColumns.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/SqlIdColumns.java
index 3b1b195393..8810aa9be3 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/SqlIdColumns.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/SqlIdColumns.java
@@ -11,6 +11,7 @@
import com.bakdata.conquery.models.forms.util.Resolution;
import com.bakdata.conquery.sql.compiler.ir.Qualifiable;
+import com.bakdata.conquery.sql.compiler.ir.QualifyingUtil;
import com.bakdata.conquery.sql.conversion.SharedAliases;
import lombok.AllArgsConstructor;
import lombok.Getter;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/StratificationSqlIdColumns.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/StratificationSqlIdColumns.java
index 678afefab5..7bf3611b56 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/StratificationSqlIdColumns.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/StratificationSqlIdColumns.java
@@ -10,6 +10,7 @@
import javax.annotation.Nullable;
import com.bakdata.conquery.models.forms.util.Resolution;
+import com.bakdata.conquery.sql.compiler.ir.QualifyingUtil;
import com.bakdata.conquery.sql.conversion.SharedAliases;
import com.google.common.base.Preconditions;
import lombok.Getter;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DateDistanceSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DateDistanceSqlAggregator.java
index ac66d083ed..3c8518d87e 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DateDistanceSqlAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DateDistanceSqlAggregator.java
@@ -12,7 +12,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
import com.bakdata.conquery.sql.conversion.model.filter.*;
import com.bakdata.conquery.sql.conversion.model.select.*;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DurationSumSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DurationSumSqlAggregator.java
index 93f9c41151..77cf07f83c 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DurationSumSqlAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DurationSumSqlAggregator.java
@@ -13,7 +13,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.filter.DateDistanceCondition;
import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter;
import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/DateRestrictionCondition.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/DateRestrictionCondition.java
index 4a03e0dc94..387bd24608 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/DateRestrictionCondition.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/DateRestrictionCondition.java
@@ -1,7 +1,7 @@
package com.bakdata.conquery.sql.conversion.model.filter;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import lombok.RequiredArgsConstructor;
import org.jooq.Condition;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
index fc6f9be593..e4d08205f8 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
@@ -29,7 +29,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.intervalpacking.IntervalPackingCteStep;
import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.CteStep;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java
index f77223db97..84e45f7488 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java
@@ -13,7 +13,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.CteStep;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDateUnionSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDateUnionSelectConverter.java
index c356537fcb..0163c6d94b 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDateUnionSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDateUnionSelectConverter.java
@@ -7,7 +7,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptSqlTables;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.google.common.base.Preconditions;
public class EventDateUnionSelectConverter implements SelectConverter {
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDurationSumSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDurationSumSelectConverter.java
index 0c60624d4e..34be5bac11 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDurationSumSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDurationSumSelectConverter.java
@@ -11,7 +11,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptSqlTables;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.google.common.base.Preconditions;
import org.jooq.Condition;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java
index 446bc81397..973e441411 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java
@@ -8,7 +8,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
import lombok.AccessLevel;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java
index 8bcea25e6d..62f963243c 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java
@@ -15,7 +15,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.CteStep;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/AbsoluteFormQueryConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/AbsoluteFormQueryConverter.java
index 7afb49e42a..6cfaf36f83 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/AbsoluteFormQueryConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/AbsoluteFormQueryConverter.java
@@ -12,7 +12,7 @@
import com.bakdata.conquery.sql.conversion.forms.FormCteStep;
import com.bakdata.conquery.sql.conversion.forms.FormType;
import com.bakdata.conquery.sql.conversion.forms.StratificationTableFactory;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/EntityDateQueryConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/EntityDateQueryConverter.java
index 71bb59a32c..ed8590d683 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/EntityDateQueryConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/EntityDateQueryConverter.java
@@ -11,7 +11,7 @@
import com.bakdata.conquery.sql.conversion.forms.FormCteStep;
import com.bakdata.conquery.sql.conversion.forms.FormType;
import com.bakdata.conquery.sql.conversion.forms.StratificationTableFactory;
-import com.bakdata.conquery.sql.conversion.model.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
import com.google.common.base.Preconditions;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/FormConversionHelper.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/FormConversionHelper.java
index 16ffecf616..3899f2466f 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/FormConversionHelper.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/FormConversionHelper.java
@@ -6,13 +6,14 @@
import com.bakdata.conquery.apiv1.query.Query;
import com.bakdata.conquery.models.query.queryplan.DateAggregationAction;
import com.bakdata.conquery.models.query.resultinfo.ResultInfo;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.SharedAliases;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.forms.FormCteStep;
import com.bakdata.conquery.sql.conversion.forms.FormType;
import com.bakdata.conquery.sql.conversion.model.*;
-import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.google.common.base.Preconditions;
import lombok.RequiredArgsConstructor;
import org.jooq.*;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java
index a6c764c706..9765000136 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java
@@ -7,13 +7,14 @@
import com.bakdata.conquery.models.common.daterange.CDateRange;
import com.bakdata.conquery.models.datasets.Column;
import com.bakdata.conquery.models.identifiable.ids.specific.ColumnId;
+import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
+import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.NodeConverter;
import com.bakdata.conquery.sql.conversion.SharedAliases;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.forms.FormCteStep;
import com.bakdata.conquery.sql.conversion.model.*;
-import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.util.TablePrimaryColumnUtil;
import com.google.common.base.Preconditions;
import lombok.RequiredArgsConstructor;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QualifyingUtil.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/QualifyingUtil.java
similarity index 60%
rename from backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QualifyingUtil.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/QualifyingUtil.java
index 599c81faf6..6c593d3253 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QualifyingUtil.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/QualifyingUtil.java
@@ -1,4 +1,4 @@
-package com.bakdata.conquery.sql.conversion.model;
+package com.bakdata.conquery.sql.compiler.ir;
import java.util.List;
import java.util.stream.Collectors;
@@ -7,7 +7,11 @@
import org.jooq.Field;
import org.jooq.impl.DSL;
-public class QualifyingUtil {
+/** Utilities for rebinding compiler IR fields and selects to a table or CTE qualifier. */
+public final class QualifyingUtil {
+
+ private QualifyingUtil() {
+ }
public static Field qualify(Field field, String qualifier) {
return DSL.field(DSL.name(qualifier, field.getName()), field.getType());
@@ -15,8 +19,7 @@ public static Field qualify(Field field, String qualifier) {
public static List qualify(List sqlSelects, String qualifier) {
return sqlSelects.stream()
- .map(sqlSelect -> sqlSelect.qualify(qualifier))
- .collect(Collectors.toList());
+ .map(sqlSelect -> sqlSelect.qualify(qualifier))
+ .collect(Collectors.toList());
}
-
}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/ColumnDateRange.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/ColumnDateRange.java
new file mode 100644
index 0000000000..977b4508f3
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/select/ColumnDateRange.java
@@ -0,0 +1,98 @@
+package com.bakdata.conquery.sql.compiler.ir.select;
+
+import java.sql.Date;
+import java.util.List;
+
+import com.bakdata.conquery.sql.compiler.ir.QualifyingUtil;
+import org.jooq.Condition;
+import org.jooq.Field;
+import org.jooq.impl.DSL;
+
+/** A two-column date-range representation used by compiler transformations. */
+public final class ColumnDateRange implements SqlSelect {
+
+ private static final String VALIDITY_DATE_COLUMN_NAME_SUFFIX = "_validity_date";
+ private static final String START_SUFFIX = "_start";
+ private static final String END_SUFFIX = "_end";
+
+ private final Field start;
+ private final Field end;
+ private final String alias;
+
+ private ColumnDateRange(Field start, Field end, String alias) {
+ this.start = start;
+ this.end = end;
+ this.alias = alias;
+ }
+
+ public static ColumnDateRange of(Field start, Field end) {
+ return new ColumnDateRange(start, end, "");
+ }
+
+ public static ColumnDateRange of(Field start, Field end, String alias) {
+ return new ColumnDateRange(start, end, alias);
+ }
+
+ public Field getStart() {
+ return start;
+ }
+
+ public Field getEnd() {
+ return end;
+ }
+
+ public String getAlias() {
+ return alias;
+ }
+
+ public ColumnDateRange asValidityDateRange(String alias) {
+ return as(alias + VALIDITY_DATE_COLUMN_NAME_SUFFIX);
+ }
+
+ @Override
+ public List> toFields() {
+ return List.of(start, end);
+ }
+
+ @Override
+ public ColumnDateRange qualify(String qualifier) {
+ return new ColumnDateRange(
+ QualifyingUtil.qualify(start, qualifier),
+ QualifyingUtil.qualify(end, qualifier),
+ alias
+ );
+ }
+
+ @Override
+ public List requiredColumns() {
+ return toFields().stream()
+ .map(Field::getName)
+ .distinct()
+ .toList();
+ }
+
+ public ColumnDateRange as(String alias) {
+ return new ColumnDateRange(start.as(alias + START_SUFFIX), end.as(alias + END_SUFFIX), alias);
+ }
+
+ public ColumnDateRange coalesce(ColumnDateRange right) {
+ return ColumnDateRange.of(DSL.coalesce(start, right.start), DSL.coalesce(end, right.end)).as(alias);
+ }
+
+ public Condition join(ColumnDateRange right) {
+ return start.eq(right.start).and(end.eq(right.end));
+ }
+
+ public Condition isNotNull() {
+ return start.isNotNull().and(end.isNotNull());
+ }
+
+ public static Condition isNotEmpty(ColumnDateRange columnDateRange) {
+ return columnDateRange.start.isNotNull().and(columnDateRange.end.isNotNull());
+ }
+
+ @Override
+ public String toString() {
+ return "ColumnDateRange(start=%s, end=%s)".formatted(start, end);
+ }
+}
diff --git a/sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/ir/QualifyingUtilTest.java b/sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/ir/QualifyingUtilTest.java
new file mode 100644
index 0000000000..02e8f5484e
--- /dev/null
+++ b/sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/ir/QualifyingUtilTest.java
@@ -0,0 +1,31 @@
+package com.bakdata.conquery.sql.compiler.ir;
+
+import static org.jooq.impl.DSL.field;
+import static org.jooq.impl.DSL.name;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.List;
+
+import com.bakdata.conquery.sql.compiler.ir.select.ExtractingSqlSelect;
+import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
+import org.junit.jupiter.api.Test;
+
+class QualifyingUtilTest {
+
+ @Test
+ void shouldQualifyFieldsWithoutChangingTheirType() {
+ var qualified = QualifyingUtil.qualify(field(name("result"), Integer.class), "query_step");
+
+ assertEquals(name("query_step", "result"), qualified.getQualifiedName());
+ assertEquals(Integer.class, qualified.getType());
+ }
+
+ @Test
+ void shouldQualifySelectLists() {
+ SqlSelect select = new ExtractingSqlSelect<>("source", "result", Integer.class);
+
+ List qualified = QualifyingUtil.qualify(List.of(select), "query_step");
+
+ assertEquals(name("query_step", "result"), qualified.getFirst().toFields().getFirst().getQualifiedName());
+ }
+}
diff --git a/sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/ir/select/ColumnDateRangeTest.java b/sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/ir/select/ColumnDateRangeTest.java
new file mode 100644
index 0000000000..7248bff712
--- /dev/null
+++ b/sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/ir/select/ColumnDateRangeTest.java
@@ -0,0 +1,45 @@
+package com.bakdata.conquery.sql.compiler.ir.select;
+
+import static org.jooq.impl.DSL.field;
+import static org.jooq.impl.DSL.name;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.sql.Date;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+class ColumnDateRangeTest {
+
+ @Test
+ void shouldAliasValidityDateColumns() {
+ ColumnDateRange range = range().asValidityDateRange("event");
+
+ assertEquals("event_validity_date", range.getAlias());
+ assertEquals("event_validity_date_start", range.getStart().getName());
+ assertEquals("event_validity_date_end", range.getEnd().getName());
+ assertEquals(List.of("event_validity_date_start", "event_validity_date_end"), range.requiredColumns());
+ }
+
+ @Test
+ void shouldQualifyAliasedDateRangeColumns() {
+ ColumnDateRange qualified = range().as("event").qualify("query_step");
+
+ assertEquals(name("query_step", "event_start"), qualified.getStart().getQualifiedName());
+ assertEquals(name("query_step", "event_end"), qualified.getEnd().getQualifiedName());
+ assertEquals("event", qualified.getAlias());
+ }
+
+ @Test
+ void shouldPreserveLeftAliasWhenCoalescing() {
+ ColumnDateRange coalesced = range().as("event").coalesce(range().as("fallback"));
+
+ assertEquals("event", coalesced.getAlias());
+ assertEquals("event_start", coalesced.getStart().getName());
+ assertEquals("event_end", coalesced.getEnd().getName());
+ }
+
+ private static ColumnDateRange range() {
+ return ColumnDateRange.of(field(name("start"), Date.class), field(name("end"), Date.class));
+ }
+}
From 04aea92c593a2907ba7dccbcb526a9e73bb0d235 Mon Sep 17 00:00:00 2001
From: MT <12283268+thoniTUB@users.noreply.github.com>
Date: Tue, 1 Sep 2026 13:59:12 +0200
Subject: [PATCH 13/81] refactor(sql): move ID column IR into connector
Move shared compiler aliases and plain and stratified ID-column representations
into the connector-owned IR. Normalize backend resolution enums before crossing
the boundary and cover aliasing, qualification, and stratification with tests.
Co-authored-by: Codex GPT-5.6-sol
---
.../query/concept/filter/FilterValue.java | 2 +-
.../mode/local/SqlEntityResolver.java | 2 +-
.../cqelement/CQExternalConverter.java | 4 +-
.../conversion/cqelement/CQYesConverter.java | 2 +-
.../aggregation/DateAggregationContext.java | 2 +-
.../cqelement/aggregation/InvertCte.java | 2 +-
.../cqelement/aggregation/RowNumberCte.java | 2 +-
.../concept/AggregationSelectCte.java | 2 +-
.../cqelement/concept/CQConceptConverter.java | 1 +
.../cqelement/concept/CQTableContext.java | 2 +-
.../cqelement/concept/FilterContext.java | 2 +-
.../cqelement/concept/JoinBranchesCte.java | 2 +-
.../cqelement/concept/PreprocessingCte.java | 2 +-
.../AnsiSqlIntervalPacker.java | 2 +-
.../IntervalPackingContext.java | 2 +-
.../PostgreSqlIntervalPacker.java | 2 +-
.../dialect/SqlFunctionProvider.java | 2 +-
.../forms/AbsoluteStratification.java | 8 +-
.../sql/conversion/forms/FormConstants.java | 2 +-
.../forms/RelativeStratification.java | 10 +-
.../forms/StratificationFunctions.java | 4 +-
.../sql/conversion/model/QueryStepJoiner.java | 3 +-
.../sql/conversion/model/Selects.java | 3 +-
.../sql/conversion/model/SqlIdColumns.java | 165 -----------------
.../model/StratificationSqlIdColumns.java | 167 ------------------
.../model/aggregator/SumSqlAggregator.java | 2 +-
.../select/ConceptColumnSelectConverter.java | 2 +-
.../model/select/DaterangeSelectUtil.java | 2 +-
.../model/select/DistinctSelectConverter.java | 2 +-
.../model/select/SelectContext.java | 2 +-
.../model/select/ValueSelectUtil.java | 2 +-
.../query/AbsoluteFormQueryConverter.java | 4 +-
.../query/EntityDateQueryConverter.java | 2 +-
.../query/FormConversionHelper.java | 6 +-
.../query/TableExportQueryConverter.java | 3 +-
.../sql/compiler/ir}/SharedAliases.java | 18 +-
.../sql/compiler/ir/SqlIdColumns.java | 137 ++++++++++++++
.../ir/StratificationSqlIdColumns.java | 113 ++++++++++++
.../sql/compiler/ir/SqlIdColumnsTest.java | 71 ++++++++
39 files changed, 380 insertions(+), 383 deletions(-)
delete mode 100644 backend/src/main/java/com/bakdata/conquery/sql/conversion/model/SqlIdColumns.java
delete mode 100644 backend/src/main/java/com/bakdata/conquery/sql/conversion/model/StratificationSqlIdColumns.java
rename {backend/src/main/java/com/bakdata/conquery/sql/conversion => sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir}/SharedAliases.java (76%)
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/SqlIdColumns.java
create mode 100644 sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/StratificationSqlIdColumns.java
create mode 100644 sql-connector/src/test/java/com/bakdata/conquery/sql/compiler/ir/SqlIdColumnsTest.java
diff --git a/backend/src/main/java/com/bakdata/conquery/apiv1/query/concept/filter/FilterValue.java b/backend/src/main/java/com/bakdata/conquery/apiv1/query/concept/filter/FilterValue.java
index 8e597eb813..2e8ecfe384 100644
--- a/backend/src/main/java/com/bakdata/conquery/apiv1/query/concept/filter/FilterValue.java
+++ b/backend/src/main/java/com/bakdata/conquery/apiv1/query/concept/filter/FilterValue.java
@@ -18,7 +18,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables;
import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters;
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
diff --git a/backend/src/main/java/com/bakdata/conquery/mode/local/SqlEntityResolver.java b/backend/src/main/java/com/bakdata/conquery/mode/local/SqlEntityResolver.java
index 8768f24c98..92b5aafbcc 100644
--- a/backend/src/main/java/com/bakdata/conquery/mode/local/SqlEntityResolver.java
+++ b/backend/src/main/java/com/bakdata/conquery/mode/local/SqlEntityResolver.java
@@ -17,7 +17,7 @@
import com.bakdata.conquery.models.config.IdColumnConfig;
import com.bakdata.conquery.models.identifiable.mapping.EntityIdMap;
import com.bakdata.conquery.models.identifiable.mapping.ExternalId;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
+import com.bakdata.conquery.sql.compiler.ir.SharedAliases;
import com.bakdata.conquery.sql.conversion.dialect.DialectBundle;
import com.bakdata.conquery.sql.execution.SqlExecutionService;
import com.bakdata.conquery.util.DateReader;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
index 2355bd13bd..853ca74f6b 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQExternalConverter.java
@@ -12,12 +12,12 @@
import com.bakdata.conquery.apiv1.query.concept.specific.external.CQExternal;
import com.bakdata.conquery.models.common.CDateSet;
import com.bakdata.conquery.sql.conversion.NodeConverter;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
+import com.bakdata.conquery.sql.compiler.ir.SharedAliases;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.google.common.base.Preconditions;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQYesConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQYesConverter.java
index c5e36e3ee7..0a59e4a3ca 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQYesConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/CQYesConverter.java
@@ -10,7 +10,7 @@
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import org.jooq.Field;
import org.jooq.Record;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java
index 3182e22641..f6c462841d 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/DateAggregationContext.java
@@ -11,7 +11,7 @@
import com.bakdata.conquery.sql.compiler.ir.Qualifiable;
import com.bakdata.conquery.sql.compiler.ir.QualifyingUtil;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Builder;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/InvertCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/InvertCte.java
index c62ad2596c..49a8483c27 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/InvertCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/InvertCte.java
@@ -13,7 +13,7 @@
import com.bakdata.conquery.sql.compiler.ir.QualifyingUtil;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import lombok.Getter;
import org.jooq.Condition;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java
index e945ae7214..55bd70ec1e 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/aggregation/RowNumberCte.java
@@ -6,7 +6,7 @@
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Getter;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationSelectCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationSelectCte.java
index 645939926b..39c1a878b0 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationSelectCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/AggregationSelectCte.java
@@ -7,7 +7,7 @@
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import org.jooq.Field;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java
index 7765cce0a7..ae79cb7394 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java
@@ -19,6 +19,7 @@
import com.bakdata.conquery.models.identifiable.ids.specific.ConceptElementId;
import com.bakdata.conquery.models.identifiable.ids.specific.SelectId;
import com.bakdata.conquery.models.query.queryplan.DateAggregationAction;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import com.bakdata.conquery.sql.conversion.NodeConverter;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java
index 18801d2d48..22408801b3 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java
@@ -11,7 +11,7 @@
import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters;
import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects;
import lombok.Builder;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/FilterContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/FilterContext.java
index ac0a1eeb5d..9108d93013 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/FilterContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/FilterContext.java
@@ -7,7 +7,7 @@
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Value;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java
index e740eb70bd..dbda348b52 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java
@@ -11,7 +11,7 @@
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.QueryStepJoiner;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.aggregator.SumSqlAggregator;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import org.jooq.Record;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/PreprocessingCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/PreprocessingCte.java
index c61d03465b..4570747992 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/PreprocessingCte.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/PreprocessingCte.java
@@ -9,7 +9,7 @@
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters;
import com.bakdata.conquery.sql.conversion.model.filter.WhereCondition;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java
index 3664de8ec5..fa3403e26b 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/AnsiSqlIntervalPacker.java
@@ -11,7 +11,7 @@
import com.bakdata.conquery.sql.compiler.ir.QualifyingUtil;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.RequiredArgsConstructor;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java
index c0ae679ea1..a869876b32 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/IntervalPackingContext.java
@@ -10,7 +10,7 @@
import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
import lombok.Builder;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/PostgreSqlIntervalPacker.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/PostgreSqlIntervalPacker.java
index d7de1cc0b0..d7135cbf72 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/PostgreSqlIntervalPacker.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/intervalpacking/PostgreSqlIntervalPacker.java
@@ -7,7 +7,7 @@
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import lombok.RequiredArgsConstructor;
/**
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlFunctionProvider.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlFunctionProvider.java
index 980c7103fb..d6a0871129 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlFunctionProvider.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/dialect/SqlFunctionProvider.java
@@ -16,7 +16,7 @@
import com.bakdata.conquery.models.datasets.concepts.DaterangeSelectOrFilter;
import com.bakdata.conquery.models.datasets.concepts.ValidityDate;
import com.bakdata.conquery.models.identifiable.ids.specific.ColumnId;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
+import com.bakdata.conquery.sql.compiler.ir.SharedAliases;
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.execution.ResultSetProcessor;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/AbsoluteStratification.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/AbsoluteStratification.java
index afdc10ab9f..5e3df52c20 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/AbsoluteStratification.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/AbsoluteStratification.java
@@ -9,12 +9,12 @@
import com.bakdata.conquery.apiv1.forms.export_form.ExportForm;
import com.bakdata.conquery.models.forms.util.Resolution;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
+import com.bakdata.conquery.sql.compiler.ir.SharedAliases;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.google.common.base.Preconditions;
import lombok.RequiredArgsConstructor;
@@ -118,7 +118,7 @@ private QueryStep createCompleteTable() {
// complete range shall have a null index because it spans the complete range, but we set it to 1 to ensure we can join tables on index,
// because a condition involving null in a join (e.g., null = some_value or null = null) always evaluates to false
Field index = field(inline(1)).as(SharedAliases.INDEX.getAlias());
- SqlIdColumns ids = baseStepSelects.getIds().withAbsoluteStratification(Resolution.COMPLETE, index);
+ SqlIdColumns ids = baseStepSelects.getIds().withStratification(Resolution.COMPLETE.name(), index);
ColumnDateRange completeRange = baseStepSelects.getStratificationDate().get();
@@ -146,7 +146,7 @@ private QueryStep createIntervalTable(QueryStep indexStartStep, ExportForm.Resol
);
Field index = stratificationFunctions.index(countsCteSelects.getIds(), countsCte.getQualifiedSelects().getStratificationDate());
- SqlIdColumns ids = countsCteSelects.getIds().withAbsoluteStratification(resolutionAndAlignment.getResolution(), index);
+ SqlIdColumns ids = countsCteSelects.getIds().withStratification(resolutionAndAlignment.getResolution().name(), index);
Selects selects = Selects.builder()
.ids(ids)
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/FormConstants.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/FormConstants.java
index e51fe3e87f..38984a7cb3 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/FormConstants.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/FormConstants.java
@@ -5,7 +5,7 @@
import com.bakdata.conquery.apiv1.query.TemporalSamplerFactory;
import com.bakdata.conquery.models.forms.managed.AbsoluteFormQuery;
import com.bakdata.conquery.models.forms.managed.EntityDateQuery;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
+import com.bakdata.conquery.sql.compiler.ir.SharedAliases;
import org.jooq.Field;
import org.jooq.impl.DSL;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java
index 4a64fe6d6c..cbe0cc53bb 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/RelativeStratification.java
@@ -18,14 +18,14 @@
import com.bakdata.conquery.models.forms.managed.RelativeFormQuery;
import com.bakdata.conquery.models.forms.util.CalendarUnit;
import com.bakdata.conquery.models.forms.util.Resolution;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
+import com.bakdata.conquery.sql.compiler.ir.SharedAliases;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.Interval;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.google.common.base.Preconditions;
import lombok.RequiredArgsConstructor;
@@ -169,14 +169,14 @@ private QueryStep createCompleteTable(QueryStep totalBoundsStep, RelativeFormQue
private QueryStep createCompleteFeatureTable(Selects predecessorSelects, Interval interval, Range intRange, QueryStep totalBoundsStep) {
Field featureIndex = DSL.field(DSL.inline(-1)).as(SharedAliases.INDEX.getAlias());
- SqlIdColumns featureIds = predecessorSelects.getIds().withRelativeStratification(Resolution.COMPLETE, featureIndex, INDEX_SELECTOR);
+ SqlIdColumns featureIds = predecessorSelects.getIds().withStratification(Resolution.COMPLETE.name(), featureIndex, INDEX_SELECTOR);
Field rangeStart = stratificationFunctions.shiftByInterval(INDEX_START_NEGATIVE, interval, DSL.inline(intRange.getMin()), Offset.NONE);
return createIntervalStep(featureIds, rangeStart, INDEX_START_NEGATIVE, Optional.empty(), totalBoundsStep);
}
private QueryStep createCompleteOutcomeTable(Selects predecessorSelects, Interval interval, Range intRange, QueryStep totalBoundsStep) {
Field outcomeIndex = DSL.field(DSL.inline(1)).as(SharedAliases.INDEX.getAlias());
- SqlIdColumns outcomeIds = predecessorSelects.getIds().withRelativeStratification(Resolution.COMPLETE, outcomeIndex, INDEX_SELECTOR);
+ SqlIdColumns outcomeIds = predecessorSelects.getIds().withStratification(Resolution.COMPLETE.name(), outcomeIndex, INDEX_SELECTOR);
Field rangeEnd = stratificationFunctions.shiftByInterval(INDEX_START_POSITIVE, interval, DSL.inline(intRange.getMax()), Offset.NONE);
return createIntervalStep(outcomeIds, INDEX_START_POSITIVE, rangeEnd, Optional.empty(), totalBoundsStep);
}
@@ -185,7 +185,7 @@ private QueryStep createIntervalTable(QueryStep totalBoundsStep, Resolution reso
Field seriesIndex = stratificationFunctions.intSeriesField();
Selects predecessorSelects = totalBoundsStep.getQualifiedSelects();
- SqlIdColumns ids = predecessorSelects.getIds().withRelativeStratification(resolution, seriesIndex, INDEX_SELECTOR);
+ SqlIdColumns ids = predecessorSelects.getIds().withStratification(resolution.name(), seriesIndex, INDEX_SELECTOR);
Interval interval = getInterval(form.getTimeUnit(), resolution);
Range bounds = toGenerateSeriesBounds(form, resolution);
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/StratificationFunctions.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/StratificationFunctions.java
index 1cd9d390a1..2b56a34f1e 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/StratificationFunctions.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/forms/StratificationFunctions.java
@@ -26,11 +26,11 @@
import com.bakdata.conquery.apiv1.forms.export_form.ExportForm;
import com.bakdata.conquery.apiv1.query.TemporalSamplerFactory;
import com.bakdata.conquery.models.forms.util.CalendarUnit;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
+import com.bakdata.conquery.sql.compiler.ir.SharedAliases;
import com.bakdata.conquery.sql.conversion.dialect.Interval;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import org.jooq.Condition;
import org.jooq.Field;
import org.jooq.Record;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
index 104f736772..c456a95b7d 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
@@ -13,9 +13,10 @@
import com.bakdata.conquery.models.config.ColumnConfig;
import com.bakdata.conquery.models.config.IdColumnConfig;
import com.bakdata.conquery.models.query.queryplan.DateAggregationAction;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
+import com.bakdata.conquery.sql.compiler.ir.SharedAliases;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.cqelement.aggregation.DateAggregationDates;
import com.bakdata.conquery.sql.conversion.dialect.SqlDateAggregator;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java
index f83f8399c2..dc072ad613 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/Selects.java
@@ -1,8 +1,9 @@
package com.bakdata.conquery.sql.conversion.model;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.compiler.ir.select.SqlSelect;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
+import com.bakdata.conquery.sql.compiler.ir.SharedAliases;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import lombok.Builder;
import lombok.Singular;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/SqlIdColumns.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/SqlIdColumns.java
deleted file mode 100644
index 8810aa9be3..0000000000
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/SqlIdColumns.java
+++ /dev/null
@@ -1,165 +0,0 @@
-package com.bakdata.conquery.sql.conversion.model;
-
-import java.sql.Date;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import javax.annotation.Nullable;
-
-import com.bakdata.conquery.models.forms.util.Resolution;
-import com.bakdata.conquery.sql.compiler.ir.Qualifiable;
-import com.bakdata.conquery.sql.compiler.ir.QualifyingUtil;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.ToString;
-import lombok.experimental.SuperBuilder;
-import org.jooq.Condition;
-import org.jooq.Field;
-import org.jooq.impl.DSL;
-
-@SuperBuilder
-@AllArgsConstructor
-@ToString
-public class SqlIdColumns implements Qualifiable {
-
- @Getter
- private final Field primaryColumn;
-
- @Nullable
- private final Field secondaryId;
-
- @Nullable
- @ToString.Exclude
- private final SqlIdColumns predecessor;
-
- public SqlIdColumns(Field primaryColumn, Field secondaryId) {
- this.primaryColumn = primaryColumn;
- this.secondaryId = secondaryId;
- this.predecessor = null;
- }
-
- public SqlIdColumns(Field primaryColumn) {
- this.primaryColumn = primaryColumn;
- this.secondaryId = null;
- this.predecessor = null;
- }
-
- public SqlIdColumns withAlias() {
- if (this.secondaryId == null) {
- return new SqlIdColumns(this.primaryColumn.as(SharedAliases.PRIMARY_COLUMN.getAlias()), null, this);
- }
- return new SqlIdColumns(
- this.primaryColumn.as(SharedAliases.PRIMARY_COLUMN.getAlias()),
- this.secondaryId.as(SharedAliases.SECONDARY_ID.getAlias()),
- this
- );
- }
-
- @Override
- public SqlIdColumns qualify(String qualifier) {
- Field primaryColumn = QualifyingUtil.qualify(this.primaryColumn, qualifier);
- if (secondaryId == null) {
- return new SqlIdColumns(primaryColumn, null, this);
- }
- Field secondaryId = QualifyingUtil.qualify(this.secondaryId, qualifier);
- return new SqlIdColumns(primaryColumn, secondaryId, this);
- }
-
- public SqlIdColumns withAbsoluteStratification(Resolution resolution, Field index) {
- Field resolutionField = DSL.inline(resolution.toString()).as(SharedAliases.RESOLUTION.getAlias());
- return StratificationSqlIdColumns.builder()
- .primaryColumn(this.primaryColumn)
- .secondaryId(this.secondaryId)
- .resolution(resolutionField)
- .index(index)
- .eventDate(null)
- .build();
- }
-
- public SqlIdColumns withRelativeStratification(Resolution resolution, Field index, Field eventDate) {
- Field resolutionField = DSL.inline(resolution.toString()).as(SharedAliases.RESOLUTION.getAlias());
- return StratificationSqlIdColumns.builder()
- .primaryColumn(this.primaryColumn)
- .secondaryId(this.secondaryId)
- .resolution(resolutionField)
- .index(index)
- .eventDate(eventDate)
- .build();
- }
-
- public SqlIdColumns forFinalSelect() {
- return this;
- }
-
- public Optional> getSecondaryId() {
- return Optional.ofNullable(this.secondaryId);
- }
-
- public Optional getPredecessor() {
- return Optional.ofNullable(this.predecessor);
- }
-
- public boolean isWithStratification() {
- return false;
- }
-
- public List> toFields() {
- if (getSecondaryId().isEmpty()){
- return List.of(getPrimaryColumn());
- }
-
- return List.of(getPrimaryColumn(), getSecondaryId().get());
- }
-
- public List join(SqlIdColumns rightIds) {
-
- // always join on primary columns
- Condition joinPrimariesCondition = primaryColumn.eq(rightIds.getPrimaryColumn());
-
- // join on secondary IDs if both are present
- Condition joinSecondaries = getSecondaryId()
- .flatMap(leftSecondaryId -> rightIds.getSecondaryId().map(leftSecondaryId::eq))
- .orElse(DSL.noCondition());
-
- return List.of(joinPrimariesCondition, joinSecondaries);
- }
-
- public SqlIdColumns coalesce(List selectsIds) {
-
- List> primaryColumns = new ArrayList<>();
- List> secondaryIds = new ArrayList<>();
-
- // add this ids
- primaryColumns.add(this.primaryColumn);
- getSecondaryId().ifPresent(secondaryIds::add);
-
- // add all other ids to coalesce with
- selectsIds.forEach(ids -> {
- primaryColumns.add(ids.getPrimaryColumn());
- ids.getSecondaryId().ifPresent(secondaryIds::add);
- });
-
- Field coalescedPrimaryColumn = coalesceFields(primaryColumns, String.class).coerce(String.class).as(SharedAliases.PRIMARY_COLUMN.getAlias());
- if (secondaryIds.isEmpty()) {
- return new SqlIdColumns(coalescedPrimaryColumn);
- }
- Field coalescedSecondaryIds = coalesceFields(secondaryIds, String.class).coerce(String.class).as(SharedAliases.SECONDARY_ID.getAlias());
- return new SqlIdColumns(coalescedPrimaryColumn, coalescedSecondaryIds);
- }
-
-
- protected static Field coalesceFields(List extends Field>> fields, Class type) {
- Field out = fields.getFirst().coerce(type);
-
- for (int index = 1; index < fields.size(); index++) {
- out = DSL.coalesce(out, fields.get(index).coerce(type));
- }
-
- return out;
- }
-
-}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/StratificationSqlIdColumns.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/StratificationSqlIdColumns.java
deleted file mode 100644
index 7bf3611b56..0000000000
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/StratificationSqlIdColumns.java
+++ /dev/null
@@ -1,167 +0,0 @@
-package com.bakdata.conquery.sql.conversion.model;
-
-import java.sql.Date;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import javax.annotation.Nullable;
-
-import com.bakdata.conquery.models.forms.util.Resolution;
-import com.bakdata.conquery.sql.compiler.ir.QualifyingUtil;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
-import com.google.common.base.Preconditions;
-import lombok.Getter;
-import lombok.experimental.SuperBuilder;
-import org.jooq.Condition;
-import org.jooq.Field;
-import org.jooq.impl.DSL;
-
-@Getter
-@SuperBuilder
-class StratificationSqlIdColumns extends SqlIdColumns {
-
- private final Field resolution;
-
- private final Field index;
-
- /**
- * Optional event date. Only set for relative forms.
- */
- @Nullable
- private final Field eventDate;
-
- @Override
- public SqlIdColumns qualify(String qualifier) {
-
- Field primaryColumn = QualifyingUtil.qualify(getPrimaryColumn(), qualifier);
- Field resolution = QualifyingUtil.qualify(this.resolution, qualifier);
- Field index = QualifyingUtil.qualify(this.index, qualifier);
- Field eventDate = null;
- if (this.eventDate != null) {
- eventDate = QualifyingUtil.qualify(this.eventDate, qualifier);
- }
-
- return StratificationSqlIdColumns.builder()
- .primaryColumn(primaryColumn)
- .secondaryId(null)
- .resolution(resolution)
- .index(index)
- .eventDate(eventDate)
- .build();
- }
-
- /**
- * Replaces the integer value of the {@link Resolution#COMPLETE} with null values.
- */
- @Override
- public SqlIdColumns forFinalSelect() {
-
- Field withNulledCompleteIndex = DSL.when(
- this.resolution.eq(DSL.inline(Resolution.COMPLETE.toString().toUpperCase())),
- DSL.inline(null, Integer.class)
- )
- .otherwise(this.index)
- .as(SharedAliases.INDEX.getAlias());
-
- return StratificationSqlIdColumns.builder()
- .primaryColumn(getPrimaryColumn())
- .secondaryId(null)
- .resolution(this.resolution)
- .index(withNulledCompleteIndex)
- .eventDate(this.eventDate)
- .build();
- }
-
- @Override
- public boolean isWithStratification() {
- return true;
- }
-
- @Override
- public List> toFields() {
- return Stream.of(
- getPrimaryColumn(),
- this.resolution,
- this.index,
- this.eventDate
- )
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
- }
-
- @Override
- public List join(SqlIdColumns rightIds) {
-
- if (!rightIds.isWithStratification()) {
- return super.join(rightIds);
- }
-
- StratificationSqlIdColumns rightStratificationIds = (StratificationSqlIdColumns) rightIds;
- Condition joinResolutionAndIndex = this.resolution.eq(rightStratificationIds.getResolution()).and(this.index.eq(rightStratificationIds.getIndex()));
-
- Condition joinEventDateCondition;
- if (this.eventDate != null) {
- joinEventDateCondition = this.eventDate.eq(rightStratificationIds.getEventDate());
- }
- else {
- joinEventDateCondition = DSL.noCondition();
- }
-
- return Stream.concat(
- super.join(rightIds).stream(),
- Stream.of(joinResolutionAndIndex, joinEventDateCondition)
- )
- .toList();
- }
-
- @Override
- public SqlIdColumns coalesce(List selectsIds) {
-
- Preconditions.checkArgument(
- selectsIds.stream().allMatch(SqlIdColumns::isWithStratification),
- "Can only coalesce SqlIdColumns if all are with stratification"
- );
-
- List> primaryColumns = new ArrayList<>();
- List> resolutions = new ArrayList<>();
- List> indices = new ArrayList<>();
- List> eventDates = new ArrayList<>();
-
- // add this ids
- primaryColumns.add(getPrimaryColumn());
- resolutions.add(this.resolution);
- indices.add(this.index);
- if (this.eventDate != null) {
- eventDates.add(this.eventDate);
- }
-
- for (SqlIdColumns ids : selectsIds) {
- StratificationSqlIdColumns stratificationIds = (StratificationSqlIdColumns) ids;
- primaryColumns.add(stratificationIds.getPrimaryColumn());
- resolutions.add(stratificationIds.getResolution());
- indices.add(stratificationIds.getIndex());
- if (stratificationIds.getEventDate() != null) {
- eventDates.add(stratificationIds.getEventDate());
- }
- }
-
- Field coalescedPrimaryColumn = coalesceFields(primaryColumns, String.class).coerce(String.class).as(SharedAliases.PRIMARY_COLUMN.getAlias());
- Field coalescedResolutions = coalesceFields(resolutions, String.class).as(SharedAliases.RESOLUTION.getAlias());
- Field coalescedIndices = coalesceFields(indices, Integer.class).as(SharedAliases.INDEX.getAlias());
- Field eventDate = null;
- if (!eventDates.isEmpty()) {
- eventDate = coalesceFields(eventDates, Date.class).as(SharedAliases.INDEX_SELECTOR.getAlias());
- }
-
- return StratificationSqlIdColumns.builder()
- .primaryColumn(coalescedPrimaryColumn)
- .secondaryId(null)
- .resolution(coalescedResolutions)
- .index(coalescedIndices)
- .eventDate(eventDate)
- .build();
- }
-}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java
index df20b8a43f..c455f1a636 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java
@@ -19,7 +19,7 @@
import com.bakdata.conquery.sql.conversion.model.NumberMapUtil;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter;
import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptColumnSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptColumnSelectConverter.java
index 9c305f2e21..18efd2be87 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptColumnSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptColumnSelectConverter.java
@@ -19,7 +19,7 @@
import com.bakdata.conquery.sql.conversion.model.NameGenerator;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.execution.ResultSetProcessor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
index e4d08205f8..54ab2b4e9c 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DaterangeSelectUtil.java
@@ -32,7 +32,7 @@
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.CteStep;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters;
import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java
index 84e45f7488..47b03b1684 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/DistinctSelectConverter.java
@@ -17,7 +17,7 @@
import com.bakdata.conquery.sql.conversion.model.CteStep;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.execution.ResultSetProcessor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java
index 973e441411..08e188f56d 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java
@@ -9,7 +9,7 @@
import com.bakdata.conquery.sql.conversion.dialect.LegacyCompilerDialect;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.conversion.model.SqlTables;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java
index 62f963243c..b6917dc491 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java
@@ -19,7 +19,7 @@
import com.bakdata.conquery.sql.conversion.model.CteStep;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/AbsoluteFormQueryConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/AbsoluteFormQueryConverter.java
index 6cfaf36f83..ce788a2c70 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/AbsoluteFormQueryConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/AbsoluteFormQueryConverter.java
@@ -7,7 +7,7 @@
import com.bakdata.conquery.models.common.daterange.CDateRange;
import com.bakdata.conquery.models.forms.managed.AbsoluteFormQuery;
import com.bakdata.conquery.sql.conversion.NodeConverter;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
+import com.bakdata.conquery.sql.compiler.ir.SharedAliases;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.forms.FormCteStep;
import com.bakdata.conquery.sql.conversion.forms.FormType;
@@ -15,7 +15,7 @@
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.conversion.model.QueryStep;
import com.bakdata.conquery.sql.conversion.model.Selects;
-import com.bakdata.conquery.sql.conversion.model.SqlIdColumns;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.google.common.base.Preconditions;
import lombok.RequiredArgsConstructor;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/EntityDateQueryConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/EntityDateQueryConverter.java
index ed8590d683..76535c7993 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/EntityDateQueryConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/EntityDateQueryConverter.java
@@ -5,7 +5,7 @@
import com.bakdata.conquery.models.forms.managed.EntityDateQuery;
import com.bakdata.conquery.sql.conversion.NodeConverter;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
+import com.bakdata.conquery.sql.compiler.ir.SharedAliases;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.forms.FormCteStep;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/FormConversionHelper.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/FormConversionHelper.java
index 3899f2466f..c992c300e6 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/FormConversionHelper.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/FormConversionHelper.java
@@ -4,11 +4,13 @@
import com.bakdata.conquery.apiv1.query.ArrayConceptQuery;
import com.bakdata.conquery.apiv1.query.ConceptQuery;
import com.bakdata.conquery.apiv1.query.Query;
+import com.bakdata.conquery.models.forms.util.Resolution;
import com.bakdata.conquery.models.query.queryplan.DateAggregationAction;
import com.bakdata.conquery.models.query.resultinfo.ResultInfo;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
+import com.bakdata.conquery.sql.compiler.ir.SharedAliases;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.forms.FormCteStep;
@@ -43,7 +45,7 @@ private static Selects getFinalSelects(
Selects preFinalSelects = convertedFeatures.getQualifiedSelects();
Selects stratificationSelects = stratificationTable.getQualifiedSelects();
- SqlIdColumns ids = stratificationSelects.getIds().forFinalSelect();
+ SqlIdColumns ids = stratificationSelects.getIds().forFinalSelect(Resolution.COMPLETE.name());
Selects.SelectsBuilder selects = Selects.builder()
.ids(ids)
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java
index 9765000136..9f40ac8799 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/query/TableExportQueryConverter.java
@@ -7,10 +7,11 @@
import com.bakdata.conquery.models.common.daterange.CDateRange;
import com.bakdata.conquery.models.datasets.Column;
import com.bakdata.conquery.models.identifiable.ids.specific.ColumnId;
+import com.bakdata.conquery.sql.compiler.ir.SqlIdColumns;
import com.bakdata.conquery.sql.compiler.ir.select.ColumnDateRange;
import com.bakdata.conquery.sql.compiler.ir.select.FieldWrapper;
import com.bakdata.conquery.sql.conversion.NodeConverter;
-import com.bakdata.conquery.sql.conversion.SharedAliases;
+import com.bakdata.conquery.sql.compiler.ir.SharedAliases;
import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider;
import com.bakdata.conquery.sql.conversion.forms.FormCteStep;
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/SharedAliases.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/SharedAliases.java
similarity index 76%
rename from backend/src/main/java/com/bakdata/conquery/sql/conversion/SharedAliases.java
rename to sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/SharedAliases.java
index 191d96ecae..ff1cf3167e 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/SharedAliases.java
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/SharedAliases.java
@@ -1,10 +1,6 @@
-package com.bakdata.conquery.sql.conversion;
+package com.bakdata.conquery.sql.compiler.ir;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-@Getter
-@RequiredArgsConstructor
+/** Shared SQL aliases used by compiler transformations and their generated output columns. */
public enum SharedAliases {
PRIMARY_COLUMN("primary_id"),
@@ -13,7 +9,6 @@ public enum SharedAliases {
NOP_TABLE("nop_table"),
- // form related
INDEX_START("index_start"),
QUARTER_START("quarter_start"),
QUARTER_END("quarter_end"),
@@ -32,8 +27,15 @@ public enum SharedAliases {
STRATIFICATION_BOUNDS("stratification_bounds"),
OBSERVATION_SCOPE("scope"),
- // full export form
SOURCE("source");
private final String alias;
+
+ SharedAliases(String alias) {
+ this.alias = alias;
+ }
+
+ public String getAlias() {
+ return alias;
+ }
}
diff --git a/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/SqlIdColumns.java b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/SqlIdColumns.java
new file mode 100644
index 0000000000..bfb122a4a2
--- /dev/null
+++ b/sql-connector/src/main/java/com/bakdata/conquery/sql/compiler/ir/SqlIdColumns.java
@@ -0,0 +1,137 @@
+package com.bakdata.conquery.sql.compiler.ir;
+
+import java.sql.Date;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import org.jooq.Condition;
+import org.jooq.Field;
+import org.jooq.impl.DSL;
+
+/** Compiler IR for the columns identifying an entity. */
+public class SqlIdColumns implements Qualifiable {
+
+ private final Field primaryColumn;
+ private final Field secondaryId;
+ private final SqlIdColumns predecessor;
+
+ public SqlIdColumns(Field