Repository: squashql/squashql
Pull request #319 (2024-04-26 11:09:06)

[Experimental] Support PostgreSQL


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 713d641..7e26f28 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -46,6 +46,24 @@ jobs:
         env:
           TESTCONTAINERS_REUSE_ENABLE: true
 
+  postgresql:
+    runs-on: ubuntu-latest
+    timeout-minutes: 10
+    steps:
+      - uses: actions/checkout@v4
+      - name: Set up JDK 17
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'temurin'
+          java-version: '17'
+          cache: 'maven'
+      - name: Build with Maven
+        run: mvn -pl :squashql-postgresql -am clean install --no-transfer-progress -DskipTests
+      - name: Run clickhouse tests
+        run: mvn -pl :squashql-postgresql --no-transfer-progress test
+        env:
+          TESTCONTAINERS_REUSE_ENABLE: true
+
   spark:
     runs-on: ubuntu-latest
     timeout-minutes: 10
diff --git a/bigquery/src/main/java/io/squashql/query/database/BigQueryEngine.java b/bigquery/src/main/java/io/squashql/query/database/BigQueryEngine.java
index 807d3aa..0229c04 100644
--- a/bigquery/src/main/java/io/squashql/query/database/BigQueryEngine.java
+++ b/bigquery/src/main/java/io/squashql/query/database/BigQueryEngine.java
@@ -124,7 +124,7 @@ public class BigQueryEngine extends AQueryEngine<BigQueryDatastore> {
   }
 
   @Override
-  public QueryRewriter queryRewriter(DatabaseQuery query) {
+  public QueryRewriter queryRewriter() {
     return new BigQueryQueryRewriter(this.datastore.getProjectId(), this.datastore.getDatasetName());
   }
 }
diff --git a/clickhouse/src/main/java/io/squashql/query/database/ClickHouseQueryEngine.java b/clickhouse/src/main/java/io/squashql/query/database/ClickHouseQueryEngine.java
index 391be1b..e924862 100644
--- a/clickhouse/src/main/java/io/squashql/query/database/ClickHouseQueryEngine.java
+++ b/clickhouse/src/main/java/io/squashql/query/database/ClickHouseQueryEngine.java
@@ -15,7 +15,6 @@ import io.squashql.query.Header;
 import io.squashql.table.ColumnarTable;
 import io.squashql.table.RowTable;
 import io.squashql.table.Table;
-import io.squashql.type.TypedField;
 import org.eclipse.collections.api.tuple.Pair;
 
 import java.util.HashSet;
@@ -125,32 +124,7 @@ public class ClickHouseQueryEngine extends AQueryEngine<ClickHouseDatastore> {
   }
 
   @Override
-  public QueryRewriter queryRewriter(DatabaseQuery query) {
+  public QueryRewriter queryRewriter() {
     return new ClickHouseQueryRewriter();
   }
-
-  static class ClickHouseQueryRewriter implements QueryRewriter {
-
-    @Override
-    public String fieldName(String field) {
-      return SqlUtils.backtickEscape(field);
-    }
-
-    @Override
-    public String escapeAlias(String alias) {
-      return SqlUtils.backtickEscape(alias);
-    }
-
-    @Override
-    public boolean usePartialRollupSyntax() {
-      // Not supported as of now: https://github.com/ClickHouse/ClickHouse/issues/322#issuecomment-615087004
-      // Tested with version https://github.com/ClickHouse/ClickHouse/tree/v22.10.2.11-stable
-      return false;
-    }
-
-    @Override
-    public String arrayContains(TypedField field, Object value) {
-      return "has(" + field.sqlExpression(this) + ", " + value + ")";
-    }
-  }
 }
