Created
March 10, 2026 11:55
-
-
Save ovr/f845f78549af8bcc9ed03a2a17b99df4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import java.sql.*; | |
| import java.util.Properties; | |
| public class Discover { | |
| public static void main(String[] args) throws Exception { | |
| Properties props = new Properties(); | |
| props.setProperty("user", "ovr"); | |
| props.setProperty("password", "test"); | |
| try (Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost:5555/db", props)) { | |
| // List public tables | |
| ResultSet tables = conn.getMetaData().getTables(null, "public", "%", null); | |
| System.out.println("=== PUBLIC TABLES ==="); | |
| while (tables.next()) { | |
| System.out.println(" " + tables.getString("TABLE_NAME") + " (" + tables.getString("TABLE_TYPE") + ")"); | |
| } | |
| // For each table, list columns | |
| Statement st = conn.createStatement(); | |
| ResultSet rs = st.executeQuery( | |
| "SELECT table_name, column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' ORDER BY table_name, ordinal_position"); | |
| System.out.println("\n=== COLUMNS ==="); | |
| while (rs.next()) { | |
| System.out.printf(" %s.%s (%s)%n", rs.getString(1), rs.getString(2), rs.getString(3)); | |
| } | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import java.sql.*; | |
| import java.util.Properties; | |
| /** | |
| * Reproducer for CubeSQL per-column format bug (GitHub issue #10418). | |
| * | |
| * Tableau and other JDBC clients use the Extended Query protocol which | |
| * requests binary format for specific column types (int8, float8) and | |
| * text format for others. This mix of per-column format codes in the | |
| * Bind message exposes a bug in CubeSQL where RowDescription only sends | |
| * a single format code instead of per-column format codes. | |
| * | |
| * Usage: | |
| * cd rust/cubesql/java-reproducer | |
| * curl -O https://jdbc.postgresql.org/download/postgresql-42.7.5.jar | |
| * javac PgReproducer.java | |
| * java -cp .:postgresql-42.7.5.jar PgReproducer | |
| */ | |
| public class PgReproducer { | |
| public static void main(String[] args) { | |
| String url = "jdbc:postgresql://localhost:5555/db"; | |
| Properties props = new Properties(); | |
| props.setProperty("user", "ovr"); | |
| props.setProperty("password", "test"); | |
| // After 1 execution the driver switches to server-prepared → binary transfer | |
| props.setProperty("prepareThreshold", "1"); | |
| // Enable binary transfer only for int8 (OID 20) and float8 (OID 701) | |
| // This creates a MIX of binary and text format codes in the Bind message, | |
| // just like Tableau does. | |
| props.setProperty("binaryTransfer", "true"); | |
| props.setProperty("binaryTransferEnable", "20,701"); | |
| props.setProperty("binaryTransferDisable", ""); | |
| System.out.println("Connecting to " + url + " ..."); | |
| try (Connection conn = DriverManager.getConnection(url, props)) { | |
| System.out.println("Connected. Server version: " | |
| + conn.getMetaData().getDatabaseProductVersion()); | |
| // Query 1: orders without aggregation — mixed types | |
| // id=float8(binary), status=text(text), order_sum=float8(binary), created=timestamp(text) | |
| runQuery(conn, "Query 1: orders without SUM", | |
| "SELECT id, status, order_sum, created FROM orders LIMIT 5"); | |
| // Query 2: orders with SUM aggregation — mixed types | |
| // status=text(text), total=float8(binary), cnt=int8(binary) | |
| runQuery(conn, "Query 2: orders with SUM", | |
| "SELECT status, SUM(order_sum) AS total, COUNT(*) AS cnt FROM orders GROUP BY 1"); | |
| System.out.println("\nALL QUERIES SUCCEEDED — no errors."); | |
| } catch (Exception e) { | |
| System.err.println("\nFAILED:"); | |
| e.printStackTrace(); | |
| System.exit(1); | |
| } | |
| } | |
| private static void runQuery(Connection conn, String label, String query) throws SQLException { | |
| System.out.println("\n=== " + label + " ==="); | |
| System.out.println("SQL: " + query); | |
| // Reuse the same PreparedStatement so the driver crosses prepareThreshold | |
| // and switches to binary format on the 2nd+ execution. | |
| try (PreparedStatement ps = conn.prepareStatement(query)) { | |
| for (int attempt = 1; attempt <= 3; attempt++) { | |
| System.out.println("\n --- Execution #" + attempt | |
| + (attempt == 1 ? " (text format)" : " (mixed binary/text format)") + " ---"); | |
| try (ResultSet rs = ps.executeQuery()) { | |
| ResultSetMetaData meta = rs.getMetaData(); | |
| int cols = meta.getColumnCount(); | |
| System.out.println(" Columns (" + cols + "):"); | |
| for (int i = 1; i <= cols; i++) { | |
| System.out.printf(" [%d] %s type=%s (%d)%n", | |
| i, | |
| meta.getColumnLabel(i), | |
| meta.getColumnTypeName(i), | |
| meta.getColumnType(i)); | |
| } | |
| System.out.println(" Rows:"); | |
| int rowNum = 0; | |
| while (rs.next()) { | |
| rowNum++; | |
| StringBuilder sb = new StringBuilder(" row " + rowNum + ": "); | |
| for (int i = 1; i <= cols; i++) { | |
| if (i > 1) sb.append(", "); | |
| Object val = rs.getObject(i); | |
| sb.append(meta.getColumnLabel(i)) | |
| .append("=") | |
| .append(val) | |
| .append(" (") | |
| .append(val == null ? "null" : val.getClass().getSimpleName()) | |
| .append(")"); | |
| } | |
| System.out.println(sb); | |
| } | |
| System.out.println(" Total rows: " + rowNum); | |
| } catch (SQLException e) { | |
| System.err.println(" ERROR on execution #" + attempt + ": " + e.getMessage()); | |
| throw e; | |
| } | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment