Concat() expression with string literals generates untyped parameters ($N) in PostgreSQL prepared statements – "could not determine data type of parameter"

Environment

  • Metabase version: 0.63.2.7 (self-hosted, JAR on Windows Server 2022)

  • PostgreSQL version: 17

  • JDBC driver: bundled with Metabase


Description

When using the concat() expression in the query builder with string literals as separators, Metabase generates SQL with untyped prepared statement parameters ($9, $10, etc.) that PostgreSQL cannot resolve. This causes the following error in the PostgreSQL log:

ERROR: could not determine data type of parameter $9

Example expression that causes the error:

concat([Created Year], "-", [Created Tertial])

Generated SQL (from PostgreSQL log):

sql

CONCAT(CAST(extract(year FROM "created_col4") AS integer), $9, 
  CASE WHEN get_month("created_col4") <= 4 THEN $10 
       WHEN get_month("created_col4") <= 8 THEN $11 
       ELSE $12 END)

The string literals "-", "T1", "T2", "T3" are sent as untyped parameters. PostgreSQL cannot infer their type when using prepared statements.

The error also occurs with more complex expressions like:

concat("/", [Category 1], "/", [Category 2], "/", [Category 3])

where the "/" separators become $9, $11, $13 etc.


Impact

We have 10 models and 2 questions affected. All cards depending on these models (553+ cards) fail intermittently. Several wallboard dashboards auto-refresh every 30 seconds and generate this error continuously, filling the PostgreSQL log.


What we tried

  1. Wrapping first argument with text(): concat(text([Created Year]), "-", [Created Tertial]) Result: First argument gets CAST(... AS TEXT) but separator "-" is still untyped as $114.

  2. Nesting concat to force type inference: concat(text([Created Year]), concat("-", [Created Tertial])) Result: Separator is still the first argument in the inner CONCAT and remains untyped.

  3. Setting prepareThreshold=0 in additional-options: Saved correctly in metabase_database.details, Metabase restarted. Result: No effect – prepared statements still used.

  4. Setting prepareThreshold=0&preferQueryMode=simple: Result: Metabase sent queries using ? placeholders (MySQL syntax) instead of $N, causing a new PostgreSQL error: syntax error at or near "WHEN". Rolled back.

  5. Setting prepareThreshold=0&preparedStatementCacheQueries=0: Not yet tested.

  6. Direct JSON update of dataset_query in the application database to restructure expressions – same result as UI changes.

  7. ALTER ROLE ... SET plan_cache_mode = 'force_generic_plan' on the PostgreSQL server – not possible, no server admin access.

Please file a bug on this in the project GitHub.

What is the source database or source of the columns? I did a simple test with concat of two text fields with a string separator and it worked fine, though I am on 0.62 with a PG 18 database. It makes a difference if the columns are straight out of a table or derived in some way (previous custom column, model, etc.).