Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clickhouse-jdbc/docs/datetime.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ The methods which take a [Calendar]((https://docs.oracle.com/javase/8/docs/api/j
For Date and DateTime fields, the JDBC driver has enough time zone related information available, so these methods would only be relevant for String or other typed fields. There might be valid use cases, but for now we think that adding such an option would make things even more complicated.

Requested Type | Number | Date | DateTime | Other
---------------| ----------------------------------
---------------| -------|------|----------|--------
[Date](https://docs.oracle.com/javase/8/docs/api/java/sql/Date.html) | Seconds or milliseconds past epoch truncated to day in relevant time zone | Date in relevant time zone, midnight | Date time in relevant time zone, rewind to midnight | Try number, date time (with or without offset) truncated to day, date
[Time](https://docs.oracle.com/javase/8/docs/api/java/sql/Time.html) | Local time at 1970-01-01 (e.g. “1337” is “13:37:00” at TZ) | Midnight on 1970-01-01 in relevant time zone | Local time in relevant time zone | Local time in relevant time zone via ISO format or via number, at 1970-01-01
[Timestamp](https://docs.oracle.com/javase/8/docs/api/java/sql/Timestamp.html) | Seconds or milliseconds past epoch | Local date at midnight in relevant time zone | Local date and time in relevant time zone | Number, date time with or without offset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,22 +209,25 @@ public String nativeSQL(String sql) throws SQLException {

@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {

if (autoCommit) {
return;
}
throw new SQLFeatureNotSupportedException("Transactions are not supported");
}

@Override
public boolean getAutoCommit() throws SQLException {
return false;
return true;
}

@Override
public void commit() throws SQLException {

throw new SQLException("Cannot commit when auto-commit is enabled");
}

@Override
public void rollback() throws SQLException {

throw new SQLException("Cannot rollback when auto-commit is enabled");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ public void addBatch(String sql) throws SQLException {

@Override
public void addBatch() throws SQLException {
if (parsedStmt.getStatementType() == StatementType.INSERT) {
if (parsedStmt.getStatementType() == StatementType.INSERT && parsedStmt.hasValues()) {
batchRows.addAll(buildBatch());
} else {
batchStmts.add(buildSql());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import ru.yandex.clickhouse.except.ClickHouseExceptionSpecifier;
import ru.yandex.clickhouse.jdbc.parser.ClickHouseSqlParser;
import ru.yandex.clickhouse.jdbc.parser.ClickHouseSqlStatement;
import ru.yandex.clickhouse.jdbc.parser.StatementType;
import ru.yandex.clickhouse.response.ClickHouseLZ4Stream;
import ru.yandex.clickhouse.response.ClickHouseResponse;
import ru.yandex.clickhouse.response.ClickHouseResponseSummary;
Expand Down Expand Up @@ -141,7 +142,7 @@ public boolean isStreaming() {
* between creation of this object and query execution, but javadoc does not allow
* {@code setCatalog} influence on already created statements.
*/
private final String initialDatabase;
protected String currentDatabase;

protected ClickHouseSqlStatement getLastStatement() {
ClickHouseSqlStatement stmt = null;
Expand Down Expand Up @@ -303,7 +304,7 @@ public ClickHouseStatementImpl(CloseableHttpClient client, ClickHouseConnection
this.httpContext = ClickHouseHttpClientBuilder.createClientContext(properties);
this.connection = connection;
this.properties = properties == null ? new ClickHouseProperties() : properties;
this.initialDatabase = this.properties.getDatabase();
this.currentDatabase = this.properties.getDatabase();
this.isResultSetScrollable = (resultSetType != ResultSet.TYPE_FORWARD_ONLY);

this.batchStmts = new ArrayList<>();
Expand Down Expand Up @@ -709,7 +710,11 @@ private InputStream getInputStream(
Map<String, String> additionalRequestParams
) throws ClickHouseException {
String sql = parsedStmt.getSQL();
boolean ignoreDatabase = parsedStmt.isRecognized() && !parsedStmt.isDML();
boolean ignoreDatabase = parsedStmt.isRecognized() && !parsedStmt.isDML()
&& parsedStmt.containsKeyword("DATABASE");
if (parsedStmt.getStatementType() == StatementType.USE) {
currentDatabase = parsedStmt.getDatabaseOrDefault(currentDatabase);
}

log.debug("Executing SQL: {}", sql);

Expand Down Expand Up @@ -866,7 +871,7 @@ private List<NameValuePair> getUrlQueryParams(

Map<ClickHouseQueryParam, String> params = properties.buildQueryParams(true);
if (!ignoreDatabase) {
params.put(ClickHouseQueryParam.DATABASE, initialDatabase);
params.put(ClickHouseQueryParam.DATABASE, currentDatabase);
}

params.putAll(getAdditionalDBParams());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,22 +188,24 @@ public void writeFloat64(double value) throws IOException {
Utils.writeLong(out, Double.doubleToLongBits(value));
}

public void writeDecimal128(BigDecimal num, int scale) throws IOException {
BigInteger bi = Utils.toBigInteger(num, scale);
byte[] r = bi.toByteArray();
for (int i = r.length; i > 0; i--) {
out.write(r[i - 1]);
public void writeBigInteger(BigInteger value, int byteLength) throws IOException {
byte empty = value.signum() == -1 ? (byte) 0xFF : 0x00;
byte[] bytes = value.toByteArray();
for (int i = bytes.length - 1; i >= 0; i--) {
out.write(bytes[i]);
}

for (int i = byteLength - bytes.length; i > 0; i--) {
out.write(empty);
}
out.write(new byte[16 - r.length]);
}

public void writeDecimal128(BigDecimal num, int scale) throws IOException {
writeBigInteger(Utils.toBigInteger(num, scale), 16);
}

public void writeDecimal256(BigDecimal num, int scale) throws IOException {
BigInteger bi = Utils.toBigInteger(num, scale);
byte[] r = bi.toByteArray();
for (int i = r.length; i > 0; i--) {
out.write(r[i - 1]);
}
out.write(new byte[32 - r.length]);
writeBigInteger(Utils.toBigInteger(num, scale), 32);
}

public void writeDecimal64(BigDecimal num, int scale) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ru.yandex.clickhouse.integration;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

Expand All @@ -13,8 +14,8 @@
import ru.yandex.clickhouse.except.ClickHouseException;
import ru.yandex.clickhouse.settings.ClickHouseProperties;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;

Expand Down Expand Up @@ -75,6 +76,69 @@ public void testOofWrongPassword() throws Exception {
assertFailure(createDataSource("oof", "baz"));
}

@Test
public void testDefaultDatabase() throws Exception {
ClickHouseDataSource ds = ClickHouseContainerForTest.newDataSource();
String currentDbQuery = "select currentDatabase()";
try (Connection conn = ds.getConnection(); Statement s = conn.createStatement()) {
try (ResultSet rs = s.executeQuery(currentDbQuery)) {
assertTrue(rs.next());
assertEquals(rs.getString(1), "default");
assertFalse(rs.next());
}

PreparedStatement p = conn.prepareStatement(currentDbQuery);
try (ResultSet rs = p.executeQuery()) {
assertTrue(rs.next());
assertEquals(rs.getString(1), "default");
assertFalse(rs.next());
}
s.execute("create database if not exists tdb1; create database if not exists tdb2");
}

ds = ClickHouseContainerForTest.newDataSource("tdb2");
try (Connection conn = ds.getConnection(); Statement s = conn.createStatement()) {
try (ResultSet rs = s.executeQuery(currentDbQuery)) {
assertTrue(rs.next());
assertEquals(rs.getString(1), "tdb2");
assertFalse(rs.next());
}

s.execute("create table tdb2_aaa(a String) engine=Memory; insert into tdb2_aaa values('3')");

try (ResultSet rs = s.executeQuery("select currentDatabase(), a from tdb2_aaa")) {
assertTrue(rs.next());
assertEquals(rs.getString(1), "tdb2");
assertEquals(rs.getString(2), "3");
assertFalse(rs.next());
}

s.execute("use tdb1; create table tdb1_aaa(a String) engine=Memory; insert into tdb1_aaa values('1')");

try (ResultSet rs = s.executeQuery("select currentDatabase(), a from tdb1_aaa")) {
assertTrue(rs.next());
assertEquals(rs.getString(1), "tdb1");
assertEquals(rs.getString(2), "1");
assertFalse(rs.next());
}

try (ResultSet rs = s.executeQuery("use `tdb2`; select currentDatabase(), a from tdb2_aaa")) {
assertTrue(rs.next());
assertEquals(rs.getString(1), "tdb2");
assertEquals(rs.getString(2), "3");
assertFalse(rs.next());
}

String sql = "select currentDatabase(), a from tdb2_aaa";
try (PreparedStatement p = conn.prepareStatement(sql); ResultSet rs = p.executeQuery()) {
assertTrue(rs.next());
assertEquals(rs.getString(1), "tdb2");
assertEquals(rs.getString(2), "3");
assertFalse(rs.next());
}
}
}

private static void assertSuccess(DataSource dataSource) throws Exception {
Connection connection = dataSource.getConnection();
assertTrue(connection.createStatement().execute("SELECT 1"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import ru.yandex.clickhouse.ClickHouseContainerForTest;
import ru.yandex.clickhouse.ClickHouseDataSource;
import ru.yandex.clickhouse.ClickHouseDatabaseMetadata;
import ru.yandex.clickhouse.util.ClickHouseVersionNumberUtil;

public class ClickHouseDatabaseMetadataTest {

Expand Down Expand Up @@ -159,7 +160,15 @@ public void testToDateTimeTZ() throws Exception {
ResultSetMetaData meta = rs.getMetaData();
Assert.assertEquals(meta.getColumnClassName(1), Timestamp.class.getCanonicalName());
TimeZone timezone = ((ClickHouseConnection) connection).getTimeZone();
Assert.assertEquals(meta.getColumnTypeName(1), "DateTime('" + timezone.getID() + "')");
String version = ((ClickHouseConnection) connection).getServerVersion();
int majorVersion = ClickHouseVersionNumberUtil.getMajorVersion(version);
int minorVersion = ClickHouseVersionNumberUtil.getMinorVersion(version);
if (majorVersion > 21 || (majorVersion == 21 && minorVersion >= 6)) {
Assert.assertEquals(meta.getColumnTypeName(1), "DateTime");
} else {
Assert.assertEquals(meta.getColumnTypeName(1), "DateTime('" + timezone.getID() + "')");
}

Assert.assertEquals(meta.getColumnType(1), Types.TIMESTAMP);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,44 +111,55 @@ public void testMaps() throws Exception {
return;
}

// skip 21.4
String serverVersion = conn.getServerVersion();
if (ClickHouseVersionNumberUtil.getMajorVersion(serverVersion) == 21
&& ClickHouseVersionNumberUtil.getMinorVersion(serverVersion) == 4) {
String columns = ", ma Map(Integer, Array(String)), mi Map(Integer, Integer)";
String values = ",1:['11','12'],2:['22','23']},{1:11,2:22}";
String version = ((ClickHouseConnection) conn).getServerVersion();
int majorVersion = ClickHouseVersionNumberUtil.getMajorVersion(version);
int minorVersion = ClickHouseVersionNumberUtil.getMinorVersion(version);
if (majorVersion > 21 || (majorVersion == 21 && minorVersion >= 3)) {
// https://github.com/ClickHouse/ClickHouse/issues/25026
columns = "";
values = "";

return;
}

try (Statement s = conn.createStatement()) {
s.execute("DROP TABLE IF EXISTS test_maps");
s.execute(
"CREATE TABLE IF NOT EXISTS test_maps(ma Map(Integer, Array(String)), mi Map(Integer, Integer), ms Map(String, String)) ENGINE = Memory");
s.execute("insert into test_maps values ({1:['11','12'],2:['22','23']},{1:11,2:22},{'k1':'v1','k2':'v2'})");
s.execute("CREATE TABLE IF NOT EXISTS test_maps(ms Map(String, String)" + columns + ") ENGINE = Memory");
s.execute("insert into test_maps values ({{'k1':'v1','k2':'v2'}" + values + ")");

try (ResultSet rs = s.executeQuery("select * from test_maps")) {
assertTrue(rs.next());
assertMap(rs.getObject("ma"),
Utils.mapOf(1, new String[] { "11", "12" }, 2, new String[] { "22", "23" }));
assertMap(rs.getObject("mi"), Utils.mapOf(1, 11, 2, 22));
assertMap(rs.getObject("ms"), Utils.mapOf("k1", "v1", "k2", "v2"));
if (!columns.isEmpty()) {
assertMap(rs.getObject("ma"),
Utils.mapOf(1, new String[] { "11", "12" }, 2, new String[] { "22", "23" }));
assertMap(rs.getObject("mi"), Utils.mapOf(1, 11, 2, 22));
}
}

s.execute("truncate table test_maps");
}

try (PreparedStatement s = conn.prepareStatement("insert into test_maps values(?,?,?)")) {
s.setObject(1, Utils.mapOf(1, new String[] { "11", "12" }, 2, new String[] { "22", "23" }));
s.setObject(2, Utils.mapOf(1, 11, 2, 22));
s.setObject(3, Utils.mapOf("k1", "v1", "k2", "v2"));
s.setObject(1, Utils.mapOf("k1", "v1", "k2", "v2"));
if (!columns.isEmpty()) {
s.setObject(2, Utils.mapOf(1, new String[] { "11", "12" }, 2, new String[] { "22", "23" }));
s.setObject(3, Utils.mapOf(1, 11, 2, 22));
}
s.execute();
}

try (Statement s = conn.createStatement()) {
try (ResultSet rs = s.executeQuery("select * from test_maps")) {
assertTrue(rs.next());
assertMap(rs.getObject("ma"),
Utils.mapOf(1, new String[] { "11", "12" }, 2, new String[] { "22", "23" }));
assertMap(rs.getObject("mi"), Utils.mapOf(1, 11, 2, 22));
assertMap(rs.getObject("ms"), Utils.mapOf("k1", "v1", "k2", "v2"));
if (!columns.isEmpty()) {
assertMap(rs.getObject("ma"),
Utils.mapOf(1, new String[] { "11", "12" }, 2, new String[] { "22", "23" }));
assertMap(rs.getObject("mi"), Utils.mapOf(1, 11, 2, 22));
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertThrows;
import static org.testng.Assert.assertTrue;

import java.io.EOFException;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.ResultSet;
Expand Down Expand Up @@ -276,6 +276,46 @@ public void testBitmap() throws Exception {
}
}

@Test
public void testBigDecimals() throws Exception {
try (ClickHouseStatement statement = connection.createStatement()) {
statement.execute("set allow_experimental_bigint_types=1;"
+ "create table if not exists test.test_big_decimals(d128 Decimal128(6), d256 Decimal256(12)) engine=Memory");
} catch (SQLException e) {
return;
}

try (ClickHouseStatement statement = connection.createStatement()) {
BigDecimal[] values = new BigDecimal[] {
BigDecimal.valueOf(-123.123456789D),
BigDecimal.ZERO,
BigDecimal.valueOf(123.123456789D)
};
statement.sendRowBinaryStream("insert into table test.test_big_decimals", new ClickHouseStreamCallback() {
@Override
public void writeTo(ClickHouseRowBinaryStream stream) throws IOException {
for (int i = 0; i < values.length; i++) {
stream.writeDecimal128(values[i], 6);
stream.writeDecimal256(values[i], 12);
}
}
});

try (ResultSet rs = statement.executeQuery("select * from test.test_big_decimals order by d128")) {
int rowCounter = 0;
while (rs.next()) {
rowCounter++;
assertEquals(rs.getBigDecimal(1, 6), values[rowCounter - 1].setScale(6, BigDecimal.ROUND_DOWN));
assertEquals(rs.getBigDecimal(2, 12), values[rowCounter - 1].setScale(12, BigDecimal.ROUND_DOWN));
}

assertEquals(rowCounter, values.length);
}

statement.execute("drop table if exists test.test_big_decimals");
}
}

private void testRowBinaryStream(boolean rowBinaryResult) throws Exception {
createTable("test.raw_binary");
ClickHouseStatement statement = connection.createStatement();
Expand Down
Loading