This is an automated email from the ASF dual-hosted git repository.

cgivre pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/drill.git


The following commit(s) were added to refs/heads/master by this push:
     new 2a4a79b37a DRILL-8551: Fix Informix JDBC table qualifiers (#3065)
2a4a79b37a is described below

commit 2a4a79b37ad30c8b9dc43f05604995bf28ee97ad
Author: 林桉 <[email protected]>
AuthorDate: Tue Jul 28 22:42:57 2026 +0800

    DRILL-8551: Fix Informix JDBC table qualifiers (#3065)
---
 .../drill/exec/store/jdbc/JdbcDialectFactory.java  |  17 ++-
 .../store/jdbc/informix/InformixJdbcDialect.java   |  46 ++++++++
 .../jdbc/informix/InformixJdbcImplementor.java     |  63 +++++++++++
 .../jdbc/informix/TestInformixJdbcDialect.java     | 124 +++++++++++++++++++++
 4 files changed, 247 insertions(+), 3 deletions(-)

diff --git 
a/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/JdbcDialectFactory.java
 
b/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/JdbcDialectFactory.java
index b3eaef16ea..7e4b8e9eb7 100644
--- 
a/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/JdbcDialectFactory.java
+++ 
b/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/JdbcDialectFactory.java
@@ -18,12 +18,16 @@
 package org.apache.drill.exec.store.jdbc;
 
 import org.apache.calcite.sql.SqlDialect;
+import org.apache.calcite.sql.dialect.InformixSqlDialect;
 import org.apache.drill.exec.store.jdbc.clickhouse.ClickhouseJdbcDialect;
+import org.apache.drill.exec.store.jdbc.informix.InformixJdbcDialect;
 
 import java.time.Duration;
+import java.util.Locale;
 
 public class JdbcDialectFactory {
   public static final String JDBC_CLICKHOUSE_PREFIX = "jdbc:clickhouse";
+  public static final String JDBC_INFORMIX_PREFIX = "jdbc:informix";
   public static final int CACHE_SIZE = 100;
   public static final Duration CACHE_TTL = Duration.ofHours(1);
   private volatile JdbcDialect jdbcDialect;
@@ -38,9 +42,16 @@ public class JdbcDialectFactory {
       synchronized (this) {
         jd = jdbcDialect;
         if (jd == null) {
-          jd = plugin.getConfig().getUrl().startsWith(JDBC_CLICKHOUSE_PREFIX)
-                ? new ClickhouseJdbcDialect(plugin, dialect)
-                : new DefaultJdbcDialect(plugin, dialect);
+          String url = plugin.getConfig().getUrl();
+          String normalizedUrl = url == null ? null : 
url.toLowerCase(Locale.ROOT);
+          if (normalizedUrl != null && 
normalizedUrl.startsWith(JDBC_CLICKHOUSE_PREFIX)) {
+            jd = new ClickhouseJdbcDialect(plugin, dialect);
+          } else if (dialect instanceof InformixSqlDialect
+              || normalizedUrl != null && 
normalizedUrl.startsWith(JDBC_INFORMIX_PREFIX)) {
+            jd = new InformixJdbcDialect(plugin, dialect);
+          } else {
+            jd = new DefaultJdbcDialect(plugin, dialect);
+          }
           jdbcDialect = jd;
         }
       }
diff --git 
a/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/informix/InformixJdbcDialect.java
 
b/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/informix/InformixJdbcDialect.java
new file mode 100644
index 0000000000..4bd69269bd
--- /dev/null
+++ 
b/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/informix/InformixJdbcDialect.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.drill.exec.store.jdbc.informix;
+
+import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.adapter.jdbc.JdbcImplementor;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.sql.SqlDialect;
+import org.apache.drill.exec.store.SubsetRemover;
+import org.apache.drill.exec.store.jdbc.DefaultJdbcDialect;
+import org.apache.drill.exec.store.jdbc.JdbcStoragePlugin;
+
+public class InformixJdbcDialect extends DefaultJdbcDialect {
+
+  private final SqlDialect dialect;
+
+  public InformixJdbcDialect(JdbcStoragePlugin plugin, SqlDialect dialect) {
+    super(plugin, dialect);
+    this.dialect = dialect;
+  }
+
+  @Override
+  public String generateSql(RelOptCluster cluster, RelNode input) {
+    final JdbcImplementor jdbcImplementor = new 
InformixJdbcImplementor(dialect,
+        (JavaTypeFactory) cluster.getTypeFactory());
+    final JdbcImplementor.Result result = jdbcImplementor.visitRoot(
+      input.accept(SubsetRemover.INSTANCE));
+    return result.asStatement().toSqlString(dialect).getSql();
+  }
+}
diff --git 
a/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/informix/InformixJdbcImplementor.java
 
b/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/informix/InformixJdbcImplementor.java
new file mode 100644
index 0000000000..1524e3a351
--- /dev/null
+++ 
b/contrib/storage-jdbc/src/main/java/org/apache/drill/exec/store/jdbc/informix/InformixJdbcImplementor.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.drill.exec.store.jdbc.informix;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.adapter.jdbc.JdbcImplementor;
+import org.apache.calcite.adapter.jdbc.JdbcTable;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.sql.SqlDialect;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.parser.SqlParserPos;
+
+import com.google.common.collect.ImmutableList;
+
+import static java.util.Objects.requireNonNull;
+
+public class InformixJdbcImplementor extends JdbcImplementor {
+  public InformixJdbcImplementor(SqlDialect dialect, JavaTypeFactory 
typeFactory) {
+    super(dialect, typeFactory);
+  }
+
+  @Override
+  public Result visit(TableScan scan) {
+    SqlIdentifier sqlIdentifier = getSqlTargetTable(scan);
+    return result(sqlIdentifier, ImmutableList.of(Clause.FROM), scan, null);
+  }
+
+  static SqlIdentifier getSqlTargetTable(RelNode e) {
+    RelOptTable table = requireNonNull(e.getTable());
+    return table.maybeUnwrap(JdbcTable.class)
+      .map(jdbcTable -> {
+        List<String> names = new ArrayList<>(2);
+        // Informix JDBC reports the connected database as a catalog, but
+        // Informix SQL does not accept it as a dot-qualified table prefix.
+        if (jdbcTable.jdbcSchemaName != null) {
+          names.add(jdbcTable.jdbcSchemaName);
+        }
+        names.add(jdbcTable.jdbcTableName);
+        return new SqlIdentifier(names, SqlParserPos.ZERO);
+      })
+      .orElseGet(() -> new SqlIdentifier(table.getQualifiedName(), 
SqlParserPos.ZERO));
+  }
+}
diff --git 
a/contrib/storage-jdbc/src/test/java/org/apache/drill/exec/store/jdbc/informix/TestInformixJdbcDialect.java
 
b/contrib/storage-jdbc/src/test/java/org/apache/drill/exec/store/jdbc/informix/TestInformixJdbcDialect.java
new file mode 100644
index 0000000000..2315a759b7
--- /dev/null
+++ 
b/contrib/storage-jdbc/src/test/java/org/apache/drill/exec/store/jdbc/informix/TestInformixJdbcDialect.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.drill.exec.store.jdbc.informix;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Constructor;
+import java.util.Optional;
+
+import javax.sql.DataSource;
+
+import org.apache.calcite.adapter.jdbc.JdbcSchema;
+import org.apache.calcite.adapter.jdbc.JdbcTable;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.schema.Schema;
+import org.apache.calcite.sql.SqlDialect;
+import org.apache.calcite.sql.dialect.InformixSqlDialect;
+import org.apache.drill.exec.store.jdbc.JdbcDialect;
+import org.apache.drill.exec.store.jdbc.JdbcDialectFactory;
+import org.apache.drill.exec.store.jdbc.JdbcStorageConfig;
+import org.apache.drill.exec.store.jdbc.JdbcStoragePlugin;
+import org.junit.Test;
+
+public class TestInformixJdbcDialect {
+
+  @Test
+  public void dropsCatalogFromTargetTable() throws Exception {
+    JdbcTable jdbcTable = newJdbcTable("sample_database", null, 
"sample_table");
+    TableScan scan = mock(TableScan.class);
+    RelOptTable relOptTable = mock(RelOptTable.class);
+    when(scan.getTable()).thenReturn(relOptTable);
+    
when(relOptTable.maybeUnwrap(JdbcTable.class)).thenReturn(Optional.of(jdbcTable));
+
+    String sql = InformixJdbcImplementor.getSqlTargetTable(scan)
+      .toSqlString(InformixSqlDialect.DEFAULT)
+      .getSql();
+
+    assertEquals("sample_table", sql);
+  }
+
+  @Test
+  public void keepsSchemaWhenPresent() throws Exception {
+    JdbcTable jdbcTable = newJdbcTable("sample_database", "sample_owner", 
"sample_table");
+    TableScan scan = mock(TableScan.class);
+    RelOptTable relOptTable = mock(RelOptTable.class);
+    when(scan.getTable()).thenReturn(relOptTable);
+    
when(relOptTable.maybeUnwrap(JdbcTable.class)).thenReturn(Optional.of(jdbcTable));
+
+    String sql = InformixJdbcImplementor.getSqlTargetTable(scan)
+      .toSqlString(InformixSqlDialect.DEFAULT)
+      .getSql();
+
+    assertEquals("sample_owner.sample_table", sql);
+  }
+
+  @Test
+  public void factorySelectsInformixDialect() {
+    JdbcStoragePlugin plugin = mock(JdbcStoragePlugin.class);
+    
when(plugin.getConfig()).thenReturn(newJdbcStorageConfig("jdbc:other://localhost"));
+
+    JdbcDialect dialect = new JdbcDialectFactory().getJdbcDialect(plugin, 
InformixSqlDialect.DEFAULT);
+    assertTrue(dialect instanceof InformixJdbcDialect);
+  }
+
+  @Test
+  public void factorySelectsInformixDialectFromUrl() {
+    JdbcStoragePlugin plugin = mock(JdbcStoragePlugin.class);
+    when(plugin.getConfig()).thenReturn(
+      
newJdbcStorageConfig("jdbc:informix-sqli://localhost:1526/sample_database"));
+
+    JdbcDialect dialect = new JdbcDialectFactory().getJdbcDialect(
+      plugin, SqlDialect.DatabaseProduct.UNKNOWN.getDialect());
+    assertTrue(dialect instanceof InformixJdbcDialect);
+  }
+
+  private static JdbcStorageConfig newJdbcStorageConfig(String url) {
+    JdbcStorageConfig config = new JdbcStorageConfig(
+      "com.informix.jdbc.IfxDriver",
+      url,
+      null,
+      null,
+      true,
+      false,
+      null,
+      null,
+      null,
+      0
+    );
+    return config;
+  }
+
+  private static JdbcTable newJdbcTable(String catalog, String schema, String 
table) throws Exception {
+    DataSource dataSource = mock(DataSource.class);
+    JdbcSchema jdbcSchema = new JdbcSchema(dataSource, 
InformixSqlDialect.DEFAULT, null, catalog, schema);
+    Constructor<JdbcTable> ctor = JdbcTable.class.getDeclaredConstructor(
+      JdbcSchema.class,
+      String.class,
+      String.class,
+      String.class,
+      Schema.TableType.class
+    );
+    ctor.setAccessible(true);
+    return ctor.newInstance(jdbcSchema, catalog, schema, table, 
Schema.TableType.TABLE);
+  }
+}

Reply via email to