diff --git a/clickhouse/src/main/java/io/squashql/query/database/ClickHouseQueryRewriter.java b/clickhouse/src/main/java/io/squashql/query/database/ClickHouseQueryRewriter.java
new file mode 100644
index 0000000..8706af8
--- /dev/null
+++ b/clickhouse/src/main/java/io/squashql/query/database/ClickHouseQueryRewriter.java
@@ -0,0 +1,28 @@
+package io.squashql.query.database;
+
+import io.squashql.type.TypedField;
+
+public class ClickHouseQueryRewriter implements QueryRewriter {
+
+  @Override
+  public String fieldName(String field) {
+    return SqlUtils.backtickEscape(field);
+  }
+
+  @Override
+  public String escapeAlias(String alias) {
+    return SqlUtils.backtickEscape(alias);
+  }
+
+  @Override
+  public boolean usePartialRollupSyntax() {
+    // Not supported as of now: https://github.com/ClickHouse/ClickHouse/issues/322#issuecomment-615087004
+    // Tested with version https://github.com/ClickHouse/ClickHouse/tree/v22.10.2.11-stable
+    return false;
+  }
+
+  @Override
+  public String arrayContains(TypedField field, Object value) {
+    return "has(" + field.sqlExpression(this) + ", " + value + ")";
+  }
+}
diff --git a/clickhouse/src/main/java/io/squashql/transaction/ClickHouseDataLoader.java b/clickhouse/src/main/java/io/squashql/transaction/ClickHouseDataLoader.java
index ac470c0..66eb3f0 100644
--- a/clickhouse/src/main/java/io/squashql/transaction/ClickHouseDataLoader.java
+++ b/clickhouse/src/main/java/io/squashql/transaction/ClickHouseDataLoader.java
@@ -46,7 +46,7 @@ public class ClickHouseDataLoader implements DataLoader {
 
   @Override
   public void load(String table, List<Object[]> tuples) {
-    String join = String.join(",", IntStream.range(0, tuples.get(0).length + 1).mapToObj(i -> "?").toList());
+    String join = String.join(",", IntStream.range(0, tuples.get(0).length).mapToObj(i -> "?").toList());
     String pattern = "insert into " + table + " values(" + join + ")";
     try (ClickHouseConnection conn = this.clickHouseDataSource.getConnection();
          PreparedStatement stmt = conn.prepareStatement(pattern)) {
diff --git a/core/src/main/java/io/squashql/jdbc/JdbcUtil.java b/core/src/main/java/io/squashql/jdbc/JdbcUtil.java
index 32b8b60..855d55f 100644
--- a/core/src/main/java/io/squashql/jdbc/JdbcUtil.java
+++ b/core/src/main/java/io/squashql/jdbc/JdbcUtil.java
@@ -81,6 +81,7 @@ public final class JdbcUtil {
       case Types.TIME -> LocalDateTime.class;
       case Types.TIMESTAMP -> Timestamp.class;
       case Types.ARRAY -> List.class;
+      case Types.NUMERIC -> Number.class;
       default -> Object.class;
     };
   }
diff --git a/core/src/main/java/io/squashql/query/MeasureUtils.java b/core/src/main/java/io/squashql/query/MeasureUtils.java
index 2f0a489..5e5ec41 100644
--- a/core/src/main/java/io/squashql/query/MeasureUtils.java
+++ b/core/src/main/java/io/squashql/query/MeasureUtils.java
@@ -17,7 +17,8 @@ import java.util.function.Function;
 @NoArgsConstructor
 public final class MeasureUtils {
 
-  public static final QueryRewriter BASIC = () -> false;
+  public static final QueryRewriter BASIC = new QueryRewriter() {
+  };
 
   public static String createExpression(Measure m) {
     if (m instanceof AggregatedMeasure a) {
diff --git a/core/src/main/java/io/squashql/query/compiled/CompiledCriteria.java b/core/src/main/java/io/squashql/query/compiled/CompiledCriteria.java
index 9867cf2..ab7a706 100644
--- a/core/src/main/java/io/squashql/query/compiled/CompiledCriteria.java
+++ b/core/src/main/java/io/squashql/query/compiled/CompiledCriteria.java
@@ -20,7 +20,14 @@ public record CompiledCriteria(ConditionDto condition, ConditionType conditionTy
     if (this.field != null && condition() != null) {
       return toSql(this.field, condition(), queryRewriter);
     } else if (this.measure != null && condition() != null) {
-      return toSql(new TableTypedField(null, this.measure.alias(), Number.class), condition(), queryRewriter);
+      if (queryRewriter.useAliasInHavingClause()) {
+        return toSql(new TableTypedField(null, this.measure.alias(), Number.class), condition(), queryRewriter);
+      } else {
+        // Trick to repeat the expression in the having clause
+        String expression = this.measure.sqlExpression(queryRewriter, false);
+        return toSql(new TableTypedField(null, expression, Number.class), condition(), new QueryRewriter() {
+        });
+      }
     } else if (this.field != null && this.fieldOther != null && conditionType() != null) {
       String left = this.field.sqlExpression(queryRewriter);
       String right = this.fieldOther.sqlExpression(queryRewriter);
diff --git a/core/src/main/java/io/squashql/query/compiled/CompiledDoubleConstantMeasure.java b/core/src/main/java/io/squashql/query/compiled/CompiledDoubleConstantMeasure.java
index 9959b95..d0ebc2c 100644
--- a/core/src/main/java/io/squashql/query/compiled/CompiledDoubleConstantMeasure.java
+++ b/core/src/main/java/io/squashql/query/compiled/CompiledDoubleConstantMeasure.java
@@ -2,7 +2,7 @@ package io.squashql.query.compiled;
 
 import io.squashql.query.database.QueryRewriter;
 
-public record CompiledDoubleConstantMeasure(Double value) implements CompiledMeasure {
+public record CompiledDoubleConstantMeasure(double value) implements CompiledMeasure {
 
   @Override
   public String sqlExpression(QueryRewriter queryRewriter, boolean withAlias) {
diff --git a/core/src/main/java/io/squashql/query/compiled/CompiledLongConstantMeasure.java b/core/src/main/java/io/squashql/query/compiled/CompiledLongConstantMeasure.java
index 9493962..07961b7 100644
--- a/core/src/main/java/io/squashql/query/compiled/CompiledLongConstantMeasure.java
+++ b/core/src/main/java/io/squashql/query/compiled/CompiledLongConstantMeasure.java
@@ -2,7 +2,7 @@ package io.squashql.query.compiled;
 
 import io.squashql.query.database.QueryRewriter;
 
-public record CompiledLongConstantMeasure(Long value) implements CompiledMeasure {
+public record CompiledLongConstantMeasure(long value) implements CompiledMeasure {
 
   @Override
   public String sqlExpression(QueryRewriter queryRewriter, boolean withAlias) {
diff --git a/core/src/main/java/io/squashql/query/database/AQueryEngine.java b/core/src/main/java/io/squashql/query/database/AQueryEngine.java
index 8af2513..8f8f223 100644
--- a/core/src/main/java/io/squashql/query/database/AQueryEngine.java
+++ b/core/src/main/java/io/squashql/query/database/AQueryEngine.java
@@ -39,7 +39,7 @@ public abstract class AQueryEngine<T extends Datastore> implements QueryEngine<T
   }
 
   protected String createSqlStatement(DatabaseQuery query) {
-    return SQLTranslator.translate(query, this.queryRewriter(query));
+    return SQLTranslator.translate(query, queryRewriter());
   }
 
   protected abstract Table retrieveAggregates(DatabaseQuery query, String sql);
diff --git a/core/src/main/java/io/squashql/query/database/DefaultQueryRewriter.java b/core/src/main/java/io/squashql/query/database/DefaultQueryRewriter.java
index 0117008..5265d94 100644
--- a/core/src/main/java/io/squashql/query/database/DefaultQueryRewriter.java
+++ b/core/src/main/java/io/squashql/query/database/DefaultQueryRewriter.java
@@ -2,12 +2,6 @@ package io.squashql.query.database;
 
 public class DefaultQueryRewriter implements QueryRewriter {
 
-  private final DatabaseQuery query;
-
-  public DefaultQueryRewriter(DatabaseQuery query) {
-    this.query = query;
-  }
-
   @Override
   public String fieldName(String field) {
     return SqlUtils.backtickEscape(field);
diff --git a/core/src/main/java/io/squashql/query/database/QueryEngine.java b/core/src/main/java/io/squashql/query/database/QueryEngine.java
index c1639dc..5b4802b 100644
--- a/core/src/main/java/io/squashql/query/database/QueryEngine.java
+++ b/core/src/main/java/io/squashql/query/database/QueryEngine.java
@@ -21,5 +21,5 @@ public interface QueryEngine<T extends Datastore> {
    */
   List<String> supportedAggregationFunctions();
 
-  QueryRewriter queryRewriter(DatabaseQuery query);
+  QueryRewriter queryRewriter();
 }
diff --git a/core/src/main/java/io/squashql/query/database/QueryRewriter.java b/core/src/main/java/io/squashql/query/database/QueryRewriter.java
index 59a0aaa..c6a4d6e 100644
--- a/core/src/main/java/io/squashql/query/database/QueryRewriter.java
+++ b/core/src/main/java/io/squashql/query/database/QueryRewriter.java
@@ -127,7 +127,13 @@ public interface QueryRewriter {
    * https://www.sqlservertutorial.net/sql-server-basics/sql-server-rollup/
    * </a>.
    */
-  boolean usePartialRollupSyntax();
+  default boolean usePartialRollupSyntax(){
+    return true;
+  }
+
+  default boolean useAliasInHavingClause() {
+    return true;
+  }
 
   default String escapeSingleQuote(String s) {
     return SqlUtils.escapeSingleQuote(s, "''");
diff --git a/core/src/main/java/io/squashql/query/database/SQLTranslator.java b/core/src/main/java/io/squashql/query/database/SQLTranslator.java
index d2e310b..1a52804 100644
--- a/core/src/main/java/io/squashql/query/database/SQLTranslator.java
+++ b/core/src/main/java/io/squashql/query/database/SQLTranslator.java
@@ -18,10 +18,6 @@ public class SQLTranslator {
 
   public static final String TOTAL_CELL = "___total___";
 
-  public static String translate(DatabaseQuery query) {
-    return translate(query, new DefaultQueryRewriter(query));
-  }
-
   public static String translate(DatabaseQuery dq, QueryRewriter queryRewriter) {
     QueryScope query = dq.scope();
     List<String> selects = new ArrayList<>();
diff --git a/core/src/main/java/io/squashql/query/join/ExperimentalQueryJoinExecutor.java b/core/src/main/java/io/squashql/query/join/ExperimentalQueryJoinExecutor.java
index 31c8c9b..18516cc 100644
--- a/core/src/main/java/io/squashql/query/join/ExperimentalQueryJoinExecutor.java
+++ b/core/src/main/java/io/squashql/query/join/ExperimentalQueryJoinExecutor.java
@@ -43,7 +43,7 @@ public class ExperimentalQueryJoinExecutor {
       this.cteTableName = cteTableName;
       this.queryResolver = new QueryResolver(query, ExperimentalQueryJoinExecutor.this.queryEngine.datastore().storeByName());
       this.dbQuery = new DatabaseQuery(this.queryResolver.getScope().copyWithNewLimit(-1), new ArrayList<>(this.queryResolver.getMeasures().values()));
-      this.queryRewriter = ExperimentalQueryJoinExecutor.this.queryEngine.queryRewriter(this.dbQuery);
+      this.queryRewriter = ExperimentalQueryJoinExecutor.this.queryEngine.queryRewriter();
       this.originalTableName = query.table != null ? query.table.name : null;
       this.sql = SQLTranslator.translate(this.dbQuery, this.queryRewriter);
     }
diff --git a/documentation/CONFIGURATION.md b/documentation/CONFIGURATION.md
index 13c7e6b..fdbdf83 100644
--- a/documentation/CONFIGURATION.md
+++ b/documentation/CONFIGURATION.md
@@ -9,7 +9,9 @@ Find a ready-to-use example with DuckDB and Spring Boot [here](https://github.co
 #### DuckDB
 
 Maven
+
 ```xml
+
 <dependency>
   <groupId>io.squashql</groupId>
   <artifactId>squashql-duckdb</artifactId>
@@ -18,6 +20,7 @@ Maven
 ```
 
 Java
+
 ```
 DuckDBDatastore ds = new DuckDBDatastore();
 DuckDBQueryEngine qe = new DuckDBQueryEngine(ds);
@@ -26,7 +29,9 @@ DuckDBQueryEngine qe = new DuckDBQueryEngine(ds);
 #### Apache Spark
 
 Maven
+
 ```xml
+
 <dependency>
   <groupId>io.squashql</groupId>
   <artifactId>squashql-spark</artifactId>
@@ -35,6 +40,7 @@ Maven
 ```
 
 Java
+
 ```
 SparkSession sparkSession = ...;// to be defined
 SparkDatastore ds = new SparkDatastore(sparkSession);
@@ -44,7 +50,9 @@ SparkQueryEngine qe = new SparkQueryEngine(ds);
 #### ClickHouse
 
 Maven
+
 ```xml
+
 <dependency>
   <groupId>io.squashql</groupId>
   <artifactId>squashql-clickhouse</artifactId>
@@ -53,6 +61,7 @@ Maven
 ```
 
 Java
+
 ```
 String jdbcUrl = ...; // to be defined
 ClickHouseDatastore ds = new ClickHouseDatastore(jdbcUrl);
@@ -62,7 +71,9 @@ ClickHouseQueryEngine qe = new ClickHouseQueryEngine(ds);
 #### BigQuery
 
 Maven
+
 ```xml
+
 <dependency>
   <groupId>io.squashql</groupId>
   <artifactId>squashql-bigquery</artifactId>
@@ -71,6 +82,7 @@ Maven
 ```
 
 Java
+
 ```
 ServiceAccountCredentials credentials = ...; // to be defined
 String projectId = ...; // to be defined
@@ -79,12 +91,15 @@ BigQueryDatastore ds = new BigQueryServiceAccountDatastore(credentials, projectI
 BigQueryEngine qe = new BigQueryEngine(ds);
 ```
 
-See how to create a [credentials object in BigQuery documentation](https://cloud.google.com/bigquery/docs/authentication/service-account-file)
+See how to create
+a [credentials object in BigQuery documentation](https://cloud.google.com/bigquery/docs/authentication/service-account-file)
 
 #### Snowflake
 
 Maven
+
 ```xml
+
 <dependency>
   <groupId>io.squashql</groupId>
   <artifactId>squashql-snowflake</artifactId>
@@ -93,6 +108,7 @@ Maven
 ```
 
 Java
+
 ```
 String jdbcUrl = jdbc:snowflake://<account_identifier>.snowflakecomputing.com; // to be defined
 String database = ...; // to be defined
@@ -101,3 +117,25 @@ Properties properties = ... // to be defined, it contains in particular the cred
 SnowflakeDatastore ds = new SnowflakeDatastore(jdbcUrl, database, schema, properties);
 SnowflakeQueryEngine qe = new SnowflakeQueryEngine(ds);
 ```
+
+#### PostgreSQL
+
+Maven
+
+```xml
+
+<dependency>
+  <groupId>io.squashql</groupId>
+  <artifactId>squashql-postgresql</artifactId>
+  <version>${squashql.version}</version>
+</dependency>
+```
+
+Java
+
+```
+String jdbcUrl = jdbc:postgresql://localhost:51215/<database>; // to be defined
+Properties properties = ... // to be defined, it contains in particular the credentials (user, password...)
+PostgreSQLDatastore ds = new PostgreSQLDatastore(jdbcUrl, properties);
+PostgreSQLQueryEngine qe = new PostgreSQLQueryEngine(ds);
+```
diff --git a/duckdb/src/main/java/io/squashql/query/database/DuckDBQueryEngine.java b/duckdb/src/main/java/io/squashql/query/database/DuckDBQueryEngine.java
index ac3109e..f469f18 100644
--- a/duckdb/src/main/java/io/squashql/query/database/DuckDBQueryEngine.java
+++ b/duckdb/src/main/java/io/squashql/query/database/DuckDBQueryEngine.java
@@ -45,7 +45,7 @@ public class DuckDBQueryEngine extends JdbcQueryEngine<DuckDBDatastore> {
   }
 
   @Override
-  public QueryRewriter queryRewriter(DatabaseQuery query) {
+  public QueryRewriter queryRewriter() {
     return new DuckDBQueryRewriter();
   }
 }
diff --git a/duckdb/src/main/java/io/squashql/transaction/DuckDBDataLoader.java b/duckdb/src/main/java/io/squashql/transaction/DuckDBDataLoader.java
index 0aaa5b2..60c567e 100644
--- a/duckdb/src/main/java/io/squashql/transaction/DuckDBDataLoader.java
+++ b/duckdb/src/main/java/io/squashql/transaction/DuckDBDataLoader.java
@@ -2,18 +2,17 @@ package io.squashql.transaction;
 
 import io.squashql.DuckDBDatastore;
 import io.squashql.jdbc.JdbcUtil;
-import io.squashql.query.database.SqlUtils;
 import io.squashql.table.Table;
 import io.squashql.type.TableTypedField;
 
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.sql.Statement;
+import java.sql.*;
 import java.time.LocalDate;
-import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.StringJoiner;
+import java.util.stream.IntStream;
 
 public class DuckDBDataLoader implements DataLoader {
 
@@ -52,29 +51,50 @@ public class DuckDBDataLoader implements DataLoader {
 
   @Override
   public void load(String table, List<Object[]> tuples) {
-    StringJoiner sql = new StringJoiner(",", "insert into \"" + table + "\" values ", "");
+    String join = String.join(",", IntStream.range(0, tuples.get(0).length).mapToObj(i -> "?").toList());
+    String pattern = "insert into " + table + " values(" + join + ")";
     try (Connection conn = this.datastore.getConnection();
-         Statement stmt = conn.createStatement()) {
-      for (Object[] tuple : tuples) {
-        StringJoiner sb = new StringJoiner(",", "(", ")");
-        for (Object o : tuple) {
-          if (o != null && (o.getClass().equals(LocalDate.class) || o.getClass().equals(LocalDateTime.class))) {
-            o = o.toString();
-          }
+         PreparedStatement stmt = conn.prepareStatement(pattern)) {
 
-          if (o instanceof String) {
-            sb.add('\'' + SqlUtils.escapeSingleQuote((String) o, "''") + '\'');
-          } else {
-            sb.add(String.valueOf(o));
+      for (Object[] tuple : tuples) {
+        for (int i = 0; i < tuple.length; i++) {
+          Object o = tuple[i];
+          if (o instanceof LocalDate ld) {
+            o = new Date(ld.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli());
+          } else if (o != null && o.getClass().isArray()) {
+            o = Arrays.toString((Object[]) o);
           }
+          stmt.setObject(i + 1, o);
         }
-        sql.add(sb.toString());
+        stmt.addBatch();
       }
-      // addBatch is Not supported.
-      stmt.execute(sql.toString());
+      stmt.executeBatch();
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
+//    StringJoiner sql = new StringJoiner(",", "insert into \"" + table + "\" values ", "");
+//    try (Connection conn = this.datastore.getConnection();
+//         Statement stmt = conn.createStatement()) {
+//      for (Object[] tuple : tuples) {
+//        StringJoiner sb = new StringJoiner(",", "(", ")");
+//        for (Object o : tuple) {
+//          if (o != null && (o.getClass().equals(LocalDate.class) || o.getClass().equals(LocalDateTime.class))) {
+//            o = o.toString();
+//          }
+//
+//          if (o instanceof String) {
+//            sb.add('\'' + SqlUtils.escapeSingleQuote((String) o, "''") + '\'');
+//          } else {
+//            sb.add(String.valueOf(o));
+//          }
+//        }
+//        sql.add(sb.toString());
+//      }
+//      // addBatch is Not supported.
+//      stmt.execute(sql.toString());
+//    } catch (SQLException e) {
+//      throw new RuntimeException(e);
+//    }
   }
 
   @Override
diff --git a/pom.xml b/pom.xml
index f5e1245..f7e5000 100644
--- a/pom.xml
+++ b/pom.xml
@@ -63,6 +63,7 @@
     <spark.version>3.5.1</spark.version>
     <bigquery.version>26.37.0</bigquery.version>
     <snowflake-jdbc.version>3.15.1</snowflake-jdbc.version>
+    <postgresql.version>42.7.3</postgresql.version>
   </properties>
 
   <modules>
@@ -75,6 +76,7 @@
     <module>java-client</module>
     <module>js/typescript-library</module>
     <module>duckdb</module>
+    <module>postgresql</module>
   </modules>
 
   <distributionManagement>
diff --git a/postgresql/pom.xml b/postgresql/pom.xml
new file mode 100644
index 0000000..4c22814
--- /dev/null
+++ b/postgresql/pom.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>io.squashql</groupId>
+    <artifactId>squashql-parent</artifactId>
+    <version>1.17.3-SNAPSHOT</version>
+  </parent>
+
+  <groupId>io.squashql</groupId>
+  <artifactId>squashql-postgresql</artifactId>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.testcontainers</groupId>
+        <artifactId>testcontainers-bom</artifactId>
+        <version>${testcontainers.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <dependencies>
+    <dependency>
+      <groupId>io.squashql</groupId>
+      <artifactId>squashql-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.postgresql</groupId>
+      <artifactId>postgresql</artifactId>
+      <version>${postgresql.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>io.squashql</groupId>
+      <artifactId>squashql-core</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.platform</groupId>
+      <artifactId>junit-platform-launcher</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.platform</groupId>
+      <artifactId>junit-platform-engine</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.platform</groupId>
+      <artifactId>junit-platform-commons</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.assertj</groupId>
+      <artifactId>assertj-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.testcontainers</groupId>
+      <artifactId>testcontainers</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.testcontainers</groupId>
+      <artifactId>postgresql</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.testcontainers</groupId>
+      <artifactId>junit-jupiter</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/postgresql/src/main/java/io/squashql/PostgreSQLDatastore.java b/postgresql/src/main/java/io/squashql/PostgreSQLDatastore.java
new file mode 100644
index 0000000..4dfd0be
--- /dev/null
+++ b/postgresql/src/main/java/io/squashql/PostgreSQLDatastore.java
@@ -0,0 +1,41 @@
+package io.squashql;
+
+import com.google.common.base.Suppliers;
+import io.squashql.jdbc.JdbcDatastore;
+import io.squashql.jdbc.JdbcUtil;
+import io.squashql.store.Store;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Supplier;
+
+public class PostgreSQLDatastore implements JdbcDatastore {
+
+  private final String jdbcUrl;
+  private final Properties connectionProperties;
+  public final Supplier<Map<String, Store>> stores;
+
+  public PostgreSQLDatastore(String jdbcUrl, Properties properties) {
+    this.jdbcUrl = jdbcUrl;
+    this.connectionProperties = properties;
+    String schema = properties.getProperty("currentSchema", "public");
+    this.stores = Suppliers.memoize(() -> JdbcUtil.getStores(properties.getProperty("database"), schema, getConnection(), PostgreSQLUtil::getJavaClass));
+  }
+
+  @Override
+  public Connection getConnection() {
+    try {
+      return DriverManager.getConnection(this.jdbcUrl, this.connectionProperties);
+    } catch (SQLException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Override
+  public Map<String, Store> storeByName() {
+    return this.stores.get();
+  }
+}
diff --git a/postgresql/src/main/java/io/squashql/PostgreSQLUtil.java b/postgresql/src/main/java/io/squashql/PostgreSQLUtil.java
new file mode 100644
index 0000000..b925f4a
--- /dev/null
+++ b/postgresql/src/main/java/io/squashql/PostgreSQLUtil.java
@@ -0,0 +1,59 @@
+package io.squashql;
+
+
+import io.squashql.jdbc.JdbcUtil;
+import io.squashql.list.Lists;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+
+public final class PostgreSQLUtil {
+
+  private PostgreSQLUtil() {
+  }
+
+  /**
+   * It is used in tests by the PostgreSQL {@link io.squashql.transaction.DataLoader} impl.
+   */
+  public static String classToPostgreSQLType(Class<?> clazz) {
+    if (clazz.equals(String.class)) {
+      return "text";
+    } else if (clazz.equals(double.class)) {
+      return "double precision";
+    } else if (clazz.equals(Lists.LongList.class)) {
+      return "integer[]";
+    } else if (clazz.equals(Lists.StringList.class)) {
+      return "varchar[]";
+    } else {
+      return JdbcUtil.classToSqlType(clazz);
+    }
+  }
+
+  public static Class<?> getJavaClass(ResultSetMetaData metaData, int columnIndex) throws SQLException {
+    String columnTypeName = metaData.getColumnTypeName(columnIndex);
+    String columnClassName = metaData.getColumnClassName(columnIndex);
+    if (columnTypeName.equals("numeric")) {
+      return columnClassName.equals(BigDecimal.class.getName()) ? BigDecimal.class : BigInteger.class;
+    } else {
+      return getJavaClass(metaData.getColumnType(columnIndex), columnTypeName);
+    }
+  }
+
+  public static Class<?> getJavaClass(int dataType, String columnTypeName) {
+    if (columnTypeName.equals("numeric")) {
+      return BigDecimal.class; // FIXME this might be wrong
+    } else if (columnTypeName.equals("_int4") || columnTypeName.equals("_int8") || columnTypeName.equals("_numeric")) {
+      return Lists.LongList.class;
+    } else if (columnTypeName.equals("_float4") || columnTypeName.equals("_float8")) {
+      return Lists.DoubleList.class;
+    } else if (columnTypeName.equals("_varchar")) {
+      return Lists.StringList.class;
+    } else if (columnTypeName.equals("_date")) {
+      return Lists.LocalDateList.class;
+    } else {
+      return JdbcUtil.sqlTypeToClass(dataType);
+    }
+  }
+}
diff --git a/postgresql/src/main/java/io/squashql/query/database/PostgreSQLQueryEngine.java b/postgresql/src/main/java/io/squashql/query/database/PostgreSQLQueryEngine.java
new file mode 100644
index 0000000..4ddfcda
--- /dev/null
+++ b/postgresql/src/main/java/io/squashql/query/database/PostgreSQLQueryEngine.java
@@ -0,0 +1,79 @@
+package io.squashql.query.database;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.PostgreSQLUtil;
+import io.squashql.jdbc.JdbcQueryEngine;
+import io.squashql.jdbc.ResultSetReader;
+import io.squashql.util.Types;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.function.BiFunction;
+
+public class PostgreSQLQueryEngine extends JdbcQueryEngine<PostgreSQLDatastore> {
+
+  /**
+   * <a href="https://www.postgresql.org/docs/16/functions-aggregate.html/">aggregate functions</a>
+   * NOTE: there is more but only a subset is proposed here.
+   */
+  public static final List<String> SUPPORTED_AGGREGATION_FUNCTIONS = List.of(
+          "count",
+          "min",
+          "max",
+          "sum",
+          "avg",
+          "any");
+
+  public PostgreSQLQueryEngine(PostgreSQLDatastore datastore) {
+    super(datastore);
+  }
+
+  @Override
+  protected BiFunction<ResultSetMetaData, Integer, Class<?>> typeToClassConverter() {
+    return (metaData, column) -> {
+      try {
+        return PostgreSQLUtil.getJavaClass(metaData, column);
+      } catch (SQLException e) {
+        throw new RuntimeException(e);
+      }
+    };
+  }
+
+  @Override
+  protected ResultSetReader createResultSetReader() {
+    return new ResultSetReader() {
+      @Override
+      public Object read(List<Class<?>> columnTypes, ResultSet tableResult, int index) {
+        try {
+          if (tableResult.getObject(index + 1) == null) {
+            return null;
+          }
+
+          Class<?> klazz = columnTypes.get(index);
+          if (klazz.equals(BigDecimal.class)) {
+            return Types.castToDouble(tableResult.getBigDecimal(index + 1));
+          } else if (klazz.equals(BigInteger.class)) {
+            return tableResult.getLong(index + 1);
+          }
+        } catch (SQLException e) {
+          throw new RuntimeException(e);
+        }
+        return ResultSetReader.super.read(columnTypes, tableResult, index);
+      }
+    };
+  }
+
+  @Override
+  public List<String> supportedAggregationFunctions() {
+    return SUPPORTED_AGGREGATION_FUNCTIONS;
+  }
+
+  @Override
+  public QueryRewriter queryRewriter() {
+    return new PostgreSQLQueryRewriter();
+  }
+}
diff --git a/postgresql/src/main/java/io/squashql/query/database/PostgreSQLQueryRewriter.java b/postgresql/src/main/java/io/squashql/query/database/PostgreSQLQueryRewriter.java
new file mode 100644
index 0000000..3a61bc9
--- /dev/null
+++ b/postgresql/src/main/java/io/squashql/query/database/PostgreSQLQueryRewriter.java
@@ -0,0 +1,69 @@
+package io.squashql.query.database;
+
+import io.squashql.query.BinaryOperator;
+import io.squashql.type.FunctionTypedField;
+import io.squashql.type.TypedField;
+
+class PostgreSQLQueryRewriter implements QueryRewriter {
+
+  @Override
+  public String tableName(String table) {
+    return SqlUtils.doubleQuoteEscape(table);
+  }
+
+  @Override
+  public String cteName(String cteName) {
+    return SqlUtils.doubleQuoteEscape(cteName);
+  }
+
+  @Override
+  public String fieldName(String field) {
+    return SqlUtils.doubleQuoteEscape(field);
+  }
+
+  @Override
+  public String escapeAlias(String alias) {
+    return SqlUtils.doubleQuoteEscape(alias);
+  }
+
+  @Override
+  public boolean usePartialRollupSyntax() {
+    return true;
+  }
+
+  @Override
+  public boolean useAliasInHavingClause() {
+    return false;
+  }
+
+  @Override
+  public String functionExpression(FunctionTypedField ftf) {
+    if ("current_date".contains(ftf.function())) {
+      return "CURRENT_DATE";
+    } else if (SQLFunctions.SUPPORTED_DATE_FUNCTIONS.contains(ftf.function())) {
+      return String.format("extract(%s from %s)", ftf.function(), ftf.field().sqlExpression(this));
+    } else {
+      return QueryRewriter.super.functionExpression(ftf);
+    }
+  }
+
+  @Override
+  public String binaryOperation(BinaryOperator operator, String leftOperand, String rightOperand) {
+    // NULLIF function takes two expressions and returns NULL if they are equal and if they are not equal it will return the first expression.
+    return switch (operator) {
+      case DIVIDE -> new StringBuilder()
+              .append(leftOperand)
+              .append(" ")
+              .append(operator.infix)
+              .append(" ")
+              .append(String.format("NULLIF(%s,0)", rightOperand))
+              .toString();
+      default -> QueryRewriter.super.binaryOperation(operator, leftOperand, rightOperand);
+    };
+  }
+
+  @Override
+  public String arrayContains(TypedField field, Object value) {
+    return String.format("%s=ANY(%s)", value, field.sqlExpression(this));
+  }
+}
diff --git a/postgresql/src/main/java/io/squashql/transaction/PostgreSQLDataLoader.java b/postgresql/src/main/java/io/squashql/transaction/PostgreSQLDataLoader.java
new file mode 100644
index 0000000..84a6b20
--- /dev/null
+++ b/postgresql/src/main/java/io/squashql/transaction/PostgreSQLDataLoader.java
@@ -0,0 +1,76 @@
+package io.squashql.transaction;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.PostgreSQLUtil;
+import io.squashql.type.TableTypedField;
+import lombok.AllArgsConstructor;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Collection;
+import java.util.List;
+import java.util.StringJoiner;
+import java.util.stream.IntStream;
+
+@AllArgsConstructor
+public class PostgreSQLDataLoader implements DataLoader {
+
+  protected final PostgreSQLDatastore datastore;
+
+  public void dropAndCreateInMemoryTable(String table, List<TableTypedField> fields) {
+    dropAndCreateInMemoryTable(this.datastore, table, fields);
+  }
+
+  public static void dropAndCreateInMemoryTable(PostgreSQLDatastore datastore,
+                                                String table,
+                                                List<TableTypedField> fields) {
+    try (Connection conn = datastore.getConnection();
+         Statement stmt = conn.createStatement()) {
+      stmt.execute("drop table if exists " + table);
+      StringJoiner joiner = new StringJoiner(",", "(", ")");
+      for (TableTypedField field : fields) {
+        joiner.add("\"" + field.name() + "\" " + PostgreSQLUtil.classToPostgreSQLType(field.type()));
+      }
+      stmt.execute("create table " + table + joiner);
+    } catch (SQLException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Override
+  public void load(String table, List<Object[]> tuples) {
+    String join = String.join(",", IntStream.range(0, tuples.get(0).length).mapToObj(i -> "?").toList());
+    String pattern = "insert into " + table + " values(" + join + ")";
+    try (Connection conn = this.datastore.getConnection();
+         PreparedStatement stmt = conn.prepareStatement(pattern)) {
+
+      for (Object[] tuple : tuples) {
+        for (int i = 0; i < tuple.length; i++) {
+          stmt.setObject(i + 1, tuple[i]);
+        }
+        stmt.addBatch();
+      }
+      stmt.executeBatch();
+    } catch (SQLException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Override
+  public void loadCsv(String table, String path, String delimiter, boolean header) {
+    throw new RuntimeException("not implemented");
+  }
+
+  public void dropTables(Collection<String> tables) {
+    try (Connection conn = this.datastore.getConnection();
+         Statement stmt = conn.createStatement()) {
+      for (String table : tables) {
+        stmt.execute("drop table if exists " + table);
+      }
+    } catch (SQLException e) {
+      throw new RuntimeException(e);
+    }
+  }
+}
diff --git a/snowflake/src/main/java/io/squashql/query/database/SnowflakeQueryEngine.java b/snowflake/src/main/java/io/squashql/query/database/SnowflakeQueryEngine.java
index 4f53c4f..e301cd0 100644
--- a/snowflake/src/main/java/io/squashql/query/database/SnowflakeQueryEngine.java
+++ b/snowflake/src/main/java/io/squashql/query/database/SnowflakeQueryEngine.java
@@ -105,7 +105,7 @@ public class SnowflakeQueryEngine extends JdbcQueryEngine<SnowflakeDatastore> {
   }
 
   @Override
-  public QueryRewriter queryRewriter(DatabaseQuery query) {
+  public QueryRewriter queryRewriter() {
     return new SnowflakeQueryRewriter();
   }
 }
diff --git a/spark/src/main/java/io/squashql/query/database/SparkQueryEngine.java b/spark/src/main/java/io/squashql/query/database/SparkQueryEngine.java
index aadac69..705f6c5 100644
--- a/spark/src/main/java/io/squashql/query/database/SparkQueryEngine.java
+++ b/spark/src/main/java/io/squashql/query/database/SparkQueryEngine.java
@@ -75,7 +75,7 @@ public class SparkQueryEngine extends AQueryEngine<SparkDatastore> {
   }
 
   @Override
-  public QueryRewriter queryRewriter(DatabaseQuery query) {
-    return new SparkQueryRewriter(query);
+  public QueryRewriter queryRewriter() {
+    return new SparkQueryRewriter();
   }
 }
diff --git a/spark/src/main/java/io/squashql/query/database/SparkQueryRewriter.java b/spark/src/main/java/io/squashql/query/database/SparkQueryRewriter.java
index 6362cb6..3799cde 100644
--- a/spark/src/main/java/io/squashql/query/database/SparkQueryRewriter.java
+++ b/spark/src/main/java/io/squashql/query/database/SparkQueryRewriter.java
@@ -4,10 +4,6 @@ import io.squashql.type.TypedField;
 
 public class SparkQueryRewriter extends DefaultQueryRewriter {
 
-  public SparkQueryRewriter(DatabaseQuery query) {
-    super(query);
-  }
-
   @Override
   public String escapeSingleQuote(String s) {
     return SqlUtils.escapeSingleQuote(s, "\\'");


Test patch:
diff --git a/core/src/test/java/io/squashql/TestClass.java b/core/src/test/java/io/squashql/TestClass.java
index e73136c..81fab6d 100644
--- a/core/src/test/java/io/squashql/TestClass.java
+++ b/core/src/test/java/io/squashql/TestClass.java
@@ -20,7 +20,8 @@ public @interface TestClass {
     CLICKHOUSE("ClickHouse"),
     BIGQUERY("BigQuery"),
     SNOWFLAKE("Snowflake"),
-    DUCKDB("DuckDB");
+    DUCKDB("DuckDB"),
+    POSTGRESQL("PostgreSQL");
 
     public final String className;
 
diff --git a/core/src/test/java/io/squashql/join/TestSqlExperimentalQueryJoinExecutor.java b/core/src/test/java/io/squashql/join/TestSqlExperimentalQueryJoinExecutor.java
index fd88235..2c4ca96 100644
--- a/core/src/test/java/io/squashql/join/TestSqlExperimentalQueryJoinExecutor.java
+++ b/core/src/test/java/io/squashql/join/TestSqlExperimentalQueryJoinExecutor.java
@@ -94,8 +94,8 @@ public class TestSqlExperimentalQueryJoinExecutor {
     }
 
     @Override
-    public QueryRewriter queryRewriter(DatabaseQuery query) {
-      return new DefaultQueryRewriter(query) {
+    public QueryRewriter queryRewriter() {
+      return new DefaultQueryRewriter() {
 
         static final Pattern p = Pattern.compile("__cte([0-9])__");
 
diff --git a/core/src/test/java/io/squashql/query/ABaseTestQuery.java b/core/src/test/java/io/squashql/query/ABaseTestQuery.java
index 52a3313..7b1b6f1 100644
--- a/core/src/test/java/io/squashql/query/ABaseTestQuery.java
+++ b/core/src/test/java/io/squashql/query/ABaseTestQuery.java
@@ -14,7 +14,7 @@ import java.util.Map;
 public abstract class ABaseTestQuery {
 
   protected Datastore datastore;
-  protected QueryEngine queryEngine;
+  protected QueryEngine<?> queryEngine;
   protected DataLoader tm;
   protected QueryExecutor executor;
   protected Map<String, List<TableTypedField>> fieldsByStore;
@@ -37,7 +37,7 @@ public abstract class ABaseTestQuery {
   protected void afterSetup() {
   }
 
-  protected abstract QueryEngine createQueryEngine(Datastore datastore);
+  protected abstract QueryEngine<?> createQueryEngine(Datastore datastore);
 
   protected abstract Datastore createDatastore();
 
diff --git a/core/src/test/java/io/squashql/query/ATestBinaryOperationMeasure.java b/core/src/test/java/io/squashql/query/ATestBinaryOperationMeasure.java
index d6d10af..8da5c29 100644
--- a/core/src/test/java/io/squashql/query/ATestBinaryOperationMeasure.java
+++ b/core/src/test/java/io/squashql/query/ATestBinaryOperationMeasure.java
@@ -24,15 +24,15 @@ public abstract class ATestBinaryOperationMeasure extends ABaseTestQuery {
     TableTypedField ean = new TableTypedField(this.storeName, "ean", String.class);
     TableTypedField category = new TableTypedField(this.storeName, "category", String.class);
     TableTypedField sales = new TableTypedField(this.storeName, "sales", double.class);
-    TableTypedField qty = new TableTypedField(this.storeName, "quantity", long.class);
+    TableTypedField qty = new TableTypedField(this.storeName, "quantity", int.class);
     return Map.of(this.storeName, List.of(ean, category, sales, qty));
   }
 
   @Override
   protected void loadData() {
     this.tm.load(this.storeName, List.of(
-            new Object[]{"bottle", "drink", 20d, 10l},
-            new Object[]{"cookie", "food", 30d, 10l}
+            new Object[]{"bottle", "drink", 20d, 10},
+            new Object[]{"cookie", "food", 30d, 10}
     ));
   }
 
@@ -54,7 +54,7 @@ public abstract class ATestBinaryOperationMeasure extends ABaseTestQuery {
 
     Table table = this.executor.executeQuery(query);
     double salesV = 50d;
-    long qtyV = 20l;
+    long qtyV = 20L;
     Assertions.assertThat(table).contains(List.of(salesV, qtyV, salesV + salesV, salesV + qtyV, qtyV + qtyV));
     Assertions
             .assertThat(table.headers().stream().map(Header::name))
@@ -79,7 +79,7 @@ public abstract class ATestBinaryOperationMeasure extends ABaseTestQuery {
 
     Table table = this.executor.executeQuery(query);
     double salesV = 50d;
-    long qtyV = 20l;
+    long qtyV = 20L;
     Assertions.assertThat(table).contains(List.of(salesV, qtyV, salesV - salesV, salesV - qtyV, qtyV - qtyV));
     Assertions
             .assertThat(table.headers().stream().map(Header::name))
@@ -104,7 +104,7 @@ public abstract class ATestBinaryOperationMeasure extends ABaseTestQuery {
 
     Table table = this.executor.executeQuery(query);
     double salesV = 50d;
-    long qtyV = 20l;
+    long qtyV = 20L;
     Assertions.assertThat(table).contains(List.of(salesV, qtyV, salesV * salesV, salesV * qtyV, qtyV * qtyV));
     Assertions
             .assertThat(table.headers().stream().map(Header::name))
@@ -124,16 +124,15 @@ public abstract class ATestBinaryOperationMeasure extends ABaseTestQuery {
             .withMeasure(sales)
             .withMeasure(quantity)
             .withMeasure(new BinaryOperationMeasure("divide1", BinaryOperator.DIVIDE, sales, sales))
-            .withMeasure(new BinaryOperationMeasure("divide2", BinaryOperator.DIVIDE, sales, quantity))
-            .withMeasure(new BinaryOperationMeasure("divide3", BinaryOperator.DIVIDE, quantity, quantity));
+            .withMeasure(new BinaryOperationMeasure("divide2", BinaryOperator.DIVIDE, sales, quantity));
 
     Table table = this.executor.executeQuery(query);
     double salesV = 50d;
-    long qtyV = 20l;
-    Assertions.assertThat(table).contains(List.of(salesV, qtyV, salesV / salesV, salesV / qtyV, (double) qtyV / qtyV));
+    long qtyV = 20L;
+    Assertions.assertThat(table).contains(List.of(salesV, qtyV, salesV / salesV, salesV / qtyV));
     Assertions
             .assertThat(table.headers().stream().map(Header::name))
-            .containsExactlyInAnyOrder("sum(sales)", "sum(quantity)", "divide1", "divide2", "divide3");
+            .containsExactlyInAnyOrder("sum(sales)", "sum(quantity)", "divide1", "divide2");
   }
 
   /**
@@ -152,7 +151,7 @@ public abstract class ATestBinaryOperationMeasure extends ABaseTestQuery {
 
     Table table = this.executor.executeQuery(query);
     double salesV = 50d;
-    long qtyV = 20l;
+    long qtyV = 20L;
     Assertions.assertThat(table).contains(List.of((salesV - qtyV) / qtyV));
   }
 }
diff --git a/core/src/test/java/io/squashql/query/ATestEdgeCasesBinaryOperations.java b/core/src/test/java/io/squashql/query/ATestEdgeCasesBinaryOperations.java
index 18ac9d6..62621a8 100644
--- a/core/src/test/java/io/squashql/query/ATestEdgeCasesBinaryOperations.java
+++ b/core/src/test/java/io/squashql/query/ATestEdgeCasesBinaryOperations.java
@@ -52,7 +52,6 @@ public abstract class ATestEdgeCasesBinaryOperations extends ABaseTestQuery {
             .select(List.of(this.ean), List.of(divide("divide", sum, sum), avg))
             .build();
     Table result = this.executor.executeQuery(query);
-    result.show();
     Assertions.assertThat(result).containsExactly(
             List.of("0", 1d, 1d),
             List.of("1", 1d, 1d),
@@ -70,7 +69,7 @@ public abstract class ATestEdgeCasesBinaryOperations extends ABaseTestQuery {
     } else if (lc.contains(TestClass.Type.DUCKDB.name().toLowerCase())
             || lc.contains(TestClass.Type.CLICKHOUSE.name().toLowerCase())) {
       return Double.NaN;
-    }  else if (lc.contains(TestClass.Type.SNOWFLAKE.name().toLowerCase())) {
+    } else if (lc.contains(TestClass.Type.SNOWFLAKE.name().toLowerCase())) {
       return 0d;
     } else {
       return null;
diff --git a/core/src/test/java/io/squashql/query/ATestExperimentalQueryJoinExecutor.java b/core/src/test/java/io/squashql/query/ATestExperimentalQueryJoinExecutor.java
index 1d682b3..eb5c9f3 100644
--- a/core/src/test/java/io/squashql/query/ATestExperimentalQueryJoinExecutor.java
+++ b/core/src/test/java/io/squashql/query/ATestExperimentalQueryJoinExecutor.java
@@ -21,9 +21,9 @@ import static io.squashql.query.dto.OrderKeywordDto.DESC;
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public abstract class ATestExperimentalQueryJoinExecutor extends ABaseTestQuery {
 
-  String storeA = "StoreA" + getClass().getSimpleName().toLowerCase();
-  String storeB = "StoreB" + getClass().getSimpleName().toLowerCase();
-  String storeC = "StoreC" + getClass().getSimpleName().toLowerCase();
+  String storeA = "store_a" + getClass().getSimpleName().toLowerCase();
+  String storeB = "store_b" + getClass().getSimpleName().toLowerCase();
+  String storeC = "store_c" + getClass().getSimpleName().toLowerCase();
   Field category = new TableField(this.storeA, "category");
   Field idA = new TableField(this.storeA, "idA");
   Field idStoreA = new TableField(this.storeA, "id");
diff --git a/core/src/test/java/io/squashql/query/ATestOrderByFromOrderTable.java b/core/src/test/java/io/squashql/query/ATestOrderByFromOrderTable.java
index 481101a..a398470 100644
--- a/core/src/test/java/io/squashql/query/ATestOrderByFromOrderTable.java
+++ b/core/src/test/java/io/squashql/query/ATestOrderByFromOrderTable.java
@@ -22,7 +22,7 @@ import static io.squashql.query.Functions.criterion;
 public abstract class ATestOrderByFromOrderTable extends ABaseTestQuery {
 
   protected final String storeName = "store" + getClass().getSimpleName().toLowerCase();
-  private final String orderTable = "orderTable";
+  private final String orderTable = "ordertable";
   private final TableField portfolio = new TableField(this.storeName, "portfolio");
   private final TableField ticker = new TableField(this.storeName, "ticker");
   private final TableField orderId = new TableField(this.orderTable, "orderId");
diff --git a/core/src/test/java/io/squashql/query/ATestPeriodComparison.java b/core/src/test/java/io/squashql/query/ATestPeriodComparison.java
index 2d53d90..fd0cd6a 100644
--- a/core/src/test/java/io/squashql/query/ATestPeriodComparison.java
+++ b/core/src/test/java/io/squashql/query/ATestPeriodComparison.java
@@ -442,6 +442,8 @@ public abstract class ATestPeriodComparison extends ABaseTestQuery {
     String qesn = this.queryEngine.getClass().getSimpleName();
     if (qesn.contains(TestClass.Type.CLICKHOUSE.className) || qesn.contains(TestClass.Type.SPARK.className)) {
       return i;
+    } else if (qesn.contains(TestClass.Type.POSTGRESQL.className)) {
+      return (double) i;
     } else {
       return (long) i;
     }
@@ -451,6 +453,8 @@ public abstract class ATestPeriodComparison extends ABaseTestQuery {
     String qesn = this.queryEngine.getClass().getSimpleName();
     if (qesn.contains(TestClass.Type.CLICKHOUSE.className) || qesn.contains(TestClass.Type.SPARK.className)) {
       return i;
+    } else if (qesn.contains(TestClass.Type.POSTGRESQL.className)) {
+      return (double) i;
     } else {
       return (long) i;
     }
@@ -460,6 +464,8 @@ public abstract class ATestPeriodComparison extends ABaseTestQuery {
     String qesn = this.queryEngine.getClass().getSimpleName();
     if (qesn.contains(TestClass.Type.CLICKHOUSE.className) || qesn.contains(TestClass.Type.SPARK.className)) {
       return i;
+    } else if (qesn.contains(TestClass.Type.POSTGRESQL.className)) {
+      return (double) i;
     } else {
       return (long) i;
     }
diff --git a/core/src/test/java/io/squashql/query/ATestQueryCache.java b/core/src/test/java/io/squashql/query/ATestQueryCache.java
index e76011b..fe5436d 100644
--- a/core/src/test/java/io/squashql/query/ATestQueryCache.java
+++ b/core/src/test/java/io/squashql/query/ATestQueryCache.java
@@ -30,7 +30,7 @@ import static io.squashql.query.database.QueryEngine.GRAND_TOTAL;
 import static io.squashql.query.database.QueryEngine.TOTAL;
 import static io.squashql.transaction.DataLoader.SCENARIO_FIELD_NAME;
 
-@TestClass(ignore = {TestClass.Type.BIGQUERY, TestClass.Type.SNOWFLAKE, TestClass.Type.CLICKHOUSE, TestClass.Type.SPARK})
+@TestClass(ignore = {TestClass.Type.BIGQUERY, TestClass.Type.SNOWFLAKE, TestClass.Type.CLICKHOUSE, TestClass.Type.SPARK, TestClass.Type.POSTGRESQL})
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public abstract class ATestQueryCache extends ABaseTestQuery {
 
diff --git a/core/src/test/java/io/squashql/query/ATestQueryExecutionLocality.java b/core/src/test/java/io/squashql/query/ATestQueryExecutionLocality.java
index aa0519f..fe454bc 100644
--- a/core/src/test/java/io/squashql/query/ATestQueryExecutionLocality.java
+++ b/core/src/test/java/io/squashql/query/ATestQueryExecutionLocality.java
@@ -94,8 +94,8 @@ public abstract class ATestQueryExecutionLocality extends ABaseTestQuery {
     }
 
     @Override
-    public QueryRewriter queryRewriter(DatabaseQuery query) {
-      return this.underlying.queryRewriter(query);
+    public QueryRewriter queryRewriter() {
+      return this.underlying.queryRewriter();
     }
   }
 }
diff --git a/core/src/test/java/io/squashql/query/ATestQueryExecutor.java b/core/src/test/java/io/squashql/query/ATestQueryExecutor.java
index 58fab0f..a889217 100644
--- a/core/src/test/java/io/squashql/query/ATestQueryExecutor.java
+++ b/core/src/test/java/io/squashql/query/ATestQueryExecutor.java
@@ -419,7 +419,7 @@ public abstract class ATestQueryExecutor extends ABaseTestQuery {
    */
   @Test
   void testSumIf() {
-    QueryRewriter qr = this.queryEngine.queryRewriter(null);
+    QueryRewriter qr = this.queryEngine.queryRewriter();
     String expression = String.format("sum(case when %s = 'food' OR %s = 'drink' then %s end)",
             qr.fieldName("category"),
             qr.fieldName("category"),
@@ -661,14 +661,15 @@ public abstract class ATestQueryExecutor extends ABaseTestQuery {
 
   @Test
   void testRawQueryExecution() {
-    QueryRewriter qr = this.executor.queryEngine.queryRewriter(null);
+    QueryRewriter qr = this.executor.queryEngine.queryRewriter();
     String tableName = qr.tableName(this.storeName);
     String ean = qr.fieldName("ean");
     String price = qr.fieldName("price");
-    // Use SUMPRICE in upper case to simplify the test. Indeed, Snowflake converts lower case aliases to upper case...
-    Table result = this.executor.executeRaw(String.format("select %s, sum(%s) as SUMPRICE from %s group by %s order by %s", ean, price, tableName, ean, ean));
-    Assertions.assertThat(result.headers().stream().map(header -> header.name()).toList())
-            .containsExactly("ean", "SUMPRICE");
+    Table result = this.executor.executeRaw(String.format("select %s, sum(%s) as sumprice from %s group by %s order by %s", ean, price, tableName, ean, ean));
+    List<String> headerNames = result.headers().stream().map(Header::name).toList();
+    // Snowflake converts lower case aliases to upper case and Postgres converts everything in lowercase...
+    Assertions.assertThat(headerNames.get(0).toLowerCase()).isEqualTo("ean");
+    Assertions.assertThat(headerNames.get(1).toLowerCase()).isEqualTo("sumprice");
     Assertions.assertThat(result).containsExactly(
             List.of("cookie", 9.0d),
             List.of("shirt", 30d),
@@ -845,7 +846,6 @@ public abstract class ATestQueryExecutor extends ABaseTestQuery {
   @Test
   void testHavingConditions() {
     BasicMeasure price_sum = (BasicMeasure) sum("pricesum", "price");
-    BasicMeasure price_sum_expr = new ExpressionMeasure("p_expr", "sum(" + this.queryEngine.queryRewriter(null).fieldName("price") + ")");
     // Single condition
     QueryDto query = Query
             .from(this.storeName)
@@ -857,6 +857,7 @@ public abstract class ATestQueryExecutor extends ABaseTestQuery {
             List.of("cookie", 9.0d),
             List.of("shirt", 30d));
 
+    BasicMeasure price_sum_expr = new ExpressionMeasure("p_expr", "sum(" + this.queryEngine.queryRewriter().fieldName("price") + ")");
     // Multiple conditions
     query = Query
             .from(this.storeName)
diff --git a/core/src/test/java/io/squashql/query/ATestQueryWithJoins.java b/core/src/test/java/io/squashql/query/ATestQueryWithJoins.java
index e35f8ab..78f97e9 100644
--- a/core/src/test/java/io/squashql/query/ATestQueryWithJoins.java
+++ b/core/src/test/java/io/squashql/query/ATestQueryWithJoins.java
@@ -28,7 +28,7 @@ public abstract class ATestQueryWithJoins extends ABaseTestQuery {
   }
 
   protected final String orders = storeName("orders");
-  protected final String orderDetails = storeName("orderDetails");
+  protected final String orderDetails = storeName("orderdetails");
   protected final String shippers = storeName("shippers");
   protected final String products = storeName("products");
   protected final String categories = storeName("categories");
@@ -149,6 +149,8 @@ public abstract class ATestQueryWithJoins extends ABaseTestQuery {
       return "ambiguous column name 'name'";
     } else if (ds.contains(TestClass.Type.DUCKDB.className)) {
       return "Ambiguous reference to column name \"name\"";
+    } else if (ds.contains(TestClass.Type.POSTGRESQL.className)) {
+      return "column reference \"name\" is ambiguous";
     }
     return null;
   }
diff --git a/core/src/test/java/io/squashql/query/ATestSQLFunction.java b/core/src/test/java/io/squashql/query/ATestSQLFunction.java
index b19cc3c..60ef8f7 100644
--- a/core/src/test/java/io/squashql/query/ATestSQLFunction.java
+++ b/core/src/test/java/io/squashql/query/ATestSQLFunction.java
@@ -151,7 +151,6 @@ public abstract class ATestSQLFunction extends ABaseTestQuery {
     //    Assertions.assertThat(result).containsExactly(List.of(this.now, 1L));
     Assertions.assertThat(result.count()).isEqualTo(1L);
 
-
     // currentDate in select and where + alias. This query does not make any sense...
     Field cr = currentDate().as("CR");
     query = Query
diff --git a/core/src/test/java/io/squashql/query/ATestVector.java b/core/src/test/java/io/squashql/query/ATestVector.java
index 0be1a16..c36facb 100644
--- a/core/src/test/java/io/squashql/query/ATestVector.java
+++ b/core/src/test/java/io/squashql/query/ATestVector.java
@@ -36,10 +36,10 @@ public abstract class ATestVector extends ABaseTestQuery {
   @Override
   protected void loadData() {
     this.tm.load(this.storeName, List.of(
-            new Object[]{"A", 10d, List.of(1L, 3L, 5L), List.of("1", "3", "5")},
-            new Object[]{"B", 8d, List.of(1L, 2L), List.of("1", "2")},
-            new Object[]{"C", 2d, List.of(2L, 4L, 6L), List.of("2", "4", "6")},
-            new Object[]{"D", 1d, List.of(1L), List.of("1")}
+            new Object[]{"A", 10d, List.of(1L, 3L, 5L).toArray(new Long[0]), List.of("1", "3", "5").toArray(new String[0])},
+            new Object[]{"B", 8d, List.of(1L, 2L).toArray(new Long[0]), List.of("1", "2").toArray(new String[0])},
+            new Object[]{"C", 2d, List.of(2L, 4L, 6L).toArray(new Long[0]), List.of("2", "4", "6").toArray(new String[0])},
+            new Object[]{"D", 1d, List.of(1L).toArray(new Long[0]), List.of("1").toArray(new String[0])}
     ));
   }
 
diff --git a/core/src/test/java/io/squashql/query/TestSQLTranslator.java b/core/src/test/java/io/squashql/query/TestSQLTranslator.java
index 70822fc..9823a15 100644
--- a/core/src/test/java/io/squashql/query/TestSQLTranslator.java
+++ b/core/src/test/java/io/squashql/query/TestSQLTranslator.java
@@ -539,7 +539,7 @@ public class TestSQLTranslator {
             criterion(recoPrice, gt(0))
     ));
 
-    DefaultQueryRewriter qr = new DefaultQueryRewriter(null);
+    DefaultQueryRewriter qr = new DefaultQueryRewriter();
     String expression = compiledMeasure(measure).sqlExpression(qr, true);
     assertThat(expression)
             .isEqualTo("sum(case when (`recommendation`.`finalprice` > `recommendation`.`recoprice` and `recommendation`.`recoprice` > 0)" +
@@ -556,7 +556,7 @@ public class TestSQLTranslator {
 
   @Test
   void testComplexFieldCalculation() {
-    DefaultQueryRewriter qr = new DefaultQueryRewriter(null);
+    DefaultQueryRewriter qr = new DefaultQueryRewriter();
     Field a = new TableField("a");
     Field b = new TableField("b");
     Field c = new TableField("c");
diff --git a/postgresql/src/test/java/io/squashql/query/PostgreSQLTestUtil.java b/postgresql/src/test/java/io/squashql/query/PostgreSQLTestUtil.java
new file mode 100644
index 0000000..6bc2adc
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/PostgreSQLTestUtil.java
@@ -0,0 +1,28 @@
+package io.squashql.query;
+
+import org.testcontainers.containers.PostgreSQLContainer;
+import org.testcontainers.utility.DockerImageName;
+
+import java.util.Properties;
+
+public class PostgreSQLTestUtil {
+
+  protected static final boolean printAll = true;
+
+  protected static final Properties TEST_PROPERTIES = new Properties();
+  static {
+    TEST_PROPERTIES.setProperty("user", "test");
+    TEST_PROPERTIES.setProperty("password", "test");
+    TEST_PROPERTIES.setProperty("database", "test");
+  }
+
+  protected static PostgreSQLContainer<?> createContainer() {
+    return new PostgreSQLContainer<>(DockerImageName.parse("postgres:16.2"))
+            .withReuse(true)
+            .withLogConsumer(of -> {
+              if (printAll) {
+                System.out.print("PostgreSQLContainer Container >>>" + of.getUtf8String());
+              }
+            });
+  }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLBinaryOperationMeasure.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLBinaryOperationMeasure.java
new file mode 100644
index 0000000..81e44fa
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLBinaryOperationMeasure.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLBinaryOperationMeasure extends ATestBinaryOperationMeasure {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLBucketing.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLBucketing.java
new file mode 100644
index 0000000..d24a1b6
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLBucketing.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLBucketing extends ATestBucketing {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLDocDrillacross.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLDocDrillacross.java
new file mode 100644
index 0000000..916e57e
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLDocDrillacross.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLDocDrillacross extends ATestDocDrillacross {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLDocGroupComparison.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLDocGroupComparison.java
new file mode 100644
index 0000000..249b590
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLDocGroupComparison.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLDocGroupComparison extends ATestDocGroupComparison {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLDocPeriodComparison.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLDocPeriodComparison.java
new file mode 100644
index 0000000..449d9e9
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLDocPeriodComparison.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLDocPeriodComparison extends ATestDocPeriodComparison {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLEdgeCasesBinaryOperations.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLEdgeCasesBinaryOperations.java
new file mode 100644
index 0000000..171cc1a
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLEdgeCasesBinaryOperations.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLEdgeCasesBinaryOperations extends ATestEdgeCasesBinaryOperations {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLExperimentalQueryJoinExecutor.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLExperimentalQueryJoinExecutor.java
new file mode 100644
index 0000000..728bc6a
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLExperimentalQueryJoinExecutor.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLExperimentalQueryJoinExecutor extends ATestExperimentalQueryJoinExecutor {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLGrandTotalAlongAncestorsComparison.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLGrandTotalAlongAncestorsComparison.java
new file mode 100644
index 0000000..28dfb50
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLGrandTotalAlongAncestorsComparison.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLGrandTotalAlongAncestorsComparison extends ATestGrandTotalAlongAncestorsComparison {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLGrandTotalComparison.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLGrandTotalComparison.java
new file mode 100644
index 0000000..931e56b
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLGrandTotalComparison.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLGrandTotalComparison extends ATestGrandTotalComparison {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLGroupComparison.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLGroupComparison.java
new file mode 100644
index 0000000..827f649
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLGroupComparison.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLGroupComparison extends ATestGroupComparison {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLOrderByFromOrderTable.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLOrderByFromOrderTable.java
new file mode 100644
index 0000000..68f053b
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLOrderByFromOrderTable.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLOrderByFromOrderTable extends ATestOrderByFromOrderTable {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLParentComparison.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLParentComparison.java
new file mode 100644
index 0000000..6f43bb3
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLParentComparison.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLParentComparison extends ATestParentComparison {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLParentComparisonWithOtherColumn.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLParentComparisonWithOtherColumn.java
new file mode 100644
index 0000000..9e329df
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLParentComparisonWithOtherColumn.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLParentComparisonWithOtherColumn extends ATestParentComparisonWithOtherColumn {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLPartialHierarchicalMeasureComparison.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLPartialHierarchicalMeasureComparison.java
new file mode 100644
index 0000000..89009ac
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLPartialHierarchicalMeasureComparison.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLPartialHierarchicalMeasureComparison extends ATestPartialHierarchicalMeasureComparison {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLPeriodComparison.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLPeriodComparison.java
new file mode 100644
index 0000000..bc6356d
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLPeriodComparison.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLPeriodComparison extends ATestPeriodComparison {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLPivotTable.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLPivotTable.java
new file mode 100644
index 0000000..fd29571
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLPivotTable.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLPivotTable extends ATestPivotTable {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLQueryExecutionLocality.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLQueryExecutionLocality.java
new file mode 100644
index 0000000..e2e27fb
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLQueryExecutionLocality.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLQueryExecutionLocality extends ATestQueryExecutionLocality {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLQueryExecutor.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLQueryExecutor.java
new file mode 100644
index 0000000..81f45d9
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLQueryExecutor.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLQueryExecutor extends ATestQueryExecutor {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLQueryWithJoins.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLQueryWithJoins.java
new file mode 100644
index 0000000..29760c2
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLQueryWithJoins.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLQueryWithJoins extends ATestQueryWithJoins {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLSQLFunction.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLSQLFunction.java
new file mode 100644
index 0000000..24e406d
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLSQLFunction.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLSQLFunction extends ATestSQLFunction {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLSubQuery.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLSubQuery.java
new file mode 100644
index 0000000..ed363e2
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLSubQuery.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLSubQuery extends ATestSubQuery {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLVector.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLVector.java
new file mode 100644
index 0000000..9d95be1
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLVector.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLVector extends ATestVector {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLVectorAggregation.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLVectorAggregation.java
new file mode 100644
index 0000000..aff1d90
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLVectorAggregation.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLVectorAggregation extends ATestVectorAggregation {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLVectorAggregationDataType.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLVectorAggregationDataType.java
new file mode 100644
index 0000000..a7941e2
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLVectorAggregationDataType.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLVectorAggregationDataType extends ATestVectorAggregationDataType {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/query/TestPostgreSQLVectorOperation.java b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLVectorOperation.java
new file mode 100644
index 0000000..dd2d86d
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/query/TestPostgreSQLVectorOperation.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQLVectorOperation extends ATestVectorOperation {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}
diff --git a/postgresql/src/test/java/io/squashql/template/PostgreSQLClassTemplateGenerator.java b/postgresql/src/test/java/io/squashql/template/PostgreSQLClassTemplateGenerator.java
new file mode 100644
index 0000000..4beff9a
--- /dev/null
+++ b/postgresql/src/test/java/io/squashql/template/PostgreSQLClassTemplateGenerator.java
@@ -0,0 +1,12 @@
+package io.squashql.template;
+
+import io.squashql.TestClass;
+
+import static io.squashql.template.ClassTemplateGenerator.generateTestClasses;
+
+public class PostgreSQLClassTemplateGenerator {
+
+  public static void main(String[] args) throws Exception {
+    generateTestClasses(TestClass.Type.POSTGRESQL);
+  }
+}
diff --git a/postgresql/src/test/resources/categories.csv b/postgresql/src/test/resources/categories.csv
new file mode 100644
index 0000000..834127a
--- /dev/null
+++ b/postgresql/src/test/resources/categories.csv
@@ -0,0 +1,9 @@
+﻿CategoryID,CategoryName,Description
+1,Beverages,"Soft drinks, coffees, teas, beers, and ales"
+2,Condiments,"Sweet and savory sauces, relishes, spreads, and seasonings"
+3,Confections,"Desserts, candies, and sweet breads"
+4,Dairy Products,Cheeses
+5,Grains/Cereals,"Breads, crackers, pasta, and cereal"
+6,Meat/Poultry,Prepared meats
+7,Produce,Dried fruit and bean curd
+8,Seafood,Seaweed and fish
\ No newline at end of file
diff --git a/postgresql/src/test/resources/customers.csv b/postgresql/src/test/resources/customers.csv
new file mode 100644
index 0000000..6e7f5e0
--- /dev/null
+++ b/postgresql/src/test/resources/customers.csv
@@ -0,0 +1,92 @@
+﻿CustomerID,CustomerName,ContactName,Address,City,PostalCode,Country
+1,Alfreds Futterkiste,Maria Anders,Obere Str. 57,Berlin,12209,Germany
+2,Ana Trujillo Emparedados y helados,Ana Trujillo,Avda. de la Constitución 2222,México D.F.,5021,Mexico
+3,Antonio Moreno Taquería,Antonio Moreno,Mataderos 2312,México D.F.,5023,Mexico
+4,Around the Horn,Thomas Hardy,120 Hanover Sq.,London,WA1 1DP,UK
+5,Berglunds snabbköp,Christina Berglund,Berguvsvägen 8,Luleå,S-958 22,Sweden
+6,Blauer See Delikatessen,Hanna Moos,Forsterstr. 57,Mannheim,68306,Germany
+7,Blondel père et fils,Frédérique Citeaux,"24, place Kléber",Strasbourg,67000,France
+8,Bólido Comidas preparadas,Martín Sommer,"C/ Araquil, 67",Madrid,28023,Spain
+9,Bon app',Laurence Lebihans,"12, rue des Bouchers",Marseille,13008,France
+10,Bottom-Dollar Marketse,Elizabeth Lincoln,23 Tsawassen Blvd.,Tsawassen,T2F 8M4,Canada
+11,B's Beverages,Victoria Ashworth,Fauntleroy Circus,London,EC2 5NT,UK
+12,Cactus Comidas para llevar,Patricio Simpson,Cerrito 333,Buenos Aires,1010,Argentina
+13,Centro comercial Moctezuma,Francisco Chang,Sierras de Granada 9993,México D.F.,5022,Mexico
+14,Chop-suey Chinese,Yang Wang,Hauptstr. 29,Bern,3012,Switzerland
+15,Comércio Mineiro,Pedro Afonso,"Av. dos Lusíadas, 23",São Paulo,05432-043,Brazil
+16,Consolidated Holdings,Elizabeth Brown,Berkeley Gardens 12 Brewery,London,WX1 6LT,UK
+17,Drachenblut Delikatessend,Sven Ottlieb,Walserweg 21,Aachen,52066,Germany
+18,Du monde entier,Janine Labrune,"67, rue des Cinquante Otages",Nantes,44000,France
+19,Eastern Connection,Ann Devon,35 King George,London,WX3 6FW,UK
+20,Ernst Handel,Roland Mendel,Kirchgasse 6,Graz,8010,Austria
+21,Familia Arquibaldo,Aria Cruz,"Rua Orós, 92",São Paulo,05442-030,Brazil
+22,FISSA Fabrica Inter. Salchichas S.A.,Diego Roel,"C/ Moralzarzal, 86",Madrid,28034,Spain
+23,Folies gourmandes,Martine Rancé,"184, chaussée de Tournai",Lille,59000,France
+24,Folk och fä HB,Maria Larsson,Åkergatan 24,Bräcke,S-844 67,Sweden
+25,Frankenversand,Peter Franken,Berliner Platz 43,München,80805,Germany
+26,France restauration,Carine Schmitt,"54, rue Royale",Nantes,44000,France
+27,Franchi S.p.A.,Paolo Accorti,Via Monte Bianco 34,Torino,10100,Italy
+28,Furia Bacalhau e Frutos do Mar,Lino Rodriguez,Jardim das rosas n. 32,Lisboa,1675,Portugal
+29,Galería del gastrónomo,Eduardo Saavedra,"Rambla de Cataluña, 23",Barcelona,8022,Spain
+30,Godos Cocina Típica,José Pedro Freyre,"C/ Romero, 33",Sevilla,41101,Spain
+31,Gourmet Lanchonetes,André Fonseca,"Av. Brasil, 442",Campinas,04876-786,Brazil
+32,Great Lakes Food Market,Howard Snyder,2732 Baker Blvd.,Eugene,97403,USA
+33,GROSELLA-Restaurante,Manuel Pereira,5ª Ave. Los Palos Grandes,Caracas,1081,Venezuela
+34,Hanari Carnes,Mario Pontes,"Rua do Paço, 67",Rio de Janeiro,05454-876,Brazil
+35,HILARIÓN-Abastos,Carlos Hernández,Carrera 22 con Ave. Carlos Soublette #8-35,San Cristóbal,5022,Venezuela
+36,Hungry Coyote Import Store,Yoshi Latimer,City Center Plaza 516 Main St.,Elgin,97827,USA
+37,Hungry Owl All-Night Grocers,Patricia McKenna,8 Johnstown Road,Cork,,Ireland
+38,Island Trading,Helen Bennett,Garden House Crowther Way,Cowes,PO31 7PJ,UK
+39,Königlich Essen,Philip Cramer,Maubelstr. 90,Brandenburg,14776,Germany
+40,La corne d'abondance,Daniel Tonini,"67, avenue de l'Europe",Versailles,78000,France
+41,La maison d'Asie,Annette Roulet,1 rue Alsace-Lorraine,Toulouse,31000,France
+42,Laughing Bacchus Wine Cellars,Yoshi Tannamuri,1900 Oak St.,Vancouver,V3F 2K1,Canada
+43,Lazy K Kountry Store,John Steel,12 Orchestra Terrace,Walla Walla,99362,USA
+44,Lehmanns Marktstand,Renate Messner,Magazinweg 7,Frankfurt a.M.,60528,Germany
+45,Let's Stop N Shop,Jaime Yorres,87 Polk St. Suite 5,San Francisco,94117,USA
+46,LILA-Supermercado,Carlos González,Carrera 52 con Ave. Bolívar #65-98 Llano Largo,Barquisimeto,3508,Venezuela
+47,LINO-Delicateses,Felipe Izquierdo,Ave. 5 de Mayo Porlamar,I. de Margarita,4980,Venezuela
+48,Lonesome Pine Restaurant,Fran Wilson,89 Chiaroscuro Rd.,Portland,97219,USA
+49,Magazzini Alimentari Riuniti,Giovanni Rovelli,Via Ludovico il Moro 22,Bergamo,24100,Italy
+50,Maison Dewey,Catherine Dewey,Rue Joseph-Bens 532,Bruxelles,B-1180,Belgium
+51,Mère Paillarde,Jean Fresnière,43 rue St. Laurent,Montréal,H1J 1C3,Canada
+52,Morgenstern Gesundkost,Alexander Feuer,Heerstr. 22,Leipzig,4179,Germany
+53,North/South,Simon Crowther,South House 300 Queensbridge,London,SW7 1RZ,UK
+54,Océano Atlántico Ltda.,Yvonne Moncada,Ing. Gustavo Moncada 8585 Piso 20-A,Buenos Aires,1010,Argentina
+55,Old World Delicatessen,Rene Phillips,2743 Bering St.,Anchorage,99508,USA
+56,Ottilies Käseladen,Henriette Pfalzheim,Mehrheimerstr. 369,Köln,50739,Germany
+57,Paris spécialités,Marie Bertrand,"265, boulevard Charonne",Paris,75012,France
+58,Pericles Comidas clásicas,Guillermo Fernández,Calle Dr. Jorge Cash 321,México D.F.,5033,Mexico
+59,Piccolo und mehr,Georg Pipps,Geislweg 14,Salzburg,5020,Austria
+60,Princesa Isabel Vinhoss,Isabel de Castro,Estrada da saúde n. 58,Lisboa,1756,Portugal
+61,Que Delícia,Bernardo Batista,"Rua da Panificadora, 12",Rio de Janeiro,02389-673,Brazil
+62,Queen Cozinha,Lúcia Carvalho,"Alameda dos Canàrios, 891",São Paulo,05487-020,Brazil
+63,QUICK-Stop,Horst Kloss,Taucherstraße 10,Cunewalde,1307,Germany
+64,Rancho grande,Sergio Gutiérrez,Av. del Libertador 900,Buenos Aires,1010,Argentina
+65,Rattlesnake Canyon Grocery,Paula Wilson,2817 Milton Dr.,Albuquerque,87110,USA
+66,Reggiani Caseifici,Maurizio Moroni,Strada Provinciale 124,Reggio Emilia,42100,Italy
+67,Ricardo Adocicados,Janete Limeira,"Av. Copacabana, 267",Rio de Janeiro,02389-890,Brazil
+68,Richter Supermarkt,Michael Holz,Grenzacherweg 237,Genève,1203,Switzerland
+69,Romero y tomillo,Alejandra Camino,"Gran Vía, 1",Madrid,28001,Spain
+70,Santé Gourmet,Jonas Bergulfsen,Erling Skakkes gate 78,Stavern,4110,Norway
+71,Save-a-lot Markets,Jose Pavarotti,187 Suffolk Ln.,Boise,83720,USA
+72,Seven Seas Imports,Hari Kumar,90 Wadhurst Rd.,London,OX15 4NB,UK
+73,Simons bistro,Jytte Petersen,Vinbæltet 34,København,1734,Denmark
+74,Spécialités du monde,Dominique Perrier,"25, rue Lauriston",Paris,75016,France
+75,Split Rail Beer & Ale,Art Braunschweiger,P.O. Box 555,Lander,82520,USA
+76,Suprêmes délices,Pascale Cartrain,"Boulevard Tirou, 255",Charleroi,B-6000,Belgium
+77,The Big Cheese,Liz Nixon,89 Jefferson Way Suite 2,Portland,97201,USA
+78,The Cracker Box,Liu Wong,55 Grizzly Peak Rd.,Butte,59801,USA
+79,Toms Spezialitäten,Karin Josephs,Luisenstr. 48,Münster,44087,Germany
+80,Tortuga Restaurante,Miguel Angel Paolino,Avda. Azteca 123,México D.F.,5033,Mexico
+81,Tradição Hipermercados,Anabela Domingues,"Av. Inês de Castro, 414",São Paulo,05634-030,Brazil
+82,Trail's Head Gourmet Provisioners,Helvetius Nagy,722 DaVinci Blvd.,Kirkland,98034,USA
+83,Vaffeljernet,Palle Ibsen,Smagsløget 45,Århus,8200,Denmark
+84,Victuailles en stock,Mary Saveley,"2, rue du Commerce",Lyon,69004,France
+85,Vins et alcools Chevalier,Paul Henriot,59 rue de l'Abbaye,Reims,51100,France
+86,Die Wandernde Kuh,Rita Müller,Adenauerallee 900,Stuttgart,70563,Germany
+87,Wartian Herkku,Pirkko Koskitalo,Torikatu 38,Oulu,90110,Finland
+88,Wellington Importadora,Paula Parente,"Rua do Mercado, 12",Resende,08737-363,Brazil
+89,White Clover Markets,Karl Jablonski,305 - 14th Ave. S. Suite 3B,Seattle,98128,USA
+90,Wilman Kala,Matti Karttunen,Keskuskatu 45,Helsinki,21240,Finland
+91,Wolski,Zbyszek,ul. Filtrowa 68,Walla,01-012,Poland
\ No newline at end of file
diff --git a/postgresql/src/test/resources/order_details.csv b/postgresql/src/test/resources/order_details.csv
new file mode 100644
index 0000000..d6ef4a0
--- /dev/null
+++ b/postgresql/src/test/resources/order_details.csv
@@ -0,0 +1,519 @@
+﻿OrderDetailID,OrderID,ProductID,Quantity
+1,10248,11,12
+2,10248,42,10
+3,10248,72,5
+4,10249,14,9
+5,10249,51,40
+6,10250,41,10
+7,10250,51,35
+8,10250,65,15
+9,10251,22,6
+10,10251,57,15
+11,10251,65,20
+12,10252,20,40
+13,10252,33,25
+14,10252,60,40
+15,10253,31,20
+16,10253,39,42
+17,10253,49,40
+18,10254,24,15
+19,10254,55,21
+20,10254,74,21
+21,10255,2,20
+22,10255,16,35
+23,10255,36,25
+24,10255,59,30
+25,10256,53,15
+26,10256,77,12
+27,10257,27,25
+28,10257,39,6
+29,10257,77,15
+30,10258,2,50
+31,10258,5,65
+32,10258,32,6
+33,10259,21,10
+34,10259,37,1
+35,10260,41,16
+36,10260,57,50
+37,10260,62,15
+38,10260,70,21
+39,10261,21,20
+40,10261,35,20
+41,10262,5,12
+42,10262,7,15
+43,10262,56,2
+44,10263,16,60
+45,10263,24,28
+46,10263,30,60
+47,10263,74,36
+48,10264,2,35
+49,10264,41,25
+50,10265,17,30
+51,10265,70,20
+52,10266,12,12
+53,10267,40,50
+54,10267,59,70
+55,10267,76,15
+56,10268,29,10
+57,10268,72,4
+58,10269,33,60
+59,10269,72,20
+60,10270,36,30
+61,10270,43,25
+62,10271,33,24
+63,10272,20,6
+64,10272,31,40
+65,10272,72,24
+66,10273,10,24
+67,10273,31,15
+68,10273,33,20
+69,10273,40,60
+70,10273,76,33
+71,10274,71,20
+72,10274,72,7
+73,10275,24,12
+74,10275,59,6
+75,10276,10,15
+76,10276,13,10
+77,10277,28,20
+78,10277,62,12
+79,10278,44,16
+80,10278,59,15
+81,10278,63,8
+82,10278,73,25
+83,10279,17,15
+84,10280,24,12
+85,10280,55,20
+86,10280,75,30
+87,10281,19,1
+88,10281,24,6
+89,10281,35,4
+90,10282,30,6
+91,10282,57,2
+92,10283,15,20
+93,10283,19,18
+94,10283,60,35
+95,10283,72,3
+96,10284,27,15
+97,10284,44,21
+98,10284,60,20
+99,10284,67,5
+100,10285,1,45
+101,10285,40,40
+102,10285,53,36
+103,10286,35,100
+104,10286,62,40
+105,10287,16,40
+106,10287,34,20
+107,10287,46,15
+108,10288,54,10
+109,10288,68,3
+110,10289,3,30
+111,10289,64,9
+112,10290,5,20
+113,10290,29,15
+114,10290,49,15
+115,10290,77,10
+116,10291,13,20
+117,10291,44,24
+118,10291,51,2
+119,10292,20,20
+120,10293,18,12
+121,10293,24,10
+122,10293,63,5
+123,10293,75,6
+124,10294,1,18
+125,10294,17,15
+126,10294,43,15
+127,10294,60,21
+128,10294,75,6
+129,10295,56,4
+130,10296,11,12
+131,10296,16,30
+132,10296,69,15
+133,10297,39,60
+134,10297,72,20
+135,10298,2,40
+136,10298,36,40
+137,10298,59,30
+138,10298,62,15
+139,10299,19,15
+140,10299,70,20
+141,10300,66,30
+142,10300,68,20
+143,10301,40,10
+144,10301,56,20
+145,10302,17,40
+146,10302,28,28
+147,10302,43,12
+148,10303,40,40
+149,10303,65,30
+150,10303,68,15
+151,10304,49,30
+152,10304,59,10
+153,10304,71,2
+154,10305,18,25
+155,10305,29,25
+156,10305,39,30
+157,10306,30,10
+158,10306,53,10
+159,10306,54,5
+160,10307,62,10
+161,10307,68,3
+162,10308,69,1
+163,10308,70,5
+164,10309,4,20
+165,10309,6,30
+166,10309,42,2
+167,10309,43,20
+168,10309,71,3
+169,10310,16,10
+170,10310,62,5
+171,10311,42,6
+172,10311,69,7
+173,10312,28,4
+174,10312,43,24
+175,10312,53,20
+176,10312,75,10
+177,10313,36,12
+178,10314,32,40
+179,10314,58,30
+180,10314,62,25
+181,10315,34,14
+182,10315,70,30
+183,10316,41,10
+184,10316,62,70
+185,10317,1,20
+186,10318,41,20
+187,10318,76,6
+188,10319,17,8
+189,10319,28,14
+190,10319,76,30
+191,10320,71,30
+192,10321,35,10
+193,10322,52,20
+194,10323,15,5
+195,10323,25,4
+196,10323,39,4
+197,10324,16,21
+198,10324,35,70
+199,10324,46,30
+200,10324,59,40
+201,10324,63,80
+202,10325,6,6
+203,10325,13,12
+204,10325,14,9
+205,10325,31,4
+206,10325,72,40
+207,10326,4,24
+208,10326,57,16
+209,10326,75,50
+210,10327,2,25
+211,10327,11,50
+212,10327,30,35
+213,10327,58,30
+214,10328,59,9
+215,10328,65,40
+216,10328,68,10
+217,10329,19,10
+218,10329,30,8
+219,10329,38,20
+220,10329,56,12
+221,10330,26,50
+222,10330,72,25
+223,10331,54,15
+224,10332,18,40
+225,10332,42,10
+226,10332,47,16
+227,10333,14,10
+228,10333,21,10
+229,10333,71,40
+230,10334,52,8
+231,10334,68,10
+232,10335,2,7
+233,10335,31,25
+234,10335,32,6
+235,10335,51,48
+236,10336,4,18
+237,10337,23,40
+238,10337,26,24
+239,10337,36,20
+240,10337,37,28
+241,10337,72,25
+242,10338,17,20
+243,10338,30,15
+244,10339,4,10
+245,10339,17,70
+246,10339,62,28
+247,10340,18,20
+248,10340,41,12
+249,10340,43,40
+250,10341,33,8
+251,10341,59,9
+252,10342,2,24
+253,10342,31,56
+254,10342,36,40
+255,10342,55,40
+256,10343,64,50
+257,10343,68,4
+258,10343,76,15
+259,10344,4,35
+260,10344,8,70
+261,10345,8,70
+262,10345,19,80
+263,10345,42,9
+264,10346,17,36
+265,10346,56,20
+266,10347,25,10
+267,10347,39,50
+268,10347,40,4
+269,10347,75,6
+270,10348,1,15
+271,10348,23,25
+272,10349,54,24
+273,10350,50,15
+274,10350,69,18
+275,10351,38,20
+276,10351,41,13
+277,10351,44,77
+278,10351,65,10
+279,10352,24,10
+280,10352,54,20
+281,10353,11,12
+282,10353,38,50
+283,10354,1,12
+284,10354,29,4
+285,10355,24,25
+286,10355,57,25
+287,10356,31,30
+288,10356,55,12
+289,10356,69,20
+290,10357,10,30
+291,10357,26,16
+292,10357,60,8
+293,10358,24,10
+294,10358,34,10
+295,10358,36,20
+296,10359,16,56
+297,10359,31,70
+298,10359,60,80
+299,10360,28,30
+300,10360,29,35
+301,10360,38,10
+302,10360,49,35
+303,10360,54,28
+304,10361,39,54
+305,10361,60,55
+306,10362,25,50
+307,10362,51,20
+308,10362,54,24
+309,10363,31,20
+310,10363,75,12
+311,10363,76,12
+312,10364,69,30
+313,10364,71,5
+314,10365,11,24
+315,10366,65,5
+316,10366,77,5
+317,10367,34,36
+318,10367,54,18
+319,10367,65,15
+320,10367,77,7
+321,10368,21,5
+322,10368,28,13
+323,10368,57,25
+324,10368,64,35
+325,10369,29,20
+326,10369,56,18
+327,10370,1,15
+328,10370,64,30
+329,10370,74,20
+330,10371,36,6
+331,10372,20,12
+332,10372,38,40
+333,10372,60,70
+334,10372,72,42
+335,10373,58,80
+336,10373,71,50
+337,10374,31,30
+338,10374,58,15
+339,10375,14,15
+340,10375,54,10
+341,10376,31,42
+342,10377,28,20
+343,10377,39,20
+344,10378,71,6
+345,10379,41,8
+346,10379,63,16
+347,10379,65,20
+348,10380,30,18
+349,10380,53,20
+350,10380,60,6
+351,10380,70,30
+352,10381,74,14
+353,10382,5,32
+354,10382,18,9
+355,10382,29,14
+356,10382,33,60
+357,10382,74,50
+358,10383,13,20
+359,10383,50,15
+360,10383,56,20
+361,10384,20,28
+362,10384,60,15
+363,10385,7,10
+364,10385,60,20
+365,10385,68,8
+366,10386,24,15
+367,10386,34,10
+368,10387,24,15
+369,10387,28,6
+370,10387,59,12
+371,10387,71,15
+372,10388,45,15
+373,10388,52,20
+374,10388,53,40
+375,10389,10,16
+376,10389,55,15
+377,10389,62,20
+378,10389,70,30
+379,10390,31,60
+380,10390,35,40
+381,10390,46,45
+382,10390,72,24
+383,10391,13,18
+384,10392,69,50
+385,10393,2,25
+386,10393,14,42
+387,10393,25,7
+388,10393,26,70
+389,10393,31,32
+390,10394,13,10
+391,10394,62,10
+392,10395,46,28
+393,10395,53,70
+394,10395,69,8
+395,10396,23,40
+396,10396,71,60
+397,10396,72,21
+398,10397,21,10
+399,10397,51,18
+400,10398,35,30
+401,10398,55,120
+402,10399,68,60
+403,10399,71,30
+404,10399,76,35
+405,10399,77,14
+406,10400,29,21
+407,10400,35,35
+408,10400,49,30
+409,10401,30,18
+410,10401,56,70
+411,10401,65,20
+412,10401,71,60
+413,10402,23,60
+414,10402,63,65
+415,10403,16,21
+416,10403,48,70
+417,10404,26,30
+418,10404,42,40
+419,10404,49,30
+420,10405,3,50
+421,10406,1,10
+422,10406,21,30
+423,10406,28,42
+424,10406,36,5
+425,10406,40,2
+426,10407,11,30
+427,10407,69,15
+428,10407,71,15
+429,10408,37,10
+430,10408,54,6
+431,10408,62,35
+432,10409,14,12
+433,10409,21,12
+434,10410,33,49
+435,10410,59,16
+436,10411,41,25
+437,10411,44,40
+438,10411,59,9
+439,10412,14,20
+440,10413,1,24
+441,10413,62,40
+442,10413,76,14
+443,10414,19,18
+444,10414,33,50
+445,10415,17,2
+446,10415,33,20
+447,10416,19,20
+448,10416,53,10
+449,10416,57,20
+450,10417,38,50
+451,10417,46,2
+452,10417,68,36
+453,10417,77,35
+454,10418,2,60
+455,10418,47,55
+456,10418,61,16
+457,10418,74,15
+458,10419,60,60
+459,10419,69,20
+460,10420,9,20
+461,10420,13,2
+462,10420,70,8
+463,10420,73,20
+464,10421,19,4
+465,10421,26,30
+466,10421,53,15
+467,10421,77,10
+468,10422,26,2
+469,10423,31,14
+470,10423,59,20
+471,10424,35,60
+472,10424,38,49
+473,10424,68,30
+474,10425,55,10
+475,10425,76,20
+476,10426,56,5
+477,10426,64,7
+478,10427,14,35
+479,10428,46,20
+480,10429,50,40
+481,10429,63,35
+482,10430,17,45
+483,10430,21,50
+484,10430,56,30
+485,10430,59,70
+486,10431,17,50
+487,10431,40,50
+488,10431,47,30
+489,10432,26,10
+490,10432,54,40
+491,10433,56,28
+492,10434,11,6
+493,10434,76,18
+494,10435,2,10
+495,10435,22,12
+496,10435,72,10
+497,10436,46,5
+498,10436,56,40
+499,10436,64,30
+500,10436,75,24
+501,10437,53,15
+502,10438,19,15
+503,10438,34,20
+504,10438,57,15
+505,10439,12,15
+506,10439,16,16
+507,10439,64,6
+508,10439,74,30
+509,10440,2,45
+510,10440,16,49
+511,10440,29,24
+512,10440,61,90
+513,10441,27,50
+514,10442,11,30
+515,10442,54,80
+516,10442,66,60
+517,10443,11,6
+518,10443,28,12
\ No newline at end of file
diff --git a/postgresql/src/test/resources/orders.csv b/postgresql/src/test/resources/orders.csv
new file mode 100644
index 0000000..25fe915
--- /dev/null
+++ b/postgresql/src/test/resources/orders.csv
@@ -0,0 +1,197 @@
+﻿OrderID,CustomerID,EmployeeID,OrderDate,ShipperID
+10248,90,5,04/07/1996,3
+10249,81,6,05/07/1996,1
+10250,34,4,08/07/1996,2
+10251,84,3,08/07/1996,1
+10252,76,4,09/07/1996,2
+10253,34,3,10/07/1996,2
+10254,14,5,11/07/1996,2
+10255,68,9,12/07/1996,3
+10256,88,3,15/07/1996,2
+10257,35,4,16/07/1996,3
+10258,20,1,17/07/1996,1
+10259,13,4,18/07/1996,3
+10260,55,4,19/07/1996,1
+10261,61,4,19/07/1996,2
+10262,65,8,22/07/1996,3
+10263,20,9,23/07/1996,3
+10264,24,6,24/07/1996,3
+10265,7,2,25/07/1996,1
+10266,87,3,26/07/1996,3
+10267,25,4,29/07/1996,1
+10268,33,8,30/07/1996,3
+10269,89,5,31/07/1996,1
+10270,87,1,01/08/1996,1
+10271,75,6,01/08/1996,2
+10272,65,6,02/08/1996,2
+10273,63,3,05/08/1996,3
+10274,85,6,06/08/1996,1
+10275,49,1,07/08/1996,1
+10276,80,8,08/08/1996,3
+10277,52,2,09/08/1996,3
+10278,5,8,12/08/1996,2
+10279,44,8,13/08/1996,2
+10280,5,2,14/08/1996,1
+10281,69,4,14/08/1996,1
+10282,69,4,15/08/1996,1
+10283,46,3,16/08/1996,3
+10284,44,4,19/08/1996,1
+10285,63,1,20/08/1996,2
+10286,63,8,21/08/1996,3
+10287,67,8,22/08/1996,3
+10288,66,4,23/08/1996,1
+10289,11,7,26/08/1996,3
+10290,15,8,27/08/1996,1
+10291,61,6,27/08/1996,2
+10292,81,1,28/08/1996,2
+10293,80,1,29/08/1996,3
+10294,65,4,30/08/1996,2
+10295,85,2,02/09/1996,2
+10296,46,6,03/09/1996,1
+10297,7,5,04/09/1996,2
+10298,37,6,05/09/1996,2
+10299,67,4,06/09/1996,2
+10300,49,2,09/09/1996,2
+10301,86,8,09/09/1996,2
+10302,76,4,10/09/1996,2
+10303,30,7,11/09/1996,2
+10304,80,1,12/09/1996,2
+10305,55,8,13/09/1996,3
+10306,69,1,16/09/1996,3
+10307,48,2,17/09/1996,2
+10308,2,7,18/09/1996,3
+10309,37,3,19/09/1996,1
+10310,77,8,20/09/1996,2
+10311,18,1,20/09/1996,3
+10312,86,2,23/09/1996,2
+10313,63,2,24/09/1996,2
+10314,65,1,25/09/1996,2
+10315,38,4,26/09/1996,2
+10316,65,1,27/09/1996,3
+10317,48,6,30/09/1996,1
+10318,38,8,01/10/1996,2
+10319,80,7,02/10/1996,3
+10320,87,5,03/10/1996,3
+10321,38,3,03/10/1996,2
+10322,58,7,04/10/1996,3
+10323,39,4,07/10/1996,1
+10324,71,9,08/10/1996,1
+10325,39,1,09/10/1996,3
+10326,8,4,10/10/1996,2
+10327,24,2,11/10/1996,1
+10328,28,4,14/10/1996,3
+10329,75,4,15/10/1996,2
+10330,46,3,16/10/1996,1
+10331,9,9,16/10/1996,1
+10332,51,3,17/10/1996,2
+10333,87,5,18/10/1996,3
+10334,84,8,21/10/1996,2
+10335,37,7,22/10/1996,2
+10336,60,7,23/10/1996,2
+10337,25,4,24/10/1996,3
+10338,55,4,25/10/1996,3
+10339,51,2,28/10/1996,2
+10340,9,1,29/10/1996,3
+10341,73,7,29/10/1996,3
+10342,25,4,30/10/1996,2
+10343,44,4,31/10/1996,1
+10344,89,4,01/11/1996,2
+10345,63,2,04/11/1996,2
+10346,65,3,05/11/1996,3
+10347,21,4,06/11/1996,3
+10348,86,4,07/11/1996,2
+10349,75,7,08/11/1996,1
+10350,41,6,11/11/1996,2
+10351,20,1,11/11/1996,1
+10352,28,3,12/11/1996,3
+10353,59,7,13/11/1996,3
+10354,58,8,14/11/1996,3
+10355,4,6,15/11/1996,1
+10356,86,6,18/11/1996,2
+10357,46,1,19/11/1996,3
+10358,41,5,20/11/1996,1
+10359,72,5,21/11/1996,3
+10360,7,4,22/11/1996,3
+10361,63,1,22/11/1996,2
+10362,9,3,25/11/1996,1
+10363,17,4,26/11/1996,3
+10364,19,1,26/11/1996,1
+10365,3,3,27/11/1996,2
+10366,29,8,28/11/1996,2
+10367,83,7,28/11/1996,3
+10368,20,2,29/11/1996,2
+10369,75,8,02/12/1996,2
+10370,14,6,03/12/1996,2
+10371,41,1,03/12/1996,1
+10372,62,5,04/12/1996,2
+10373,37,4,05/12/1996,3
+10374,91,1,05/12/1996,3
+10375,36,3,06/12/1996,2
+10376,51,1,09/12/1996,2
+10377,72,1,09/12/1996,3
+10378,24,5,10/12/1996,3
+10379,61,2,11/12/1996,1
+10380,37,8,12/12/1996,3
+10381,46,3,12/12/1996,3
+10382,20,4,13/12/1996,1
+10383,4,8,16/12/1996,3
+10384,5,3,16/12/1996,3
+10385,75,1,17/12/1996,2
+10386,21,9,18/12/1996,3
+10387,70,1,18/12/1996,2
+10388,72,2,19/12/1996,1
+10389,10,4,20/12/1996,2
+10390,20,6,23/12/1996,1
+10391,17,3,23/12/1996,3
+10392,59,2,24/12/1996,3
+10393,71,1,25/12/1996,3
+10394,36,1,25/12/1996,3
+10395,35,6,26/12/1996,1
+10396,25,1,27/12/1996,3
+10397,60,5,27/12/1996,1
+10398,71,2,30/12/1996,3
+10399,83,8,31/12/1996,3
+10400,19,1,01/01/1997,3
+10401,65,1,01/01/1997,1
+10402,20,8,02/01/1997,2
+10403,20,4,03/01/1997,3
+10404,49,2,03/01/1997,1
+10405,47,1,06/01/1997,1
+10406,62,7,07/01/1997,1
+10407,56,2,07/01/1997,2
+10408,23,8,08/01/1997,1
+10409,54,3,09/01/1997,1
+10410,10,3,10/01/1997,3
+10411,10,9,10/01/1997,3
+10412,87,8,13/01/1997,2
+10413,41,3,14/01/1997,2
+10414,21,2,14/01/1997,3
+10415,36,3,15/01/1997,1
+10416,87,8,16/01/1997,3
+10417,73,4,16/01/1997,3
+10418,63,4,17/01/1997,1
+10419,68,4,20/01/1997,2
+10420,88,3,21/01/1997,1
+10421,61,8,21/01/1997,1
+10422,27,2,22/01/1997,1
+10423,31,6,23/01/1997,3
+10424,51,7,23/01/1997,2
+10425,41,6,24/01/1997,2
+10426,29,4,27/01/1997,1
+10427,59,4,27/01/1997,2
+10428,66,7,28/01/1997,1
+10429,37,3,29/01/1997,2
+10430,20,4,30/01/1997,1
+10431,10,4,30/01/1997,2
+10432,75,3,31/01/1997,2
+10433,60,3,03/02/1997,3
+10434,24,3,03/02/1997,2
+10435,16,8,04/02/1997,2
+10436,7,3,05/02/1997,2
+10437,87,8,05/02/1997,1
+10438,79,3,06/02/1997,2
+10439,51,6,07/02/1997,3
+10440,71,4,10/02/1997,2
+10441,55,3,10/02/1997,2
+10442,20,3,11/02/1997,2
+10443,66,8,12/02/1997,1
\ No newline at end of file
diff --git a/postgresql/src/test/resources/products.csv b/postgresql/src/test/resources/products.csv
new file mode 100644
index 0000000..d41649f
--- /dev/null
+++ b/postgresql/src/test/resources/products.csv
@@ -0,0 +1,78 @@
+﻿ProductID,ProductName,SupplierID,CategoryID,Unit,Price
+1,Chais,1,1,10 boxes x 20 bags,18
+2,Chang,1,1,24 - 12 oz bottles,19
+3,Aniseed Syrup,1,2,12 - 550 ml bottles,10
+4,Chef Anton's Cajun Seasoning,2,2,48 - 6 oz jars,22
+5,Chef Anton's Gumbo Mix,2,2,36 boxes,21.35
+6,Grandma's Boysenberry Spread,3,2,12 - 8 oz jars,25
+7,Uncle Bob's Organic Dried Pears,3,7,12 - 1 lb pkgs.,30
+8,Northwoods Cranberry Sauce,3,2,12 - 12 oz jars,40
+9,Mishi Kobe Niku,4,6,18 - 500 g pkgs.,97
+10,Ikura,4,8,12 - 200 ml jars,31
+11,Queso Cabrales,5,4,1 kg pkg.,21
+12,Queso Manchego La Pastora,5,4,10 - 500 g pkgs.,38
+13,Konbu,6,8,2 kg box,6
+14,Tofu,6,7,40 - 100 g pkgs.,23.25
+15,Genen Shouyu,6,2,24 - 250 ml bottles,15.5
+16,Pavlova,7,3,32 - 500 g boxes,17.45
+17,Alice Mutton,7,6,20 - 1 kg tins,39
+18,Carnarvon Tigers,7,8,16 kg pkg.,62.5
+19,Teatime Chocolate Biscuits,8,3,10 boxes x 12 pieces,9.2
+20,Sir Rodney's Marmalade,8,3,30 gift boxes,81
+21,Sir Rodney's Scones,8,3,24 pkgs. x 4 pieces,10
+22,Gustaf's Knäckebröd,9,5,24 - 500 g pkgs.,21
+23,Tunnbröd,9,5,12 - 250 g pkgs.,9
+24,Guaraná Fantástica,10,1,12 - 355 ml cans,4.5
+25,NuNuCa Nuß-Nougat-Creme,11,3,20 - 450 g glasses,14
+26,Gumbär Gummibärchen,11,3,100 - 250 g bags,31.23
+27,Schoggi Schokolade,11,3,100 - 100 g pieces,43.9
+28,Rössle Sauerkraut,12,7,25 - 825 g cans,45.6
+29,Thüringer Rostbratwurst,12,6,50 bags x 30 sausgs.,123.79
+30,Nord-Ost Matjeshering,13,8,10 - 200 g glasses,25.89
+31,Gorgonzola Telino,14,4,12 - 100 g pkgs,12.5
+32,Mascarpone Fabioli,14,4,24 - 200 g pkgs.,32
+33,Geitost,15,4,500 g,2.5
+34,Sasquatch Ale,16,1,24 - 12 oz bottles,14
+35,Steeleye Stout,16,1,24 - 12 oz bottles,18
+36,Inlagd Sill,17,8,24 - 250 g jars,19
+37,Gravad lax,17,8,12 - 500 g pkgs.,26
+38,Côte de Blaye,18,1,12 - 75 cl bottles,263.5
+39,Chartreuse verte,18,1,750 cc per bottle,18
+40,Boston Crab Meat,19,8,24 - 4 oz tins,18.4
+41,Jack's New England Clam Chowder,19,8,12 - 12 oz cans,9.65
+42,Singaporean Hokkien Fried Mee,20,5,32 - 1 kg pkgs.,14
+43,Ipoh Coffee,20,1,16 - 500 g tins,46
+44,Gula Malacca,20,2,20 - 2 kg bags,19.45
+45,Røgede sild,21,8,1k pkg.,9.5
+46,Spegesild,21,8,4 - 450 g glasses,12
+47,Zaanse koeken,22,3,10 - 4 oz boxes,9.5
+48,Chocolade,22,3,10 pkgs.,12.75
+49,Maxilaku,23,3,24 - 50 g pkgs.,20
+50,Valkoinen suklaa,23,3,12 - 100 g bars,16.25
+51,Manjimup Dried Apples,24,7,50 - 300 g pkgs.,53
+52,Filo Mix,24,5,16 - 2 kg boxes,7
+53,Perth Pasties,24,6,48 pieces,32.8
+54,Tourtière,25,6,16 pies,7.45
+55,Pâté chinois,25,6,24 boxes x 2 pies,24
+56,Gnocchi di nonna Alice,26,5,24 - 250 g pkgs.,38
+57,Ravioli Angelo,26,5,24 - 250 g pkgs.,19.5
+58,Escargots de Bourgogne,27,8,24 pieces,13.25
+59,Raclette Courdavault,28,4,5 kg pkg.,55
+60,Camembert Pierrot,28,4,15 - 300 g rounds,34
+61,Sirop d'érable,29,2,24 - 500 ml bottles,28.5
+62,Tarte au sucre,29,3,48 pies,49.3
+63,Vegie-spread,7,2,15 - 625 g jars,43.9
+64,Wimmers gute Semmelknödel,12,5,20 bags x 4 pieces,33.25
+65,Louisiana Fiery Hot Pepper Sauce,2,2,32 - 8 oz bottles,21.05
+66,Louisiana Hot Spiced Okra,2,2,24 - 8 oz jars,17
+67,Laughing Lumberjack Lager,16,1,24 - 12 oz bottles,14
+68,Scottish Longbreads,8,3,10 boxes x 8 pieces,12.5
+69,Gudbrandsdalsost,15,4,10 kg pkg.,36
+70,Outback Lager,7,1,24 - 355 ml bottles,15
+71,Fløtemysost,15,4,10 - 500 g pkgs.,21.5
+72,Mozzarella di Giovanni,14,4,24 - 200 g pkgs.,34.8
+73,Röd Kaviar,17,8,24 - 150 g jars,15
+74,Longlife Tofu,4,7,5 kg pkg.,10
+75,Rhönbräu Klosterbier,12,1,24 - 0.5 l bottles,7.75
+76,Lakkalikööri,23,1,500 ml,18
+77,Original Frankfurter grüne Soße,12,2,12 boxes,13
\ No newline at end of file
diff --git a/postgresql/src/test/resources/shippers.csv b/postgresql/src/test/resources/shippers.csv
new file mode 100644
index 0000000..b0e9775
--- /dev/null
+++ b/postgresql/src/test/resources/shippers.csv
@@ -0,0 +1,4 @@
+﻿ShipperID,ShipperName,Phone
+1,Speedy Express,(503) 555-9831
+2,United Package,(503) 555-3199
+3,Federal Shipping,(503) 555-9931
\ No newline at end of file
diff --git a/postgresql/src/test/resources/suppliers.csv b/postgresql/src/test/resources/suppliers.csv
new file mode 100644
index 0000000..be80e7a
--- /dev/null
+++ b/postgresql/src/test/resources/suppliers.csv
@@ -0,0 +1,30 @@
+﻿SupplierID,SupplierName,ContactName,Address,City,PostalCode,Country,Phone
+1,Exotic Liquid,Charlotte Cooper,49 Gilbert St.,Londona,EC1 4SD,UK,(171) 555-2222
+2,New Orleans Cajun Delights,Shelley Burke,P.O. Box 78934,New Orleans,70117,USA,(100) 555-4822
+3,Grandma Kelly's Homestead,Regina Murphy,707 Oxford Rd.,Ann Arbor,48104,USA,(313) 555-5735
+4,Tokyo Traders,Yoshi Nagase,9-8 Sekimai Musashino-shi,Tokyo,100,Japan,(03) 3555-5011
+5,Cooperativa de Quesos 'Las Cabras',Antonio del Valle Saavedra,Calle del Rosal 4,Oviedo,33007,Spain,(98) 598 76 54
+6,Mayumi's,Mayumi Ohno,92 Setsuko Chuo-ku,Osaka,545,Japan,(06) 431-7877
+7,"Pavlova, Ltd.",Ian Devling,74 Rose St. Moonie Ponds,Melbourne,3058,Australia,(03) 444-2343
+8,"Specialty Biscuits, Ltd.",Peter Wilson,29 King's Way,Manchester,M14 GSD,UK,(161) 555-4448
+9,PB Knäckebröd AB,Lars Peterson,Kaloadagatan 13,Göteborg,S-345 67,Sweden,031-987 65 43
+10,Refrescos Americanas LTDA,Carlos Diaz,Av. das Americanas 12.890,São Paulo,5442,Brazil,(11) 555 4640
+11,Heli Süßwaren GmbH & Co. KG,Petra Winkler,Tiergartenstraße 5,Berlin,10785,Germany,(010) 9984510
+12,Plutzer Lebensmittelgroßmärkte AG,Martin Bein,Bogenallee 51,Frankfurt,60439,Germany,(069) 992755
+13,Nord-Ost-Fisch Handelsgesellschaft mbH,Sven Petersen,Frahmredder 112a,Cuxhaven,27478,Germany,(04721) 8713
+14,Formaggi Fortini s.r.l.,Elio Rossi,"Viale Dante, 75",Ravenna,48100,Italy,(0544) 60323
+15,Norske Meierier,Beate Vileid,Hatlevegen 5,Sandvika,1320,Norway,(0)2-953010
+16,Bigfoot Breweries,Cheryl Saylor,3400 - 8th Avenue Suite 210,Bend,97101,USA,(503) 555-9931
+17,Svensk Sjöföda AB,Michael Björn,Brovallavägen 231,Stockholm,S-123 45,Sweden,08-123 45 67
+18,Aux joyeux ecclésiastiques,Guylène Nodier,"203, Rue des Francs-Bourgeois",Paris,75004,France,(1) 03.83.00.68
+19,New England Seafood Cannery,Robb Merchant,Order Processing Dept. 2100 Paul Revere Blvd.,Boston,2134,USA,(617) 555-3267
+20,Leka Trading,Chandra Leka,"471 Serangoon Loop, Suite #402",Singapore,512,Singapore,555-8787
+21,Lyngbysild,Niels Petersen,Lyngbysild Fiskebakken 10,Lyngby,2800,Denmark,43844108
+22,Zaanse Snoepfabriek,Dirk Luchte,Verkoop Rijnweg 22,Zaandam,9999 ZZ,Netherlands,(12345) 1212
+23,Karkki Oy,Anne Heikkonen,Valtakatu 12,Lappeenranta,53120,Finland,(953) 10956
+24,"G'day, Mate",Wendy Mackenzie,170 Prince Edward Parade Hunter's Hill,Sydney,2042,Australia,(02) 555-5914
+25,Ma Maison,Jean-Guy Lauzon,2960 Rue St. Laurent,Montréal,H1J 1C3,Canada,(514) 555-9022
+26,Pasta Buttini s.r.l.,Giovanni Giudici,"Via dei Gelsomini, 153",Salerno,84100,Italy,(089) 6547665
+27,Escargots Nouveaux,Marie Delamare,"22, rue H. Voiron",Montceau,71300,France,85.57.00.07
+28,Gai pâturage,Eliane Noz,"Bat. B 3, rue des Alpes",Annecy,74000,France,38.76.98.06
+29,Forêts d'érables,Chantal Goulet,148 rue Chasseur,Ste-Hyacinthe,J2S 7S8,Canada,(514) 555-2955
\ No newline at end of file
diff --git a/postgresql/src/test/resources/templates/TestPostgreSQLTemplate.template.java b/postgresql/src/test/resources/templates/TestPostgreSQLTemplate.template.java
new file mode 100644
index 0000000..89438f8
--- /dev/null
+++ b/postgresql/src/test/resources/templates/TestPostgreSQLTemplate.template.java
@@ -0,0 +1,57 @@
+package io.squashql.query;
+
+import io.squashql.PostgreSQLDatastore;
+import io.squashql.query.database.PostgreSQLQueryEngine;
+import io.squashql.query.database.QueryEngine;
+import io.squashql.store.Datastore;
+import io.squashql.template.PostgreSQLClassTemplateGenerator;
+import io.squashql.transaction.DataLoader;
+import io.squashql.transaction.PostgreSQLDataLoader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.PostgreSQLContainer;
+
+import static io.squashql.query.PostgreSQLTestUtil.TEST_PROPERTIES;
+import static io.squashql.query.PostgreSQLTestUtil.createContainer;
+
+/**
+ * Do not edit this class, it has been generated automatically by {@link PostgreSQLClassTemplateGenerator}.
+ */
+public class TestPostgreSQL{{classSuffix}} extends {{parentTestClass}} {
+
+  private final PostgreSQLContainer<?> container = createContainer();
+
+  @BeforeAll
+  @Override
+  void setup() {
+    this.container.start();
+    super.setup();
+  }
+
+  @AfterAll
+  void tearDown() {
+    // we do not stop the container to be able to reuse it between tests.
+    ((PostgreSQLDataLoader) this.tm).dropTables(this.fieldsByStore.keySet());
+  }
+
+  @Override
+  protected void createTables() {
+    PostgreSQLDataLoader dl = (PostgreSQLDataLoader) this.tm;
+    this.fieldsByStore.forEach(dl::dropAndCreateInMemoryTable);
+  }
+
+  @Override
+  protected QueryEngine<?> createQueryEngine(Datastore datastore) {
+    return new PostgreSQLQueryEngine((PostgreSQLDatastore) datastore);
+  }
+
+  @Override
+  protected Datastore createDatastore() {
+    return new PostgreSQLDatastore(this.container.getJdbcUrl(), TEST_PROPERTIES);
+  }
+
+  @Override
+  protected DataLoader createDataLoader() {
+      return new PostgreSQLDataLoader((PostgreSQLDatastore) this.datastore);
+    }
+}