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

ostinru pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudberry-pxf.git


The following commit(s) were added to refs/heads/main by this push:
     new 4fdba998 Testcontainers: Add ClickHouse JDBC test (#97)
4fdba998 is described below

commit 4fdba99817c9bcd8b38cdf12a807592d7dcc1ecb
Author: Nikolay Antonov <[email protected]>
AuthorDate: Thu Apr 9 19:09:06 2026 +0500

    Testcontainers: Add ClickHouse JDBC test (#97)
    
    ### Add ClickHouse JDBC tests
    
    Add new tests to cover Cloudberry -> PXF -> JDBC path. In this test we are 
verifying that main data Cloudberry and ClickHouse types can be converted back 
and forth.
    
    Test covers ClickHouse 24.x and 26.x versions. Open-source ClickHouse has 
short support lifetime.
    
    I have tested following clickhouse-jdbc drivers:
    * 0.6.x (`jdbc-v1`) - works well with old ClickHouse versions. In 
ClickHouse 25.10 `jdbc-v1` got broken 
https://github.com/ClickHouse/clickhouse-java/issues/2636 and all queries 
raised an "Magic is not correct" error message.
    * 0.9.4 - fixes "Magic" issues, however has issue with String <-> bytea 
conversion `ERROR: PXF server error : Method: getBytes("bin") encountered an 
exception.`
    * 0.9.7+ - works well
    
    ### Add jdbc-pxf-drivers project
    
    Add new `jdbc-pxf-drivers` project to server. It excluded from default 
DEB-package build. Explicit actions required to install jdbc-drivers.
    
    ### Side quest
    
    Cloudberry FDW serializes rows as Greenplum CSV => PXF `TextRecordReader` 
parses the CSV stream via univocity CSV parser => for `BYTEA` columns it calls 
`pgUtilities.parseByteaLiteral()` => which returns a `ByteBuffer`.
---
 automation/pom.xml                                 |   7 +
 .../clickhouse/read_types/expected/query01.ans     |  46 +++
 .../jdbc/clickhouse/read_types/sql/query01.sql     |  37 +++
 .../clickhouse/write_types/expected/query01.ans    |  73 +++++
 .../jdbc/clickhouse/write_types/sql/query01.sql    |  67 +++++
 .../testcontainers/ClickHouseContainer.java        |  83 ++++++
 .../testcontainers/pxf-cbdb/script/build_pxf.sh    |   3 +
 .../features/jdbc/JdbcClickhouseTest.java          | 322 +++++++++++++++++++++
 .../pxf/automation/features/jdbc/JdbcHiveTest.java |   3 -
 .../pxf/automation/features/jdbc/JdbcTest.java     |  11 -
 docs/content/jdbc_pxf.html.md.erb                  |   1 +
 docs/content/jdbc_pxf_clickhouse.html.md.erb       | 143 +++++++++
 server/Makefile                                    |  19 +-
 server/build.gradle                                |  11 +-
 .../build.gradle}                                  |  17 +-
 .../cloudberry/pxf/plugins/jdbc/JdbcResolver.java  |  20 +-
 server/settings.gradle                             |   1 +
 17 files changed, 836 insertions(+), 28 deletions(-)

diff --git a/automation/pom.xml b/automation/pom.xml
index 0c11adb0..d9632ef7 100644
--- a/automation/pom.xml
+++ b/automation/pom.xml
@@ -251,6 +251,13 @@
             <version>42.7.2</version>
         </dependency>
 
+        <dependency>
+            <groupId>com.clickhouse</groupId>
+            <artifactId>clickhouse-jdbc</artifactId>
+            <version>0.9.8</version>
+            <classifier>all</classifier>
+        </dependency>
+
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring</artifactId>
diff --git 
a/automation/sqlrepo/features/jdbc/clickhouse/read_types/expected/query01.ans 
b/automation/sqlrepo/features/jdbc/clickhouse/read_types/expected/query01.ans
new file mode 100644
index 00000000..2dcae5d4
--- /dev/null
+++ 
b/automation/sqlrepo/features/jdbc/clickhouse/read_types/expected/query01.ans
@@ -0,0 +1,46 @@
+-- start_ignore
+-- end_ignore
+-- 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.
+
+-- @description ClickHouse JDBC read: expect one row of primitive types from 
PXF (values compared by regress output)
+SET timezone='utc';
+SET
+
+SET bytea_output='hex';
+SET
+
+SELECT
+    i_int,
+    s_small,
+    b_big,
+    f_float32,
+    d_float64,
+    b_bool,
+    dec,
+    t_text,
+    bin,
+    d_date,
+    d_ts,
+    d_tstz,
+    d_uuid
+FROM pxf_ch_clickhouse_read_types
+LIMIT 1;
+ i_int | s_small | b_big | f_float32 | d_float64 | b_bool |        dec        
| t_text |    bin     |   d_date   |         d_ts          |        d_tstz      
   |                d_uuid
+-------+---------+-------+-----------+-----------+--------+-------------------+--------+------------+------------+-----------------------+-----------------------+--------------------------------------
+     1 |       2 |     3 |      1.25 | 3.1415926 | t      | 12345.6789012345 | 
hello  | \x41424344 | 2020-01-02 | 2020-01-02 03:04:05.006 | 2020-01-02 
03:04:05.006+00 | 550e8400-e29b-41d4-a716-446655440000
+(1 row)
diff --git 
a/automation/sqlrepo/features/jdbc/clickhouse/read_types/sql/query01.sql 
b/automation/sqlrepo/features/jdbc/clickhouse/read_types/sql/query01.sql
new file mode 100644
index 00000000..863f1d6b
--- /dev/null
+++ b/automation/sqlrepo/features/jdbc/clickhouse/read_types/sql/query01.sql
@@ -0,0 +1,37 @@
+-- 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.
+
+-- @description ClickHouse JDBC read: expect one row of primitive types from 
PXF (values compared by regress output)
+SET timezone='utc';
+SET bytea_output='hex';
+
+SELECT
+    i_int,
+    s_small,
+    b_big,
+    f_float32,
+    d_float64,
+    b_bool,
+    dec,
+    t_text,
+    bin,
+    d_date,
+    d_ts,
+    d_tstz,
+    d_uuid
+FROM pxf_ch_clickhouse_read_types
+    LIMIT 1;
\ No newline at end of file
diff --git 
a/automation/sqlrepo/features/jdbc/clickhouse/write_types/expected/query01.ans 
b/automation/sqlrepo/features/jdbc/clickhouse/write_types/expected/query01.ans
new file mode 100644
index 00000000..27664b27
--- /dev/null
+++ 
b/automation/sqlrepo/features/jdbc/clickhouse/write_types/expected/query01.ans
@@ -0,0 +1,73 @@
+-- start_ignore
+-- end_ignore
+-- 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.
+-- @description ClickHouse JDBC write: insert full row then verify via 
readable external table
+SET timezone='utc';
+SET
+SET bytea_output='hex';
+SET
+INSERT INTO pxf_ch_clickhouse_write_types (
+    i_int,
+    s_small,
+    b_big,
+    f_float32,
+    d_float64,
+    b_bool,
+    dec,
+    t_text,
+    bin,
+    d_date,
+    d_ts,
+    d_tstz,
+    d_uuid
+) VALUES (
+    1,
+    2,
+    3,
+    1.25,
+    3.1415926,
+    true,
+    CAST('12345.6789012345' AS numeric),
+    'hello',
+    decode('41424344', 'hex'),
+    DATE '2020-01-02',
+    TIMESTAMP '2020-01-02 03:04:05.006',
+    TIMESTAMPTZ '2020-01-02 03:04:05.006+00',
+    '550e8400-e29b-41d4-a716-446655440000'::uuid
+);
+INSERT 0 1
+SELECT
+    i_int,
+    s_small,
+    b_big,
+    f_float32,
+    d_float64,
+    b_bool,
+    dec,
+    t_text,
+    bin,
+    d_date,
+    d_ts,
+    d_tstz,
+    d_uuid
+FROM pxf_ch_clickhouse_write_verify
+LIMIT 1;
+ i_int | s_small | b_big | f_float32 | d_float64 | b_bool |        dec        
| t_text |    bin     |   d_date   |         d_ts          |        d_tstz      
   |                d_uuid
+-------+---------+-------+-----------+-----------+--------+-------------------+--------+------------+------------+-----------------------+-----------------------+--------------------------------------
+     1 |       2 |     3 |      1.25 | 3.1415926 | t      | 12345.6789012345  
| hello  | \x41424344 | 2020-01-02 | 2020-01-02 03:04:05.006 | 2020-01-02 
03:04:05.006+00 | 550e8400-e29b-41d4-a716-446655440000
+(1 row)
diff --git 
a/automation/sqlrepo/features/jdbc/clickhouse/write_types/sql/query01.sql 
b/automation/sqlrepo/features/jdbc/clickhouse/write_types/sql/query01.sql
new file mode 100644
index 00000000..4e3d146b
--- /dev/null
+++ b/automation/sqlrepo/features/jdbc/clickhouse/write_types/sql/query01.sql
@@ -0,0 +1,67 @@
+-- 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.
+
+-- @description ClickHouse JDBC write: insert full row then verify via 
readable external table
+SET timezone='utc';
+SET bytea_output='hex';
+
+INSERT INTO pxf_ch_clickhouse_write_types (
+    i_int,
+    s_small,
+    b_big,
+    f_float32,
+    d_float64,
+    b_bool,
+    dec,
+    t_text,
+    bin,
+    d_date,
+    d_ts,
+    d_tstz,
+    d_uuid
+) VALUES (
+             1,
+             2,
+             3,
+             1.25,
+             3.1415926,
+             true,
+             CAST('12345.6789012345' AS numeric),
+             'hello',
+             decode('41424344', 'hex'),
+             DATE '2020-01-02',
+             TIMESTAMP '2020-01-02 03:04:05.006',
+             TIMESTAMPTZ '2020-01-02 03:04:05.006+00',
+             '550e8400-e29b-41d4-a716-446655440000'::uuid
+         );
+
+SELECT
+    i_int,
+    s_small,
+    b_big,
+    f_float32,
+    d_float64,
+    b_bool,
+    dec,
+    t_text,
+    bin,
+    d_date,
+    d_ts,
+    d_tstz,
+    d_uuid
+FROM pxf_ch_clickhouse_write_verify
+    LIMIT 1;
\ No newline at end of file
diff --git 
a/automation/src/main/java/org/apache/cloudberry/pxf/automation/testcontainers/ClickHouseContainer.java
 
b/automation/src/main/java/org/apache/cloudberry/pxf/automation/testcontainers/ClickHouseContainer.java
new file mode 100644
index 00000000..ecccce84
--- /dev/null
+++ 
b/automation/src/main/java/org/apache/cloudberry/pxf/automation/testcontainers/ClickHouseContainer.java
@@ -0,0 +1,83 @@
+package org.apache.cloudberry.pxf.automation.testcontainers;
+
+/*
+ * 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.
+ */
+
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.Network;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.utility.DockerImageName;
+
+/**
+ * TestContainers wrapper around ClickHouse server.
+ *
+ * The container joins a shared Docker network with alias `clickhouse`,
+ * so PXF inside the Cloudberry container can reach it at `clickhouse:8123` 
(HTTP).
+ *
+ */
+public class ClickHouseContainer extends GenericContainer<ClickHouseContainer> 
{
+
+    public static final int HTTP_PORT = 8123;
+
+    private static final String DEFAULT_IMAGE = "clickhouse/clickhouse-server";
+    private static final String NETWORK_ALIAS_PREFIX = "clickhouse-";
+
+    /**
+     * Credentials for the test container. ClickHouse 24+ restricts network 
access for `default`
+     * until a password is set; `CLICKHOUSE_PASSWORD` configures the server 
and JDBC must match.
+     */
+    public static final String CLICKHOUSE_USER = "default";
+    public static final String CLICKHOUSE_PASSWORD = "pxf-test";
+
+    private final String networkAlias;
+
+    public ClickHouseContainer(String tag, Network network) {
+        super(DockerImageName.parse(DEFAULT_IMAGE + ":" + tag));
+
+        // generate unique DNS name for this Clickhouse container:
+        this.networkAlias = NETWORK_ALIAS_PREFIX + tag.replaceAll("[-.]", "");
+
+        super.withNetwork(network)
+                .withNetworkAliases(this.networkAlias)
+                .withExposedPorts(HTTP_PORT)
+                .withEnv("CLICKHOUSE_USER", CLICKHOUSE_USER)
+                .withEnv("CLICKHOUSE_PASSWORD", CLICKHOUSE_PASSWORD)
+                .waitingFor(Wait.forHttp("/ping").forPort(HTTP_PORT));
+    }
+
+    /** Embedded DNS name of this container on the Testcontainers network (for 
JDBC from other containers). */
+    public String getNetworkAlias() {
+        return networkAlias;
+    }
+
+    /** JDBC URL over HTTP, reachable from the host (mapped `HTTP_PORT`). */
+    public String getJdbcUrl() {
+        return "jdbc:clickhouse://localhost:" + getMappedPort(HTTP_PORT) + 
"/default";
+    }
+
+    /** JDBC URL over HTTP for PXF / other containers on the same Docker 
network. */
+    public String getInternalJdbcUrl() {
+        return "jdbc:clickhouse://" + networkAlias + ":" + HTTP_PORT + 
"/default";
+    }
+
+    public int getHttpMappedPort() {
+        return getMappedPort(HTTP_PORT);
+    }
+
+}
\ No newline at end of file
diff --git 
a/automation/src/main/resources/testcontainers/pxf-cbdb/script/build_pxf.sh 
b/automation/src/main/resources/testcontainers/pxf-cbdb/script/build_pxf.sh
index 923775dd..bc7bbc22 100755
--- a/automation/src/main/resources/testcontainers/pxf-cbdb/script/build_pxf.sh
+++ b/automation/src/main/resources/testcontainers/pxf-cbdb/script/build_pxf.sh
@@ -56,6 +56,7 @@ cd /home/gpadmin/workspace/cloudberry-pxf
 make -C external-table install
 make -C fdw install
 make -C server install-server
+make -C server install-jdbc-drivers
 
 # Set up PXF environment
 export PXF_BASE=$HOME/pxf-base
@@ -65,6 +66,8 @@ mkdir -p "$PXF_BASE"
 
 # Initialize PXF
 pxf prepare
+cp $PXF_HOME/lib/*.jar $PXF_BASE/lib/
+ls -l $PXF_BASE/lib/
 pxf start
 
 # Verify PXF is running
diff --git 
a/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/jdbc/JdbcClickhouseTest.java
 
b/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/jdbc/JdbcClickhouseTest.java
new file mode 100644
index 00000000..34302432
--- /dev/null
+++ 
b/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/jdbc/JdbcClickhouseTest.java
@@ -0,0 +1,322 @@
+package org.apache.cloudberry.pxf.automation.features.jdbc;
+
+/*
+ * 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.
+ */
+
+import annotations.WorksWithFDW;
+import org.apache.cloudberry.pxf.automation.AbstractTestcontainersTest;
+import 
org.apache.cloudberry.pxf.automation.structures.tables.pxf.ExternalTable;
+import 
org.apache.cloudberry.pxf.automation.structures.tables.utils.TableFactory;
+import org.apache.cloudberry.pxf.automation.testcontainers.ClickHouseContainer;
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Factory;
+import org.testng.annotations.Test;
+
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Properties;
+import java.util.TimeZone;
+import java.util.UUID;
+
+@WorksWithFDW
+public class JdbcClickhouseTest extends AbstractTestcontainersTest {
+
+    private static final String CLICKHOUSE_DRIVER = 
"com.clickhouse.jdbc.ClickHouseDriver";
+
+    private static final String CLICKHOUSE_TABLE_READ = "pxf_types_read";
+    private static final String CLICKHOUSE_TABLE_WRITE = "pxf_types_write";
+
+    // PXF protocol compression options passed via LOCATION user parameters.
+    private static final String PROTOCOL_COMPRESS_ENABLED = "compress=true";
+    private static final String PROTOCOL_COMPRESS_ALGORITHM_LZ4 = 
"compress_algorithm=lz4";
+    private static final String HTTP_CONNECTION_PROVIDER_APACHE = 
"http_connection_provider=APACHE_HTTP_CLIENT";
+
+    /** PXF external/foreign table column definitions — same for read and 
write tests. */
+    private static final String[] CLICKHOUSE_PXF_FIELDS = new String[] {
+            "i_int int",
+            "s_small smallint",
+            "b_big bigint",
+            "f_float32 real",
+            "d_float64 double precision",
+            "b_bool boolean",
+            "dec numeric",
+            "t_text text",
+            "bin bytea",
+            "d_date date",
+            "d_ts timestamp",
+            "d_tstz timestamp with time zone",
+            "d_uuid uuid"
+    };
+
+    private static final int V_I_INT = 1;
+    private static final short V_S_SMALL = 2;
+    private static final long V_B_BIG = 3L;
+    private static final float V_F_FLOAT32 = 1.25f;
+    private static final double V_D_FLOAT64 = 3.1415926d;
+    private static final boolean V_B_BOOL = true;
+    private static final String V_DEC_TEXT = "12345.6789012345";
+    private static final String V_T_TEXT = "hello";
+    private static final String V_D_DATE = "2020-01-02";
+    private static final String V_D_TS = "2020-01-02 03:04:05.006";
+    private static final String V_D_UUID = 
"550e8400-e29b-41d4-a716-446655440000";
+    private final String dockerImageTag;
+
+    private ClickHouseContainer clickhouseContainer;
+
+    /**
+     * TestNG {@link Factory}: one test class instance per {@link 
#clickhouseVersions()} row (separate ClickHouse container).
+     */
+    @Factory(dataProvider = "clickhouseVersions")
+    public static Object[] createInstances(String imageTag) {
+        return new Object[] { new JdbcClickhouseTest(imageTag) };
+    }
+
+    /** Docker image tags for `clickhouse/clickhouse-server` — same regress 
SQL for both. */
+    @DataProvider(name = "clickhouseVersions")
+    public static Object[][] clickhouseVersions() {
+        return new Object[][] {
+                // Test PXF & ClickHouse with old database versions
+                { "24" },
+                // ClickHouse 25.10+ doesn't work with old JDBC drivers
+                // https://github.com/ClickHouse/clickhouse-java/issues/2636
+                 { "26.2" },
+        };
+    }
+
+    private JdbcClickhouseTest(String dockerImageTag) {
+        this.dockerImageTag = dockerImageTag;
+    }
+
+    @Override
+    public void beforeClass() throws Exception {
+        clickhouseContainer = new ClickHouseContainer(dockerImageTag, 
container.getSharedNetwork());
+        clickhouseContainer.start();
+
+        Assert.assertTrue(container.isRunning(), "PXFCloudberry container 
should be running");
+        Assert.assertTrue(clickhouseContainer.isRunning(), "ClickHouse 
container should be running");
+    }
+
+    @Override
+    public void afterClass() throws Exception {
+        if (clickhouseContainer != null) {
+            clickhouseContainer.stop();
+        }
+    }
+
+    @Test(groups = {"testcontainers", "pxf-jdbc"})
+    public void readSupportedTypes() throws Exception {
+        runReadSupportedTypes(clickhouseContainer.getInternalJdbcUrl(), 
clickhouseContainer.getJdbcUrl(), false);
+    }
+
+    @Test(groups = {"testcontainers", "pxf-jdbc"})
+    public void readSupportedTypesWithProtocolCompression() throws Exception {
+        runReadSupportedTypes(clickhouseContainer.getInternalJdbcUrl(), 
clickhouseContainer.getJdbcUrl(), true);
+    }
+
+    @Test(groups = {"testcontainers", "pxf-jdbc"})
+    public void readSupportedTypesWithHttpConnectionProvider() throws 
Exception {
+        runReadSupportedTypes(clickhouseContainer.getInternalJdbcUrl(), 
clickhouseContainer.getJdbcUrl(), false, true);
+    }
+
+    @Test(groups = {"testcontainers", "pxf-jdbc"})
+    public void writeSupportedTypes() throws Exception {
+        runWriteSupportedTypes(clickhouseContainer.getInternalJdbcUrl(), 
clickhouseContainer.getJdbcUrl(), false);
+    }
+
+    @Test(groups = {"testcontainers", "pxf-jdbc"})
+    public void writeSupportedTypesWithProtocolCompression() throws Exception {
+        runWriteSupportedTypes(clickhouseContainer.getInternalJdbcUrl(), 
clickhouseContainer.getJdbcUrl(), true);
+    }
+
+    @Test(groups = {"testcontainers", "pxf-jdbc"})
+    public void writeSupportedTypesWithHttpConnectionProvider() throws 
Exception {
+        runWriteSupportedTypes(clickhouseContainer.getInternalJdbcUrl(), 
clickhouseContainer.getJdbcUrl(), false, true);
+    }
+
+    @Test(groups = {"testcontainers", "pxf-jdbc"})
+    public void writeSupportedTypesWithHttpConnectionProviderAndCompression() 
throws Exception {
+        runWriteSupportedTypes(clickhouseContainer.getInternalJdbcUrl(), 
clickhouseContainer.getJdbcUrl(), true, true);
+    }
+
+    private void runReadSupportedTypes(String internalJdbcUrl, String 
externalJdbcUrl, boolean enableProtocolCompression) throws Exception {
+        runReadSupportedTypes(internalJdbcUrl, externalJdbcUrl, 
enableProtocolCompression, false);
+    }
+
+    private void runReadSupportedTypes(String internalJdbcUrl, String 
externalJdbcUrl, boolean enableProtocolCompression, boolean 
enableHttpConnectionProvider) throws Exception {
+        createAndSeedClickhouseReadTable(externalJdbcUrl);
+
+        ExternalTable pxfRead = TableFactory.getPxfJdbcReadableTable(
+                "pxf_ch_clickhouse_read_types",
+                CLICKHOUSE_PXF_FIELDS,
+                CLICKHOUSE_TABLE_READ,
+                CLICKHOUSE_DRIVER,
+                internalJdbcUrl,
+                ClickHouseContainer.CLICKHOUSE_USER,
+                "PASS=" + ClickHouseContainer.CLICKHOUSE_PASSWORD);
+        pxfRead.setHost(pxfHost);
+        pxfRead.setPort(pxfPort);
+        if (enableProtocolCompression) {
+            pxfRead.addUserParameter(PROTOCOL_COMPRESS_ENABLED);
+            pxfRead.addUserParameter(PROTOCOL_COMPRESS_ALGORITHM_LZ4);
+        }
+        if (enableHttpConnectionProvider) {
+            pxfRead.addUserParameter(HTTP_CONNECTION_PROVIDER_APACHE);
+        }
+        cloudberry.createTableAndVerify(pxfRead);
+
+        try {
+            regress.runSqlTest("features/jdbc/clickhouse/read_types");
+        } finally {
+            cloudberry.dropTable(pxfRead, true);
+        }
+    }
+
+    private void runWriteSupportedTypes(String internalJdbcUrl, String 
externalJdbcUrl, boolean enableProtocolCompression) throws Exception {
+        runWriteSupportedTypes(internalJdbcUrl, externalJdbcUrl, 
enableProtocolCompression, false);
+    }
+
+    private void runWriteSupportedTypes(String internalJdbcUrl, String 
externalJdbcUrl, boolean enableProtocolCompression, boolean 
enableHttpConnectionProvider) throws Exception {
+        createClickhouseWriteTable(externalJdbcUrl);
+
+        ExternalTable pxfWrite = TableFactory.getPxfJdbcWritableTable(
+                "pxf_ch_clickhouse_write_types",
+                CLICKHOUSE_PXF_FIELDS,
+                CLICKHOUSE_TABLE_WRITE,
+                CLICKHOUSE_DRIVER,
+                internalJdbcUrl,
+                ClickHouseContainer.CLICKHOUSE_USER,
+                "PASS=" + ClickHouseContainer.CLICKHOUSE_PASSWORD);
+        pxfWrite.setHost(pxfHost);
+        pxfWrite.setPort(pxfPort);
+        if (enableProtocolCompression) {
+            pxfWrite.addUserParameter(PROTOCOL_COMPRESS_ENABLED);
+            pxfWrite.addUserParameter(PROTOCOL_COMPRESS_ALGORITHM_LZ4);
+        }
+        if (enableHttpConnectionProvider) {
+            pxfWrite.addUserParameter(HTTP_CONNECTION_PROVIDER_APACHE);
+        }
+        cloudberry.createTableAndVerify(pxfWrite);
+
+        ExternalTable pxfVerify = TableFactory.getPxfJdbcReadableTable(
+                "pxf_ch_clickhouse_write_verify",
+                CLICKHOUSE_PXF_FIELDS,
+                CLICKHOUSE_TABLE_WRITE,
+                CLICKHOUSE_DRIVER,
+                internalJdbcUrl,
+                ClickHouseContainer.CLICKHOUSE_USER,
+                "PASS=" + ClickHouseContainer.CLICKHOUSE_PASSWORD);
+        pxfVerify.setHost(pxfHost);
+        pxfVerify.setPort(pxfPort);
+        if (enableProtocolCompression) {
+            pxfVerify.addUserParameter(PROTOCOL_COMPRESS_ENABLED);
+            pxfVerify.addUserParameter(PROTOCOL_COMPRESS_ALGORITHM_LZ4);
+        }
+        if (enableHttpConnectionProvider) {
+            pxfVerify.addUserParameter(HTTP_CONNECTION_PROVIDER_APACHE);
+        }
+        cloudberry.createTableAndVerify(pxfVerify);
+
+        try {
+            regress.runSqlTest("features/jdbc/clickhouse/write_types");
+        } finally {
+            cloudberry.dropTable(pxfVerify, true);
+            cloudberry.dropTable(pxfWrite, true);
+        }
+    }
+
+    private void createAndSeedClickhouseReadTable(String jdbcUrl) throws 
SQLException {
+        try (Connection chConn = openClickhouseConnection(jdbcUrl)) {
+            createClickhouseServerTable(chConn, CLICKHOUSE_TABLE_READ);
+            insertClickhouseReadFixture(chConn);
+        }
+    }
+
+    private void createClickhouseWriteTable(String externalJdbcUrl) throws 
SQLException {
+        try (Connection chConn = openClickhouseConnection(externalJdbcUrl)) {
+            createClickhouseServerTable(chConn, CLICKHOUSE_TABLE_WRITE);
+        }
+    }
+
+    /** Creates ClickHouse MergeTree table ({@code DROP IF EXISTS} + {@code 
CREATE}). */
+    private void createClickhouseServerTable(Connection chConn, String 
tableName) throws SQLException {
+        try (Statement st = chConn.createStatement()) {
+            st.execute("DROP TABLE IF EXISTS " + tableName);
+            st.execute("CREATE TABLE " + tableName + " ( "
+                    + "i_int Int32, "
+                    + "s_small Int16, "
+                    + "b_big Int64, "
+                    + "f_float32 Float32, "
+                    + "d_float64 Float64, "
+                    + "b_bool Bool, "
+                    + "dec Decimal(38,10), "
+                    + "t_text String, "
+                    + "bin String, "                 // binary data
+                    + "d_date Date, "
+                    + "d_ts DateTime64(3,'UTC'), "
+                    + "d_tstz DateTime64(3,'UTC'), "
+                    + "d_uuid UUID "
+                    + ") ENGINE = MergeTree ORDER BY (i_int)");
+        }
+    }
+
+    /** Inserts fixture row into {@link #CLICKHOUSE_TABLE_READ} for the read 
test. */
+    private void insertClickhouseReadFixture(Connection chConn) throws 
SQLException {
+        // TODO: setBytes() for ClickHouse String is driver-version sensitive 
and can serialize
+        // into an unexpected textual representation instead of raw bytes; use 
unhex() in SQL.
+        String insertSql = "INSERT INTO " + CLICKHOUSE_TABLE_READ + " ("
+                + "i_int, s_small, b_big, f_float32, d_float64, b_bool, dec, 
t_text, bin, d_date, d_ts, d_tstz, d_uuid"
+                + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, unhex('41424344'), ?, ?, 
?, ?)";
+
+        try (PreparedStatement ps = chConn.prepareStatement(insertSql)) {
+            // JDBC setDate/setTimestamp without Calendar uses JVM default 
timezone during
+            // value conversion, which can shift wall-clock date/time on 
non-UTC hosts.
+            // Pass UTC explicitly to keep fixture values deterministic across 
environments.
+            Calendar utcCalendar = 
Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+            ps.setInt(1, V_I_INT);
+            ps.setShort(2, V_S_SMALL);
+            ps.setLong(3, V_B_BIG);
+            ps.setFloat(4, V_F_FLOAT32);
+            ps.setDouble(5, V_D_FLOAT64);
+            ps.setBoolean(6, V_B_BOOL);
+            ps.setBigDecimal(7, new BigDecimal(V_DEC_TEXT));
+            ps.setString(8, V_T_TEXT);
+            ps.setDate(9, Date.valueOf(V_D_DATE), utcCalendar);
+            ps.setTimestamp(10, Timestamp.valueOf(V_D_TS), utcCalendar);
+            ps.setTimestamp(11, Timestamp.valueOf(V_D_TS), utcCalendar);
+            ps.setObject(12, UUID.fromString(V_D_UUID));
+            ps.executeUpdate();
+        }
+    }
+
+    private Connection openClickhouseConnection(String jdbcUrl) throws 
SQLException {
+        Properties props = new Properties();
+        props.setProperty("user", ClickHouseContainer.CLICKHOUSE_USER);
+        props.setProperty("password", ClickHouseContainer.CLICKHOUSE_PASSWORD);
+        return DriverManager.getConnection(jdbcUrl, props);
+    }
+
+}
\ No newline at end of file
diff --git 
a/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/jdbc/JdbcHiveTest.java
 
b/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/jdbc/JdbcHiveTest.java
index 3090a099..3e902969 100644
--- 
a/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/jdbc/JdbcHiveTest.java
+++ 
b/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/jdbc/JdbcHiveTest.java
@@ -245,9 +245,6 @@ public class JdbcHiveTest extends BaseFeature {
         runSqlTest("features/jdbc/hive");
     }
 
-    // Fails with the error: ERROR:  PXF server error : 
java.io.DataInputStream cannot be cast to [B
-    // TODO: pxf_regress shows diff for this test. Should be fixed.
-    @FailsWithFDW
     @Test(enabled = false, groups = {"features", "gpdb", "security"})
     public void jdbcHiveWrite() throws Exception {
         prepareDataForWriteTest();
diff --git 
a/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/jdbc/JdbcTest.java
 
b/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/jdbc/JdbcTest.java
index d52c850c..2263deb5 100755
--- 
a/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/jdbc/JdbcTest.java
+++ 
b/automation/src/test/java/org/apache/cloudberry/pxf/automation/features/jdbc/JdbcTest.java
@@ -21,7 +21,6 @@ package org.apache.cloudberry.pxf.automation.features.jdbc;
 
 import java.io.File;
 
-import annotations.FailsWithFDW;
 import annotations.WorksWithFDW;
 import org.apache.cloudberry.pxf.automation.AbstractTestcontainersTest;
 import org.apache.cloudberry.pxf.automation.applications.CloudberryApplication;
@@ -508,31 +507,21 @@ public class JdbcTest extends AbstractTestcontainersTest {
         runSqlTest("features/jdbc/session_params");
     }
 
-    @FailsWithFDW
-    // All the Writable Tests are failing with this Error:
-    // ERROR:  PXF server error : class java.io.DataInputStream cannot be cast 
to class
-    // [B (java.io.DataInputStream and [B are in module java.base of loader 
'bootstrap')
     @Test(groups = {"testcontainers", "pxf-jdbc"})
     public void jdbcWritableTable() throws Exception {
         runSqlTest("features/jdbc/writable");
     }
 
-    @FailsWithFDW
-    // All the Writable Tests are failing with this Error:
-    // ERROR:  PXF server error : class java.io.DataInputStream cannot be cast 
to class
-    // [B (java.io.DataInputStream and [B are in module java.base of loader 
'bootstrap')
     @Test(groups = {"testcontainers", "pxf-jdbc"})
     public void jdbcWritableTableWithDateWideRange() throws Exception {
         runSqlTest("features/jdbc/writable_date_wide_range");
     }
 
-    @FailsWithFDW
     @Test(groups = {"testcontainers", "pxf-jdbc"})
     public void jdbcWritableTableNoBatch() throws Exception {
         runSqlTest("features/jdbc/writable_nobatch");
     }
 
-    @FailsWithFDW
     @Test(groups = {"testcontainers", "pxf-jdbc"})
     public void jdbcWritableTablePool() throws Exception {
         runSqlTest("features/jdbc/writable_pool");
diff --git a/docs/content/jdbc_pxf.html.md.erb 
b/docs/content/jdbc_pxf.html.md.erb
index cef58764..e562a642 100644
--- a/docs/content/jdbc_pxf.html.md.erb
+++ b/docs/content/jdbc_pxf.html.md.erb
@@ -187,6 +187,7 @@ Refer to the following topics for examples on how to use 
PXF to read data from a
 - [Reading From and Writing to a PostgreSQL Table](jdbc_pxf_postgresql.html)
 - [Reading From and Writing to a MySQL Table](jdbc_pxf_mysql.html)
 - [Reading From and Writing to an Oracle Table](jdbc_pxf_oracle.html)
+- [Reading From and Writing to a ClickHouse Table](jdbc_pxf_clickhouse.html)
 - [Reading From and Writing to a Trino Table](jdbc_pxf_trino.html)
 
 ## <a id="about_nq"></a>About Using Named Queries
diff --git a/docs/content/jdbc_pxf_clickhouse.html.md.erb 
b/docs/content/jdbc_pxf_clickhouse.html.md.erb
new file mode 100644
index 00000000..01b6a52f
--- /dev/null
+++ b/docs/content/jdbc_pxf_clickhouse.html.md.erb
@@ -0,0 +1,143 @@
+---
+title: 'Example: Reading From and Writing to a ClickHouse Table'
+---
+
+<!--
+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.
+-->
+
+In this example, you:
+
+- Install the ClickHouse JDBC driver on the Apache Cloudberry coordinator (see 
[JDBC driver installation](jdbc_pxf.html#cfg_jdbc_driver))
+- Create a ClickHouse table, and insert data into the table
+- Create a PXF readable external table that references the ClickHouse table 
using `JDBC_DRIVER` and `DB_URL` in the `LOCATION` clause (no separate PXF 
server configuration directory is required)
+- Read the data in the ClickHouse table using PXF
+- Create a PXF writable external table that references the ClickHouse table
+- Write data to the ClickHouse table using PXF
+- Read the data in the ClickHouse table again
+
+## <a id="ex_create_ch_1"></a>Create a ClickHouse Table
+
+Perform the following steps to create a ClickHouse table named `pxf_ch_types` 
in the `default` database:
+
+```sql
+CREATE TABLE default.pxf_ch_types (
+  i_int Int32,
+  s_small Int16,
+  b_big Int64,
+  f_float32 Float32,
+  d_float64 Float64,
+  b_bool Bool,
+  dec Decimal(38,10),
+  t_text String,
+  bin String,
+  d_date Date,
+  d_ts DateTime64(3,'UTC'),
+  d_tstz DateTime64(3,'UTC'),
+  d_uuid UUID
+) ENGINE = MergeTree ORDER BY (i_int);
+
+INSERT INTO default.pxf_ch_types
+  (i_int, s_small, b_big, f_float32, d_float64, b_bool, dec, t_text, bin, 
d_date, d_ts, d_tstz, d_uuid)
+VALUES
+  (1, 2, 3, 1.25, 3.1415926, true, toDecimal64('12345.6789012345', 10), 
'hello', 'ABCD',
+   toDate('2020-01-02'), toDateTime64('2020-01-02 03:04:05.006', 3, 'UTC'),
+   toDateTime64('2020-01-02 03:04:05.006', 3, 'UTC'), 
toUUID('550e8400-e29b-41d4-a716-446655440000'));
+```
+
+## <a id="ex_jdbconfig_1"></a>JDBC driver
+
+Place the ClickHouse JDBC driver JAR under `$PXF_BASE/lib` on every 
coordinator and segment host, synchronize PXF, and restart PXF. The Cloudberry 
build can bundle JDBC drivers from `server/pxf-jdbc-drivers`; follow your 
deployment’s procedure (for example, the automation `Makefile` target that 
stages PXF and links drivers).
+
+You do **not** need a `servers/clickhouse/jdbc-site.xml` if you pass 
connection options in the external table definition, as shown below.
+
+Automated tests exercise ClickHouse server versions **24** and **26** together 
with the ClickHouse JDBC drivers.
+
+For ClickHouse we recommend latest clickhouse-jdbc-all:0.9.7 or newer (fat jar 
version with shaded dependencies). In case you observe HTTP reliably issues you 
can use Apache HttpClient: add the JDBC URL parameter 
`http_connection_provider=APACHE_HTTP_CLIENT` (for example 
`jdbc:clickhouse://clickhousehost:8123/default?http_connection_provider=APACHE_HTTP_CLIENT`).
+
+
+## <a id="ex_readjdbc_1"></a>Read from the ClickHouse Table
+
+Perform the following procedure to create a PXF external table that references 
the `pxf_ch_types` ClickHouse table, and reads the data in the table:
+
+1. Create a PXF readable external table using the `jdbc` profile. Substitute 
your ClickHouse host, port, and driver class as appropriate:
+
+   ```sql
+   CREATE EXTERNAL TABLE pxf_ch_read(
+     i_int int, s_small smallint, b_big bigint,
+     f_float32 real, d_float64 double precision, b_bool boolean,
+     dec numeric, t_text text, bin bytea, d_date date,
+     d_ts timestamp, d_tstz timestamp with time zone, d_uuid uuid)
+     LOCATION 
('pxf://default.pxf_ch_types?PROFILE=jdbc&JDBC_DRIVER=com.clickhouse.jdbc.ClickHouseDriver&DB_URL=jdbc:clickhouse://clickhousehost:8123/default')
+     FORMAT 'CUSTOM' (FORMATTER='pxfwritable_import');
+   ```
+
+2. Read the data:
+
+   ```sql
+   SELECT * FROM pxf_ch_read ORDER BY i_int;
+   ```
+
+## <a id="ex_writejdbc_1"></a>Write to the ClickHouse Table
+
+Perform the following procedure to create a PXF writable external table that 
references the `pxf_ch_types` ClickHouse table, and writes data to the table:
+
+1. Create a PXF writable external table:
+
+   ```sql
+   CREATE WRITABLE EXTERNAL TABLE pxf_ch_write(
+     i_int int, s_small smallint, b_big bigint,
+     f_float32 real, d_float64 double precision, b_bool boolean,
+     dec numeric, t_text text, bin bytea, d_date date,
+     d_ts timestamp, d_tstz timestamp with time zone, d_uuid uuid)
+     LOCATION 
('pxf://default.pxf_ch_types?PROFILE=jdbc&JDBC_DRIVER=com.clickhouse.jdbc.ClickHouseDriver&DB_URL=jdbc:clickhouse://clickhousehost:8123/default')
+     FORMAT 'CUSTOM' (FORMATTER='pxfwritable_export');
+   ```
+
+2. Write data:
+
+   ```sql
+   INSERT INTO pxf_ch_write VALUES
+     (2, 3, 4, 2.25, 4.1415926, true, CAST('12345.6789012345' AS numeric),
+      'hello2', decode('41424344','hex'), DATE '2020-01-02',
+      TIMESTAMP '2020-01-02 03:04:05.006',
+      TIMESTAMPTZ '2020-01-02 03:04:05.006+00',
+      '550e8400-e29b-41d4-a716-446655440000'::uuid);
+   ```
+
+3. Read the data back:
+
+   ```sql
+   SELECT * FROM pxf_ch_read ORDER BY i_int;
+   ```
+
+## Data Type Mapping and Limitations
+
+PXF supports the following JDBC data types (see [JDBC Data Types 
Supported](jdbc_pxf.html#datatypes)).
+For ClickHouse, the typical JDBC-side mappings are:
+
+- `INTEGER`, `SMALLINT`, `BIGINT` → `Int32`, `Int16`, `Int64`
+- `REAL`, `FLOAT8` → `Float32`, `Float64`
+- `NUMERIC` → `Decimal(precision, scale)`
+- `BOOLEAN` → `Bool`
+- `VARCHAR`, `BPCHAR`, `TEXT` → `String`
+- `DATE` → `Date`
+- `TIMESTAMP` → `DateTime64(precision, 'UTC')`
+- `TIMESTAMPTZ` → `DateTime64(precision, 'UTC')`
+- `BYTEA` → `String`
+- `UUID` → `UUID`
diff --git a/server/Makefile b/server/Makefile
index edd2cbb1..e6d05e61 100644
--- a/server/Makefile
+++ b/server/Makefile
@@ -40,7 +40,10 @@ help:
        @echo   "  - test - runs unit tests for all PXF modules"
        @echo   "  - coverage - runs unit tests for all PXF modules and gives a 
coverage report"
        @echo   "  - install - setup PXF in the configured deployPath"
-       @echo   "  - stage - build PXF server and stage resulting artifacts for 
packaging"
+       @echo   "  - stage - build PXF server and stage resulting artifacts for 
packaging (no JDBC drivers in build/stage/lib)"
+       @echo   "  - stage-notest - same as stage but skip tests (no JDBC 
drivers in build/stage/lib)"
+       @echo   "  - stage-jdbc-drivers - copy JDBC driver JARs to 
build/stage/lib"
+       @echo   "  - install-jdbc-drivers - setup PXF JDBC drivers in the 
configured deployPath"
        @echo   "  - doc - creates aggregate javadoc under docs"
 
 all: prepare-gradle-wrapper
@@ -95,6 +98,20 @@ stage-notest: prepare-gradle-wrapper
        install -m 700 -d "build/stage/run"
        install -m 700 -d "build/stage/keytabs"
 
+.PHONY: stage-jdbc-drivers
+stage-jdbc-drivers: prepare-gradle-wrapper
+       rm -rf build/stage/lib
+       ./gradlew $(PXF_GRADLE_PROPERTIES) stageJdbcDrivers
+
+.PHONY: install-jdbc-drivers
+install-jdbc-drivers: stage-jdbc-drivers
+       @if [ -z "$(PXF_HOME)" ]; then \
+               echo "ERROR: PXF_HOME is not set"; exit 2; \
+       fi
+       mkdir -p "$(PXF_HOME)"/lib
+       rm -f "$(PXF_HOME)"/lib/clickhouse-jdbc-*.jar
+       cp -R build/stage/lib/. "$(PXF_HOME)"/lib/
+
 clean: prepare-gradle-wrapper
        ./gradlew clean
        rm -rf build
diff --git a/server/build.gradle b/server/build.gradle
index e530d872..6a518c79 100644
--- a/server/build.gradle
+++ b/server/build.gradle
@@ -212,8 +212,9 @@ configure(javaProjects) {
                 entry("avro")
                 entry("avro-mapred")
             }
-            // Zstd support for Avro/Parquet
+            // Compression support for Avro/Parquet
             dependency("com.github.luben:zstd-jni:1.5.7-6")
+            dependency("at.yawk.lz4:lz4-java:1.10.3")
 
             // Jackson 1.x dependencies
             dependencySet(group:"org.codehaus.jackson", version:"1.9.13") {
@@ -374,3 +375,11 @@ task stage(type: Copy) {
 
     doLast { new File("${buildDir}/stage/version").text = "${version}\n" }
 }
+
+task stageJdbcDrivers(type: Copy) {
+    description "Copy JDBC driver JARs from pxf-jdbc-drivers to 
build/stage/lib."
+    dependsOn ":pxf-jdbc-drivers:jar"
+    from(project(':pxf-jdbc-drivers').configurations.runtimeClasspath)
+    into("${buildDir}/stage/lib")
+    duplicatesStrategy = DuplicatesStrategy.FAIL
+}
diff --git a/server/settings.gradle b/server/pxf-jdbc-drivers/build.gradle
similarity index 77%
copy from server/settings.gradle
copy to server/pxf-jdbc-drivers/build.gradle
index dd419679..052eef02 100644
--- a/server/settings.gradle
+++ b/server/pxf-jdbc-drivers/build.gradle
@@ -17,14 +17,11 @@
  * under the License.
  */
 
-rootProject.name = 'pxf'
+dependencies {
+    // PostgreSQL driver is used by PXF itself. 
+    // Its version is defined in server/build.gradle
 
-include 'pxf-service',
-        'pxf-api',
-        'pxf-hbase',
-        'pxf-hdfs',
-        'pxf-hive',
-        'pxf-jdbc',
-        'pxf-json',
-        'pxf-s3',
-        'pxf-diagnostic'
+    // Clickhouse
+    implementation('com.clickhouse:clickhouse-jdbc:0.9.8:all')                
{ transitive = false }
+
+}
\ No newline at end of file
diff --git 
a/server/pxf-jdbc/src/main/java/org/apache/cloudberry/pxf/plugins/jdbc/JdbcResolver.java
 
b/server/pxf-jdbc/src/main/java/org/apache/cloudberry/pxf/plugins/jdbc/JdbcResolver.java
index 99b1df73..485a0959 100644
--- 
a/server/pxf-jdbc/src/main/java/org/apache/cloudberry/pxf/plugins/jdbc/JdbcResolver.java
+++ 
b/server/pxf-jdbc/src/main/java/org/apache/cloudberry/pxf/plugins/jdbc/JdbcResolver.java
@@ -33,6 +33,7 @@ import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.math.BigDecimal;
+import java.nio.ByteBuffer;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -277,7 +278,7 @@ public class JdbcResolver extends JdbcBasePlugin implements 
Resolver {
                 if (oneField.val == null) {
                     valDebug = "null";
                 } else if (oneFieldType == DataType.BYTEA) {
-                    valDebug = String.format("'{}'", new String((byte[]) 
oneField.val));
+                    valDebug = String.format("'{}'", new 
String(toByteArray(oneField.val)));
                 } else {
                     valDebug = String.format("'{}'", oneField.val.toString());
                 }
@@ -421,7 +422,7 @@ public class JdbcResolver extends JdbcBasePlugin implements 
Resolver {
                     if (field.val == null) {
                         statement.setNull(i, Types.BINARY);
                     } else {
-                        statement.setBytes(i, (byte[]) field.val);
+                        statement.setBytes(i, toByteArray(field.val));
                     }
                     break;
                 case DATE:
@@ -506,4 +507,19 @@ public class JdbcResolver extends JdbcBasePlugin 
implements Resolver {
         }
         return value;
     }
+
+    /**
+     * Converts a BYTEA field value to byte[]. The value can arrive
+     *   as byte[] (OutputFormat.GPDBWritable from external-table)
+     *   as ByteBuffer (OutputFormat.TEXT from FDW)
+     */
+    private static byte[] toByteArray(Object val) {
+        if (val instanceof byte[]) {
+            return (byte[]) val;
+        }
+        ByteBuffer bb = (ByteBuffer) val;
+        byte[] bytes = new byte[bb.remaining()];
+        bb.duplicate().get(bytes);
+        return bytes;
+    }
 }
diff --git a/server/settings.gradle b/server/settings.gradle
index dd419679..5df3ff21 100644
--- a/server/settings.gradle
+++ b/server/settings.gradle
@@ -25,6 +25,7 @@ include 'pxf-service',
         'pxf-hdfs',
         'pxf-hive',
         'pxf-jdbc',
+        'pxf-jdbc-drivers',
         'pxf-json',
         'pxf-s3',
         'pxf-diagnostic'


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to