Skip to content

[Bug] PostgreSQL: UPDATE ... FROM (VALUES ...) AS t(col_list) fails with "Unknown column 't.X' in 'unknown clause'" #38643

@adsk-duf

Description

@adsk-duf

Bug Report

For English only, other languages will not accept.

Before report a bug, make sure you have:

Please pay attention on issues you submitted, because we maybe need more details.
If no response anymore and we cannot reproduce it on current information, we will close it.

Please answer these questions before submitting your issue. Thanks!

Which version of ShardingSphere did you use?

5.5.3 (Proxy, frontend = PostgreSQL, backend = PostgreSQL 15+)

Which project did you use? ShardingSphere-JDBC or ShardingSphere-Proxy?

ShardingSphere-Proxy

Expected behavior

The PostgreSQL-valid statement below should pass SQL binding and execute on the backend.

  UPDATE t1 f
  SET col_a = CASE WHEN t.col_a_type = 1 THEN t.col_a ELSE f.col_a END
  FROM (VALUES ('k1', 'v1', 1), ('k2', 'v2', 0)) AS t(pk, col_a, col_a_type)
  WHERE t.pk = f.pk;

Actual behavior

Binding fails with:
  Unknown column 't.col_a_type' in 'unknown clause'.
The same SQL runs fine when sent directly to PostgreSQL.

Reason analyze (If you can)

Root cause is in the PostgreSQL parser + binder interaction:

  1. parser/sql/engine/dialect/postgresql/.../PostgreSQLStatementVisitor.java#visitAliasClause flattens the alias column list into the alias name string rather than extracting it as individual column aliases:

  public ASTNode visitAliasClause(final AliasClauseContext ctx) {
      StringBuilder aliasName = new StringBuilder(ctx.colId().getText());
      if (null != ctx.nameList()) {
          aliasName.append(ctx.LP_().getText());
          aliasName.append(ctx.nameList().getText());
          aliasName.append(ctx.RP_().getText());
      }
      return new AliasSegment(..., new IdentifierValue(aliasName.toString()));
  }

  So AS t(pk, col_a, col_a_type) is parsed as a single alias name literally "t(pk,col_a,col_a_type)".

  2. SubqueryTableSegmentBinder.bind registers the derived-table context keyed by that full string (CaseInsensitiveString.of("t(pk,col_a,col_a_type)")). Subsequent references to owner t in the SET/WHERE clauses cannot find a match.
  3. Column resolution falls through all binder contexts and throws ColumnNotFoundException. Because the SET clause's parentSegmentType == SET_ASSIGNMENT is not in ColumnSegmentBinder.SEGMENT_TYPE_MESSAGES, the message label defaults to
  "unknown clause" — producing the confusing error text.

  Secondary (related) gap: visitSimpleSelect only fills projections from targetList(); the valuesClause alternative of simpleSelect is ignored, so a parenthesized (VALUES ...) subquery yields an empty ProjectionsSegment regardless.

  Reason analyze (summary)
  - PG parser does not surface the column-list of derived-table aliases as separate column aliases.
  - VALUES clause inside a subquery table yields no projections.
  - Combined, (VALUES ...) AS alias(col_list) references like alias.col are unresolvable during binding.

Steps to reproduce the behavior, such as: SQL to execute, sharding rule configuration, when exception occur etc.

  1. Stand up SS Proxy with PostgreSQL frontend/backend.
  2. Create a sharded table t1(pk, col_a) with sharding on pk.
  3. Run the SQL above.

Example codes for reproduce this issue (such as a github link).

None required — minimal SQL above reproduces.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions