{
+
+ private final List extends Converter extends C, R>> converters;
+
+ protected ConverterService(List extends Converter extends C, R>> converters) {
+ this.converters = converters;
+ }
+
+ public R convert(C selectNode, ConversionContext context) {
+ return converters.stream()
+ .flatMap(converter -> converter.tryConvert(selectNode, context).stream())
+ .collect(MoreCollectors.onlyElement());
+ }
+
+}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConverter.java
new file mode 100644
index 0000000000..7eb6cb0c0c
--- /dev/null
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConverter.java
@@ -0,0 +1,17 @@
+package com.bakdata.conquery.sql.conversion;
+
+import com.bakdata.conquery.models.query.Visitable;
+import com.bakdata.conquery.sql.conversion.context.ConversionContext;
+
+/**
+ * Interface for converters that implement the translation of a ConQuery query to an SQL query.
+ *
+ *
+ * A ConQuery is a graph that has a {@link com.bakdata.conquery.apiv1.query.QueryDescription} as its root.
+ * The children of the root are of type {@link com.bakdata.conquery.apiv1.query.CQElement}.
+ *
+ * @param type of the node to convert
+ */
+public interface NodeConverter extends Converter {
+
+}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConverterService.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConverterService.java
new file mode 100644
index 0000000000..f174a48265
--- /dev/null
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/NodeConverterService.java
@@ -0,0 +1,32 @@
+package com.bakdata.conquery.sql.conversion;
+
+import com.bakdata.conquery.apiv1.query.QueryDescription;
+import com.bakdata.conquery.models.config.SqlConnectorConfig;
+import com.bakdata.conquery.models.query.Visitable;
+import com.bakdata.conquery.sql.conversion.context.ConversionContext;
+import com.bakdata.conquery.sql.conversion.dialect.SqlDialect;
+
+/**
+ * Entry point for converting {@link QueryDescription} to an SQL query.
+ */
+public class NodeConverterService extends ConverterService {
+
+ private final SqlDialect dialect;
+ private final SqlConnectorConfig config;
+
+ public NodeConverterService(SqlDialect dialect, SqlConnectorConfig config) {
+ super(dialect.getNodeConverters());
+ this.dialect = dialect;
+ this.config = config;
+ }
+
+ public ConversionContext convert(QueryDescription queryDescription) {
+ ConversionContext initialCtx = ConversionContext.builder()
+ .config(config)
+ .nodeConverterService(this)
+ .sqlDialect(this.dialect)
+ .build();
+ return convert(queryDescription, initialCtx);
+ }
+
+}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/SqlConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/SqlConverter.java
new file mode 100644
index 0000000000..8ee38b2f56
--- /dev/null
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/SqlConverter.java
@@ -0,0 +1,22 @@
+package com.bakdata.conquery.sql.conversion;
+
+import com.bakdata.conquery.apiv1.query.QueryDescription;
+import com.bakdata.conquery.models.config.SqlConnectorConfig;
+import com.bakdata.conquery.sql.SqlQuery;
+import com.bakdata.conquery.sql.conversion.context.ConversionContext;
+import com.bakdata.conquery.sql.conversion.dialect.SqlDialect;
+import org.jooq.conf.ParamType;
+
+public class SqlConverter {
+
+ private final NodeConverterService nodeConverterService;
+
+ public SqlConverter(SqlDialect dialect, SqlConnectorConfig config) {
+ this.nodeConverterService = new NodeConverterService(dialect, config);
+ }
+
+ public SqlQuery convert(QueryDescription queryDescription) {
+ ConversionContext converted = nodeConverterService.convert(queryDescription);
+ return new SqlQuery(converted.getFinalQuery().getSQL(ParamType.INLINED));
+ }
+}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/context/ConversionContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/context/ConversionContext.java
new file mode 100644
index 0000000000..b07383dacf
--- /dev/null
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/context/ConversionContext.java
@@ -0,0 +1,44 @@
+package com.bakdata.conquery.sql.conversion.context;
+
+import com.bakdata.conquery.models.common.daterange.CDateRange;
+import com.bakdata.conquery.models.config.SqlConnectorConfig;
+import com.bakdata.conquery.sql.conversion.NodeConverterService;
+import com.bakdata.conquery.sql.conversion.context.step.QueryStep;
+import com.bakdata.conquery.sql.conversion.dialect.SqlDialect;
+import lombok.Builder;
+import lombok.Singular;
+import lombok.Value;
+import lombok.With;
+import org.jooq.Record;
+import org.jooq.Select;
+
+import java.util.List;
+
+@Value
+@With
+@Builder(toBuilder = true)
+public class ConversionContext {
+
+ SqlConnectorConfig config;
+ NodeConverterService nodeConverterService;
+ SqlDialect sqlDialect;
+ @Singular
+ List querySteps;
+ Select finalQuery;
+ boolean negation;
+ CDateRange dateRestrictionRange;
+ int queryStepCounter;
+
+
+ public boolean dateRestrictionActive() {
+ return this.dateRestrictionRange != null;
+ }
+
+ public ConversionContext withQueryStep(QueryStep queryStep) {
+ return this.toBuilder()
+ .queryStep(queryStep)
+ .queryStepCounter(queryStepCounter + 1)
+ .build();
+ }
+
+}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/context/selects/ConceptSelects.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/context/selects/ConceptSelects.java
new file mode 100644
index 0000000000..8e42dde561
--- /dev/null
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/context/selects/ConceptSelects.java
@@ -0,0 +1,69 @@
+package com.bakdata.conquery.sql.conversion.context.selects;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import com.bakdata.conquery.apiv1.query.concept.specific.CQConcept;
+import lombok.Builder;
+import lombok.Value;
+import lombok.With;
+import org.jooq.Field;
+
+/**
+ * {@link ConceptSelects} represent all select fields of a {@link CQConcept}.
+ */
+@Value
+@With
+@Builder(toBuilder = true)
+public class ConceptSelects implements Selects {
+
+ Field