diff --git a/databend-client/src/main/java/com/databend/client/DatabendClientV1.java b/databend-client/src/main/java/com/databend/client/DatabendClientV1.java index 46160ead..2aeb67cf 100644 --- a/databend-client/src/main/java/com/databend/client/DatabendClientV1.java +++ b/databend-client/src/main/java/com/databend/client/DatabendClientV1.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.OptionalLong; import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Logger; import static com.databend.client.JsonCodec.jsonCodec; import static com.google.common.base.MoreObjects.firstNonNull; @@ -60,6 +61,7 @@ public class DatabendClientV1 // client session private final AtomicReference databendSession; private final AtomicReference currentResults = new AtomicReference<>(); + private static final Logger logger = Logger.getLogger(DatabendClientV1.class.getPackage().getName()); public DatabendClientV1(OkHttpClient httpClient, String sql, ClientSettings settings) { requireNonNull(httpClient, "httpClient is null"); diff --git a/databend-client/src/main/java/com/databend/client/data/DatabendDataType.java b/databend-client/src/main/java/com/databend/client/data/DatabendDataType.java index 8a6f987d..421c3d64 100644 --- a/databend-client/src/main/java/com/databend/client/data/DatabendDataType.java +++ b/databend-client/src/main/java/com/databend/client/data/DatabendDataType.java @@ -46,7 +46,7 @@ public enum DatabendDataType { MAP(Types.OTHER, DatabendTypes.MAP, false, 0, false, "Map"), BITMAP(Types.OTHER, DatabendTypes.MAP, false, 0, false, "Bitmap"), TUPLE(Types.OTHER, DatabendTypes.TUPLE, false, 0, false, "Tuple"), - VARIANT(Types.OTHER, DatabendTypes.VARIANT, false, 0, false, "Variant", "Json"), + VARIANT(Types.VARCHAR, DatabendTypes.VARIANT, false, 0, false, "Variant", "Json"), BINARY(Types.BINARY, DatabendTypes.BINARY, false, 0, false, "Binary"), @@ -87,7 +87,7 @@ public static DatabendDataType getByTypeName(String typeName) { return INT_16; } else if (DatabendTypes.UINT16.equalsIgnoreCase(typeName)) { return UNSIGNED_INT_16; - } else if (DatabendTypes.INT32.equalsIgnoreCase(typeName)) { + } else if (DatabendTypes.INT32.equalsIgnoreCase(typeName) || "int".equalsIgnoreCase(typeName)) { return INT_32; } else if (DatabendTypes.UINT32.equalsIgnoreCase(typeName)) { return UNSIGNED_INT_32; diff --git a/databend-client/src/main/java/com/databend/client/data/DatabendTypes.java b/databend-client/src/main/java/com/databend/client/data/DatabendTypes.java index 13ca8f7d..65030d83 100644 --- a/databend-client/src/main/java/com/databend/client/data/DatabendTypes.java +++ b/databend-client/src/main/java/com/databend/client/data/DatabendTypes.java @@ -43,5 +43,5 @@ public final class DatabendTypes { public static final String VARIANT_ARRAY = "variantarray"; public static final String VARIANT_OBJECT = "variantobject"; public static final String INTERVAL = "interval"; - public static final String DECIMAL = "Decimal"; + public static final String DECIMAL = "decimal"; } diff --git a/databend-jdbc/pom.xml b/databend-jdbc/pom.xml index 004b2af1..e7ad7ded 100644 --- a/databend-jdbc/pom.xml +++ b/databend-jdbc/pom.xml @@ -51,10 +51,6 @@ slf4j-api 1.7.6 - - - - com.google.guava guava diff --git a/databend-jdbc/src/main/java/com/databend/jdbc/DatabendColumnInfo.java b/databend-jdbc/src/main/java/com/databend/jdbc/DatabendColumnInfo.java index 4e4f9940..e749eac3 100644 --- a/databend-jdbc/src/main/java/com/databend/jdbc/DatabendColumnInfo.java +++ b/databend-jdbc/src/main/java/com/databend/jdbc/DatabendColumnInfo.java @@ -57,7 +57,9 @@ public DatabendColumnInfo(int columnType, List columnParameterTypes, Da public static DatabendColumnInfo of(String name, DatabendRawType type) { Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "Provided name is null or empty"); - return newBuilder(name, type).build(); + Builder builder = newBuilder(name, type); + setTypeInfo(builder, type); + return builder.build(); } public static void setTypeInfo(Builder builder, DatabendRawType type) { @@ -163,7 +165,9 @@ public static void setTypeInfo(Builder builder, DatabendRawType type) { } public static Builder newBuilder(String name, DatabendRawType type) { - return (new Builder()).setColumnName(name).setColumnType(type.getDataType().getSqlType()); + return (new Builder()) + .setColumnName(name) + .setDatabendRawType(type); } public int getColumnType() { @@ -273,6 +277,11 @@ public Builder setColumnType(int columnType) { return this; } + public Builder setDatabendRawType(DatabendRawType type) { + this.type = type; + return this; + } + public void setColumnParameterTypes(List columnParameterTypes) { this.columnParameterTypes = ImmutableList.copyOf(requireNonNull(columnParameterTypes, "columnParameterTypes is null")); } diff --git a/databend-jdbc/src/main/java/com/databend/jdbc/DatabendParameterMetaData.java b/databend-jdbc/src/main/java/com/databend/jdbc/DatabendParameterMetaData.java new file mode 100644 index 00000000..c1d23199 --- /dev/null +++ b/databend-jdbc/src/main/java/com/databend/jdbc/DatabendParameterMetaData.java @@ -0,0 +1,89 @@ +package com.databend.jdbc; + +import java.sql.ParameterMetaData; +import java.sql.SQLException; +import java.sql.Types; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import static com.databend.jdbc.DatabendResultSetMetaData.getTypeClassName; +import static java.util.Objects.requireNonNull; + +public class DatabendParameterMetaData extends JdbcWrapper implements ParameterMetaData { + protected final List params; + protected final JdbcTypeMapping mapper; + + protected DatabendParameterMetaData(List params, JdbcTypeMapping mapper) { + this.params = requireNonNull(params, "connection is null"); + this.mapper = mapper; + } + + protected DatabendColumnInfo getParameter(int param) throws SQLException { + if (param < 1 || param > params.size()) { + throw new RuntimeException(format("Parameter index should between 1 and %d but we got %d", params.size(), param)); + } + + return params.get(param - 1); + } + + public static String format(String template, Object... args) { + return String.format(Locale.ROOT, template, args); + } + + @Override + public int getParameterCount() throws SQLException { + return params.size(); + } + + @Override + public int isNullable(int param) throws SQLException { + DatabendColumnInfo p = getParameter(param); + if (p == null) { + return ParameterMetaData.parameterNullableUnknown; + } + + return p.getType().isNullable() ? ParameterMetaData.parameterNullable : ParameterMetaData.parameterNoNulls; + } + + @Override + public boolean isSigned(int param) throws SQLException { + DatabendColumnInfo p = getParameter(param); + return p != null && p.isSigned(); + } + + @Override + public int getPrecision(int param) throws SQLException { + DatabendColumnInfo p = getParameter(param); + return p != null ? p.getPrecision() : 0; + } + + @Override + public int getScale(int param) throws SQLException { + DatabendColumnInfo p = getParameter(param); + return p != null ? p.getScale() : 0; + } + + @Override + public int getParameterType(int param) throws SQLException { + DatabendColumnInfo p = getParameter(param); + return p != null ? mapper.toSqlType(p) : Types.OTHER; + } + + @Override + public String getParameterTypeName(int param) throws SQLException { + DatabendColumnInfo p = getParameter(param); + return p != null ? p.getColumnTypeName() : ""; + } + + @Override + public String getParameterClassName(int param) throws SQLException { + DatabendColumnInfo p = getParameter(param); + return getTypeClassName(p.getColumnType()); + } + + @Override + public int getParameterMode(int param) throws SQLException { + return ParameterMetaData.parameterModeIn; + } +} diff --git a/databend-jdbc/src/main/java/com/databend/jdbc/DatabendPreparedStatement.java b/databend-jdbc/src/main/java/com/databend/jdbc/DatabendPreparedStatement.java index bfadd5c5..4ae8f8bd 100644 --- a/databend-jdbc/src/main/java/com/databend/jdbc/DatabendPreparedStatement.java +++ b/databend-jdbc/src/main/java/com/databend/jdbc/DatabendPreparedStatement.java @@ -1,6 +1,8 @@ package com.databend.jdbc; import com.databend.client.StageAttachment; +import com.databend.client.data.DatabendDataType; +import com.databend.client.data.DatabendRawType; import com.databend.jdbc.cloud.DatabendCopyParams; import com.databend.jdbc.cloud.DatabendStage; import com.databend.jdbc.parser.BatchInsertUtils; @@ -63,6 +65,7 @@ public class DatabendPreparedStatement extends DatabendStatement implements Prep private final RawStatementWrapper rawStatement; static final DateTimeFormatter TIME_FORMATTER = DateTimeFormat.forPattern("HH:mm:ss.SSS"); static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS"); + private final DatabendParameterMetaData paramMetaData; private static final java.time.format.DateTimeFormatter LOCAL_DATE_TIME_FORMATTER = new DateTimeFormatterBuilder() .append(ISO_LOCAL_DATE) @@ -87,6 +90,13 @@ public class DatabendPreparedStatement extends DatabendStatement implements Prep this.batchValues = new ArrayList<>(); this.batchInsertUtils = BatchInsertUtils.tryParseInsertSql(sql); this.rawStatement = StatementUtil.parseToRawStatementWrapper(sql); + Map params = StatementUtil.extractColumnTypes(sql); + List list = params.entrySet().stream().map(entry -> { + String type = entry.getValue(); + DatabendRawType databendRawType = new DatabendRawType(type); + return DatabendColumnInfo.of(entry.getKey().toString(), databendRawType); + }).collect(Collectors.toList()); + this.paramMetaData = new DatabendParameterMetaData(Collections.unmodifiableList(list), new JdbcTypeMapping()); } private static String formatBooleanLiteral(boolean x) { @@ -199,6 +209,7 @@ private StageAttachment uploadBatches() throws SQLException { LocalDateTime.now().getSecond(), uuid); String fileName = saved.getName(); + // upload to stage c.uploadStream(null, stagePrefix, fis, fileName, saved.length(), false); String stagePath = "@~/" + stagePrefix + fileName; StageAttachment attachment = buildStateAttachment(c, stagePath); @@ -753,9 +764,13 @@ public void setBlob(int i, Blob x) } @Override - public void setClob(int i, Clob clob) + public void setClob(int i, Clob x) throws SQLException { - throw new SQLFeatureNotSupportedException("PreparedStatement", "setClob"); + if (x != null) { + setCharacterStream(i, x.getCharacterStream()); + } else { + setNull(i, Types.CLOB); + } } @Override @@ -800,10 +815,11 @@ public void setURL(int i, URL url) throw new SQLFeatureNotSupportedException("PreparedStatement", "setURL"); } + // If you want to use ps.getParameterMetaData().* methods, you need to use a valid sql such as + // insert into table_name (col1 type1, col2 typ2, col3 type3) values (?, ?, ?) @Override - public ParameterMetaData getParameterMetaData() - throws SQLException { - return null; + public ParameterMetaData getParameterMetaData() throws SQLException { + return paramMetaData; } @Override @@ -951,6 +967,7 @@ public void setNClob(int i, Reader reader) throw new SQLFeatureNotSupportedException("PreparedStatement", "setNClob"); } + private String toDateLiteral(Object value) throws IllegalArgumentException { requireNonNull(value, "value is null"); if (value instanceof java.util.Date) { diff --git a/databend-jdbc/src/main/java/com/databend/jdbc/DatabendResultSetMetaData.java b/databend-jdbc/src/main/java/com/databend/jdbc/DatabendResultSetMetaData.java index 8d492ea0..9dfe335e 100644 --- a/databend-jdbc/src/main/java/com/databend/jdbc/DatabendResultSetMetaData.java +++ b/databend-jdbc/src/main/java/com/databend/jdbc/DatabendResultSetMetaData.java @@ -19,7 +19,7 @@ public class DatabendResultSetMetaData implements ResultSetMetaData { this.databendColumnInfo = databendColumnInfo; } - static String getType(int type) { + static String getTypeClassName(int type) { // see javax.sql.rowset.RowSetMetaDataImpl switch (type) { case Types.NUMERIC: @@ -198,7 +198,7 @@ public boolean isDefinitelyWritable(int i) @Override public String getColumnClassName(int i) throws SQLException { - return getType(column(i).getColumnType()); + return getTypeClassName(column(i).getColumnType()); } @Override diff --git a/databend-jdbc/src/main/java/com/databend/jdbc/JdbcTypeMapping.java b/databend-jdbc/src/main/java/com/databend/jdbc/JdbcTypeMapping.java new file mode 100644 index 00000000..09f0f052 --- /dev/null +++ b/databend-jdbc/src/main/java/com/databend/jdbc/JdbcTypeMapping.java @@ -0,0 +1,149 @@ +package com.databend.jdbc; + +import com.databend.client.data.DatabendDataType; + +import java.sql.Types; +import java.util.Map; + +public class JdbcTypeMapping { + /** + * Converts {@link DatabendColumnInfo} to generic SQL type defined in JDBC. + * + * @param column non-null column definition + * @return generic SQL type defined in JDBC + */ + public int toSqlType(DatabendColumnInfo column) { + DatabendDataType dataType = column.getType().getDataType(); + int sqlType = Types.OTHER; + switch (dataType) { + case BOOLEAN: + sqlType = Types.BOOLEAN; + break; + case INT_8: + sqlType = Types.TINYINT; + break; + case INT_16: + sqlType = Types.SMALLINT; + break; + case INT_32: + sqlType = Types.INTEGER; + break; + case INT_64: + sqlType = Types.BIGINT; + break; + case FLOAT: + sqlType = Types.FLOAT; + break; + case DOUBLE: + sqlType = Types.DOUBLE; + break; + case DECIMAL: + sqlType = Types.DECIMAL; + break; + case STRING: + sqlType = Types.VARCHAR; + break; + case DATE: + sqlType = Types.DATE; + break; + case TIMESTAMP: + sqlType = Types.TIMESTAMP; + break; + case ARRAY: + sqlType = Types.ARRAY; + break; + case VARIANT: + sqlType = Types.VARCHAR; + break; + case TUPLE: + sqlType = Types.STRUCT; + break; + case NULL: + sqlType = Types.NULL; + break; + default: + break; + } + return sqlType; + } + + /** + * Gets corresponding {@link DatabendDataType} of the given {@link Types}. + * + * @param sqlType generic SQL types defined in JDBC + * @return non-null Databend data type + */ + protected DatabendDataType getDataType(int sqlType) { + DatabendDataType dataType; + + switch (sqlType) { + case Types.BOOLEAN: + dataType = DatabendDataType.UNSIGNED_INT_8; + break; + case Types.TINYINT: + dataType = DatabendDataType.INT_8; + break; + case Types.SMALLINT: + dataType = DatabendDataType.INT_16; + break; + case Types.INTEGER: + dataType = DatabendDataType.INT_32; + break; + case Types.BIGINT: + dataType = DatabendDataType.INT_64; + break; + case Types.FLOAT: + dataType = DatabendDataType.FLOAT; + break; + case Types.DOUBLE: + dataType = DatabendDataType.DOUBLE; + break; + case Types.DECIMAL: + dataType = DatabendDataType.DECIMAL; + break; + case Types.BIT: + case Types.BLOB: + case Types.BINARY: + case Types.CHAR: + case Types.CLOB: + case Types.JAVA_OBJECT: + case Types.LONGNVARCHAR: + case Types.LONGVARBINARY: + case Types.LONGVARCHAR: + case Types.NCHAR: + case Types.NCLOB: + case Types.NVARCHAR: + case Types.OTHER: + case Types.SQLXML: + case Types.VARBINARY: + case Types.VARCHAR: + dataType = DatabendDataType.STRING; + break; + case Types.DATE: + dataType = DatabendDataType.DATE; + break; + case Types.TIME: + case Types.TIME_WITH_TIMEZONE: + case Types.TIMESTAMP: + case Types.TIMESTAMP_WITH_TIMEZONE: + dataType = DatabendDataType.TIMESTAMP; + break; + case Types.ARRAY: + dataType = DatabendDataType.ARRAY; + break; + case Types.STRUCT: + dataType = DatabendDataType.TUPLE; + break; + case Types.DATALINK: + case Types.DISTINCT: + case Types.REF: + case Types.REF_CURSOR: + case Types.ROWID: + case Types.NULL: + default: + dataType = DatabendDataType.NULL; + break; + } + return dataType; + } +} diff --git a/databend-jdbc/src/main/java/com/databend/jdbc/JdbcWrapper.java b/databend-jdbc/src/main/java/com/databend/jdbc/JdbcWrapper.java new file mode 100644 index 00000000..70fb383b --- /dev/null +++ b/databend-jdbc/src/main/java/com/databend/jdbc/JdbcWrapper.java @@ -0,0 +1,22 @@ +package com.databend.jdbc; + +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; + +public abstract class JdbcWrapper { + public T unwrap(Class iface) throws SQLException { + if (iface.isAssignableFrom(getClass())) { + return iface.cast(this); + } + + throw unsupportedError("Cannot unwrap to " + iface.getName()); + } + + public boolean isWrapperFor(Class iface) throws SQLException { + return iface.isAssignableFrom(getClass()); + } + + public static SQLFeatureNotSupportedException unsupportedError(String message) { + return new SQLFeatureNotSupportedException(message); + } +} diff --git a/databend-jdbc/src/main/java/com/databend/jdbc/StatementUtil.java b/databend-jdbc/src/main/java/com/databend/jdbc/StatementUtil.java index 9d0052f2..a4680ba7 100644 --- a/databend-jdbc/src/main/java/com/databend/jdbc/StatementUtil.java +++ b/databend-jdbc/src/main/java/com/databend/jdbc/StatementUtil.java @@ -1,6 +1,7 @@ package com.databend.jdbc; import java.util.*; +import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -51,6 +52,30 @@ public Optional> extractParamFromSetStatement(@NonNull Stri return Optional.empty(); } + /** + * This method is used to extract column types from a SQL statement. + * It parses the SQL statement and finds the column types defined in the first pair of parentheses. + * The column types are then stored in a Map where the key is the index of the column in the SQL statement + * and the value is the type of the column. + * + * @param sql The SQL statement from which to extract column types. + * @return A Map where the key is the index of the column and the value is the type of the column. + */ + public static Map extractColumnTypes(String sql) { + Map columnTypes = new LinkedHashMap<>(); + Pattern pattern = Pattern.compile("\\((.*?)\\)"); + Matcher matcher = pattern.matcher(sql); + if (matcher.find()) { + String[] columns = matcher.group(1).split(","); + for (int i = 0; i < columns.length; i++) { + String column = columns[i].trim(); + String type = column.substring(column.lastIndexOf(' ') + 1).replace("'", ""); + columnTypes.put(i, type); + } + } + return columnTypes; + } + /** * Parse the sql statement to a list of {@link StatementInfoWrapper} * diff --git a/databend-jdbc/src/main/java/com/databend/jdbc/cloud/DatabendPresignClientV1.java b/databend-jdbc/src/main/java/com/databend/jdbc/cloud/DatabendPresignClientV1.java index 0ecef6fb..23779985 100644 --- a/databend-jdbc/src/main/java/com/databend/jdbc/cloud/DatabendPresignClientV1.java +++ b/databend-jdbc/src/main/java/com/databend/jdbc/cloud/DatabendPresignClientV1.java @@ -103,6 +103,7 @@ private ResponseBody executeInternal(Request request, boolean shouldClose) throw Exception cause = null; while (true) { if (attempts > 0) { + logger.info("try to presign upload again: " + attempts); Duration sinceStart = Duration.ofNanos(System.nanoTime() - start); if (sinceStart.getSeconds() >= 600) { logger.warning("Presign upload failed, error is:" + cause.toString()); diff --git a/databend-jdbc/src/main/java/com/databend/jdbc/examples/Examples.java b/databend-jdbc/src/main/java/com/databend/jdbc/examples/Examples.java index 2f56f401..f23c87f8 100644 --- a/databend-jdbc/src/main/java/com/databend/jdbc/examples/Examples.java +++ b/databend-jdbc/src/main/java/com/databend/jdbc/examples/Examples.java @@ -43,7 +43,7 @@ public static void main(String[] args) throws SQLException { try (PreparedStatement statement = conn.prepareStatement(updateSQL)) { statement.setInt(2, 1); // Attention: now setString(1, "c") will throw exception, need to setString(1, "'c'") - statement.setString(1, "'c'"); + statement.setString(1, "c"); int result = statement.executeUpdate(); System.out.println(result); } diff --git a/databend-jdbc/src/test/java/com/databend/jdbc/StatementUtilTest.java b/databend-jdbc/src/test/java/com/databend/jdbc/StatementUtilTest.java index d374e309..dc7af752 100644 --- a/databend-jdbc/src/test/java/com/databend/jdbc/StatementUtilTest.java +++ b/databend-jdbc/src/test/java/com/databend/jdbc/StatementUtilTest.java @@ -13,6 +13,7 @@ public class StatementUtilTest { void shouldGetAllQueryParamsFromIn() { String sql = "SElECT * FROM EMPLOYEES WHERE id IN (?,?)"; assertEquals(ImmutableMap.of(1, 37, 2, 39), StatementUtil.getParamMarketsPositions(sql)); + System.out.println(StatementUtil.parseToRawStatementWrapper(sql).getSubStatements()); assertEquals(1, StatementUtil.parseToRawStatementWrapper(sql).getSubStatements().size()); } @Test diff --git a/databend-jdbc/src/test/java/com/databend/jdbc/TestDatabendParameterMetaData.java b/databend-jdbc/src/test/java/com/databend/jdbc/TestDatabendParameterMetaData.java new file mode 100644 index 00000000..e241c062 --- /dev/null +++ b/databend-jdbc/src/test/java/com/databend/jdbc/TestDatabendParameterMetaData.java @@ -0,0 +1,82 @@ +package com.databend.jdbc; + +import com.databend.client.data.DatabendDataType; +import org.testng.Assert; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.sql.*; +import java.util.Properties; + + +public class TestDatabendParameterMetaData { + + private Connection createConnection() + throws SQLException { + String url = "jdbc:databend://localhost:8000"; + return DriverManager.getConnection(url, "databend", "databend"); + } + + private Connection createConnection(boolean presignDisabled) throws SQLException { + String url = "jdbc:databend://localhost:8000?presigned_url_disabled=" + presignDisabled; + return DriverManager.getConnection(url, "databend", "databend"); + } + + @BeforeTest + public void setUp() + throws SQLException { + // create table + Connection c = createConnection(); + System.out.println("-----------------"); + System.out.println("drop all existing test table"); + } + + + @Test(groups = "integration") + public void testGetParameterMetaData() throws SQLException { + try (Connection conn = createConnection(); + PreparedStatement emptyPs = conn.prepareStatement("select 1"); + // If you want to use ps.getParameterMetaData().* methods, you need to use a valid sql such as + // insert into table_name (col1 type1, col2 typ2, col3 type3) values (?, ?, ?) + PreparedStatement inputPs = conn.prepareStatement( + "insert into non_existing_table ('col2 String, col3 Int8, col1 String') values (?, ?, ?)"); + PreparedStatement sqlPs = conn.prepareStatement("insert into test_table (a int, b int, c string) values (?,?,?)");) { + Assert.assertEquals(emptyPs.getParameterMetaData().getParameterCount(), 0); + + for (PreparedStatement ps : new PreparedStatement[]{inputPs, sqlPs}) { + Assert.assertNotNull(ps.getParameterMetaData()); + Assert.assertTrue(ps.getParameterMetaData() == ps.getParameterMetaData(), + "parameter mete data should be singleton"); + Assert.assertEquals(ps.getParameterMetaData().getParameterCount(), 3); + Assert.assertEquals(ps.getParameterMetaData().getParameterMode(3), ParameterMetaData.parameterModeIn); + Assert.assertEquals(ps.getParameterMetaData().getParameterType(3), Types.VARCHAR); + Assert.assertEquals(ps.getParameterMetaData().getPrecision(3), 1024 * 1024 * 1024); + Assert.assertEquals(ps.getParameterMetaData().getScale(3), 0); + Assert.assertEquals(ps.getParameterMetaData().getParameterClassName(3), String.class.getName()); + Assert.assertEquals(ps.getParameterMetaData().getParameterTypeName(3), DatabendDataType.STRING.name().toLowerCase()); + } + } + + try (Connection conn = createConnection(); + PreparedStatement ps = conn.prepareStatement("insert into test_table (a int, b int) values (?,?)");) { + Assert.assertEquals(ps.getParameterMetaData().getParameterCount(), 2); + Assert.assertEquals(ps.getParameterMetaData().getParameterMode(2), ParameterMetaData.parameterModeIn); + Assert.assertEquals(ps.getParameterMetaData().getParameterType(2), Types.INTEGER); + Assert.assertEquals(ps.getParameterMetaData().getPrecision(2), 10); + Assert.assertEquals(ps.getParameterMetaData().getScale(2), 0); + Assert.assertEquals(ps.getParameterMetaData().getParameterClassName(2), Integer.class.getName()); + Assert.assertEquals(ps.getParameterMetaData().getParameterTypeName(2), DatabendDataType.INT_32.getDisplayName().toLowerCase()); + } + + try (Connection conn = createConnection(); + PreparedStatement ps = conn.prepareStatement("insert into test_table (a int, b VARIANT) values (?,?)");) { + Assert.assertEquals(ps.getParameterMetaData().getParameterCount(), 2); + Assert.assertEquals(ps.getParameterMetaData().getParameterMode(2), ParameterMetaData.parameterModeIn); + Assert.assertEquals(ps.getParameterMetaData().getParameterType(2), Types.VARCHAR); + Assert.assertEquals(ps.getParameterMetaData().getPrecision(2), 0); + Assert.assertEquals(ps.getParameterMetaData().getScale(2), 0); + Assert.assertEquals(ps.getParameterMetaData().getParameterClassName(2), String.class.getName()); + Assert.assertEquals(ps.getParameterMetaData().getParameterTypeName(2), DatabendDataType.VARIANT.getDisplayName().toLowerCase()); + } + } +} diff --git a/databend-jdbc/src/test/java/com/databend/jdbc/TestStatementUtil.java b/databend-jdbc/src/test/java/com/databend/jdbc/TestStatementUtil.java new file mode 100644 index 00000000..6304bee9 --- /dev/null +++ b/databend-jdbc/src/test/java/com/databend/jdbc/TestStatementUtil.java @@ -0,0 +1,20 @@ +package com.databend.jdbc; + +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestStatementUtil { + @Test + public void testExtractColumnTypes() { + String sql = "insert into non_existing_table ('col2 String, col3 Int8, col1 VARIANT') values (?, ?, ?)"; + Map columnTypes = StatementUtil.extractColumnTypes(sql); + + assertEquals(3, columnTypes.size()); + assertEquals("String", columnTypes.get(0)); + assertEquals("Int8", columnTypes.get(1)); + assertEquals("VARIANT", columnTypes.get(2)); + } +}