Skip to content

Commit dc86bc8

Browse files
author
Paultagoras
committed
Adding the RowBinary methods
1 parent 5ea0e26 commit dc86bc8

File tree

5 files changed

+956
-57
lines changed

5 files changed

+956
-57
lines changed

jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java

Lines changed: 166 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.clickhouse.jdbc;
22

3+
import com.clickhouse.client.api.insert.InsertSettings;
4+
import com.clickhouse.client.api.metadata.TableSchema;
35
import org.slf4j.Logger;
46
import org.slf4j.LoggerFactory;
57

@@ -33,9 +35,11 @@
3335
import java.time.format.DateTimeFormatter;
3436
import java.time.format.DateTimeFormatterBuilder;
3537
import java.time.temporal.ChronoField;
38+
import java.util.ArrayList;
3639
import java.util.Calendar;
3740
import java.util.Collection;
3841
import java.util.GregorianCalendar;
42+
import java.util.List;
3943
import java.util.Map;
4044

4145
public class PreparedStatementImpl extends StatementImpl implements PreparedStatement, JdbcV2Wrapper {
@@ -48,20 +52,50 @@ public class PreparedStatementImpl extends StatementImpl implements PreparedStat
4852
.appendPattern("yyyy-MM-dd HH:mm:ss").appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).toFormatter();
4953

5054
String originalSql;
51-
String [] sqlSegments;
52-
Object [] parameters;
53-
public PreparedStatementImpl(ConnectionImpl connection, String sql) {
55+
String[] sqlSegments;
56+
Object[] parameters;
57+
int setCalls;//The number of times set* methods have been called
58+
59+
//For insertRowBinary fanciness
60+
StatementType statementType;
61+
boolean insertRowBinary;
62+
List<Object> insertRowBinaryData;
63+
String tableName;
64+
TableSchema tableSchema;
65+
66+
67+
public PreparedStatementImpl(ConnectionImpl connection, String sql) throws SQLException {
5468
super(connection);
55-
this.originalSql = sql;
56-
//Split the sql string into an array of strings around question mark tokens
57-
this.sqlSegments = sql.split("\\?");
58-
59-
//Create an array of objects to store the parameters
60-
if (originalSql.contains("?")) {
61-
int count = originalSql.length() - originalSql.replace("?", "").length();
62-
this.parameters = new Object[count];
63-
} else {
64-
this.parameters = new Object[0];
69+
70+
try {
71+
this.originalSql = sql;
72+
this.setCalls = 0;
73+
this.statementType = parseStatementType(sql);
74+
this.insertRowBinary = false;
75+
this.insertRowBinaryData = new ArrayList<>();
76+
this.tableName = null;
77+
this.tableSchema = null;
78+
79+
//Split the sql string into an array of strings around question mark tokens
80+
this.sqlSegments = sql.split("\\?");
81+
82+
//Create an array of objects to store the parameters
83+
if (originalSql.contains("?")) {
84+
int count = originalSql.length() - originalSql.replace("?", "").length();
85+
this.parameters = new Object[count];
86+
} else {
87+
if (statementType != StatementType.INSERT) {
88+
throw new SQLException("SQL prepared statement does not contain any placeholders.");
89+
}
90+
91+
this.parameters = new Object[0];
92+
this.insertRowBinary = true;//We only use this when no parameters are present and the statement is an insert
93+
this.tableName = parseTableName(sql);
94+
this.tableSchema = connection.client.getTableSchema(tableName);
95+
}
96+
} catch (Exception e) {
97+
LOG.error("Error creating prepared statement", e);
98+
throw new SQLException("Error creating prepared statement", e);
6599
}
66100
}
67101

@@ -77,16 +111,91 @@ private String compileSql() {
77111
return sb.toString();
78112
}
79113

114+
private boolean enoughParameters() {
115+
return this.parameters.length == setCalls || insertRowBinary;
116+
}
117+
80118
@Override
81119
public ResultSet executeQuery() throws SQLException {
82120
checkClosed();
83-
return executeQuery(compileSql());
121+
122+
if (!enoughParameters()) {
123+
throw new SQLException("The number of parameters does not match the number of placeholders in the SQL string.");
124+
}
125+
126+
return super.executeQuery(compileSql());
127+
}
128+
129+
@Override
130+
public ResultSet executeQuery(String sql) throws SQLException {
131+
checkClosed();
132+
throw new SQLException("executeQuery(String) is not supported in PreparedStatements.");
84133
}
85134

86135
@Override
87136
public int executeUpdate() throws SQLException {
88137
checkClosed();
89-
return executeUpdate(compileSql());
138+
if (insertRowBinary) {
139+
return (int) super.executeInsert(tableName, insertRowBinaryData, new InsertSettings());
140+
}
141+
142+
if (!enoughParameters()) {
143+
throw new SQLException("The number of parameters does not match the number of placeholders in the SQL string.");
144+
}
145+
146+
return super.executeUpdate(compileSql());
147+
}
148+
149+
@Override
150+
public int executeUpdate(String sql) throws SQLException {
151+
checkClosed();
152+
throw new SQLException("executeUpdate(String) is not supported in PreparedStatements.");
153+
}
154+
155+
@Override
156+
public boolean execute() throws SQLException {
157+
checkClosed();
158+
if (insertRowBinary) {
159+
super.executeInsert(tableName, insertRowBinaryData, new InsertSettings());
160+
return false;
161+
}
162+
163+
if (!enoughParameters()) {
164+
throw new SQLException("The number of parameters does not match the number of placeholders in the SQL string.");
165+
}
166+
return super.execute(compileSql());
167+
}
168+
169+
@Override
170+
public boolean execute(String sql) throws SQLException {
171+
checkClosed();
172+
throw new SQLException("execute(String) is not supported in PreparedStatements.");
173+
}
174+
175+
@Override
176+
public void addBatch() throws SQLException {
177+
checkClosed();
178+
if (!insertRowBinary) {//We ignore this for insertRowBinary
179+
if (!enoughParameters()) {
180+
throw new SQLException("The number of parameters does not match the number of placeholders in the SQL string.");
181+
}
182+
super.addBatch(compileSql());
183+
}
184+
}
185+
186+
@Override
187+
public void addBatch(String sql) throws SQLException {
188+
checkClosed();
189+
throw new SQLException("addBatch(String) is not supported in PreparedStatements.");
190+
}
191+
192+
@Override
193+
public int[] executeBatch() throws SQLException {
194+
checkClosed();
195+
if (insertRowBinary) {
196+
return new int[] { (int) super.executeInsert(tableName, insertRowBinaryData, new InsertSettings()) };
197+
}
198+
return super.executeBatch();
90199
}
91200

92201
@Override
@@ -152,6 +261,7 @@ public void setString(int parameterIndex, String x) throws SQLException {
152261
@Override
153262
public void setBytes(int parameterIndex, byte[] x) throws SQLException {
154263
checkClosed();
264+
155265
parameters[parameterIndex - 1] = encodeObject(x);
156266
}
157267

@@ -194,11 +304,9 @@ public void setBinaryStream(int parameterIndex, InputStream x, int length) throw
194304
@Override
195305
public void clearParameters() throws SQLException {
196306
checkClosed();
197-
if (originalSql.contains("?")) {
198-
this.parameters = new Object[sqlSegments.length];
199-
} else {
200-
this.parameters = new Object[0];
201-
}
307+
int paramCount = parameters.length;
308+
this.parameters = new Object[paramCount];
309+
this.setCalls = 0;
202310
}
203311

204312
@Override
@@ -213,18 +321,6 @@ public void setObject(int parameterIndex, Object x) throws SQLException {
213321
setObject(parameterIndex, x, Types.OTHER);
214322
}
215323

216-
@Override
217-
public boolean execute() throws SQLException {
218-
checkClosed();
219-
return execute(compileSql());
220-
}
221-
222-
@Override
223-
public void addBatch() throws SQLException {
224-
checkClosed();
225-
addBatch(compileSql());
226-
}
227-
228324
@Override
229325
public void setCharacterStream(int parameterIndex, Reader x, int length) throws SQLException {
230326
checkClosed();
@@ -240,13 +336,13 @@ public void setRef(int parameterIndex, Ref x) throws SQLException {
240336
@Override
241337
public void setBlob(int parameterIndex, Blob x) throws SQLException {
242338
checkClosed();
243-
parameters[parameterIndex - 1] = encodeObject(x);
339+
throw new SQLFeatureNotSupportedException("Blob is not supported.");
244340
}
245341

246342
@Override
247343
public void setClob(int parameterIndex, Clob x) throws SQLException {
248344
checkClosed();
249-
parameters[parameterIndex - 1] = encodeObject(x);
345+
throw new SQLFeatureNotSupportedException("Clob is not supported.");
250346
}
251347

252348
@Override
@@ -342,31 +438,31 @@ public void setNCharacterStream(int parameterIndex, Reader x, long length) throw
342438
@Override
343439
public void setNClob(int parameterIndex, NClob x) throws SQLException {
344440
checkClosed();
345-
parameters[parameterIndex - 1] = encodeObject(x);
441+
throw new SQLFeatureNotSupportedException("NClob is not supported.");
346442
}
347443

348444
@Override
349445
public void setClob(int parameterIndex, Reader x, long length) throws SQLException {
350446
checkClosed();
351-
parameters[parameterIndex - 1] = encodeObject(x);
447+
throw new SQLFeatureNotSupportedException("Clob is not supported.");
352448
}
353449

354450
@Override
355451
public void setBlob(int parameterIndex, InputStream x, long length) throws SQLException {
356452
checkClosed();
357-
parameters[parameterIndex - 1] = encodeObject(x);
453+
throw new SQLFeatureNotSupportedException("Blob is not supported.");
358454
}
359455

360456
@Override
361457
public void setNClob(int parameterIndex, Reader x, long length) throws SQLException {
362458
checkClosed();
363-
parameters[parameterIndex - 1] = encodeObject(x);
459+
throw new SQLFeatureNotSupportedException("NClob is not supported.");
364460
}
365461

366462
@Override
367463
public void setSQLXML(int parameterIndex, SQLXML x) throws SQLException {
368464
checkClosed();
369-
parameters[parameterIndex - 1] = encodeObject(x);
465+
throw new SQLFeatureNotSupportedException("SQLXML is not supported.");
370466
}
371467

372468
@Override
@@ -420,25 +516,40 @@ public void setNCharacterStream(int parameterIndex, Reader x) throws SQLExceptio
420516
@Override
421517
public void setClob(int parameterIndex, Reader x) throws SQLException {
422518
checkClosed();
423-
parameters[parameterIndex - 1] = encodeObject(x);
519+
throw new SQLFeatureNotSupportedException("Clob is not supported.");
424520
}
425521

426522
@Override
427523
public void setBlob(int parameterIndex, InputStream x) throws SQLException {
428524
checkClosed();
429-
parameters[parameterIndex - 1] = encodeObject(x);
525+
throw new SQLFeatureNotSupportedException("Blob is not supported.");
430526
}
431527

432528
@Override
433529
public void setNClob(int parameterIndex, Reader x) throws SQLException {
434530
checkClosed();
435-
parameters[parameterIndex - 1] = encodeObject(x);
531+
throw new SQLFeatureNotSupportedException("NClob is not supported.");
436532
}
437533

438534
@Override
439535
public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException {
440536
checkClosed();
441-
parameters[parameterIndex - 1] = encodeObject(x);
537+
538+
try {
539+
if (!insertRowBinary) {
540+
parameters[parameterIndex - 1] = encodeObject(x);
541+
} else {
542+
if (insertRowBinaryData.isEmpty()) {//Register the first object
543+
Class<?> clazz = x.getClass();
544+
connection.client.register(clazz, tableSchema);
545+
}
546+
547+
insertRowBinaryData.add(x);
548+
}
549+
} catch (Exception e) {
550+
LOG.error("Error setting object", e);
551+
throw new SQLException("Error setting object", e);
552+
}
442553
}
443554

444555
@Override
@@ -453,7 +564,14 @@ public long executeLargeUpdate() throws SQLException {
453564
return PreparedStatement.super.executeLargeUpdate();
454565
}
455566

456-
private static String encodeObject(Object x) throws SQLException {
567+
private String encodeObject(Object x) throws SQLException {
568+
return encodeObject(x, true);
569+
}
570+
private String encodeObject(Object x, boolean shouldIncrementCount) throws SQLException {
571+
if (shouldIncrementCount) {
572+
setCalls++;
573+
}
574+
457575
try {
458576
if (x == null) {
459577
return "NULL";
@@ -476,8 +594,8 @@ private static String encodeObject(Object x) throws SQLException {
476594
} else if (x instanceof Array) {
477595
StringBuilder listString = new StringBuilder();
478596
listString.append("[");
479-
for (Object item : (Object[])((Array) x).getArray()) {
480-
listString.append(encodeObject(item)).append(", ");
597+
for (Object item : (Object[]) ((Array) x).getArray()) {
598+
listString.append(encodeObject(item, false)).append(", ");
481599
}
482600
listString.delete(listString.length() - 2, listString.length());
483601
listString.append("]");
@@ -487,7 +605,7 @@ private static String encodeObject(Object x) throws SQLException {
487605
StringBuilder listString = new StringBuilder();
488606
listString.append("[");
489607
for (Object item : (Collection<?>) x) {
490-
listString.append(encodeObject(item)).append(", ");
608+
listString.append(encodeObject(item, false)).append(", ");
491609
}
492610
listString.delete(listString.length() - 2, listString.length());
493611
listString.append("]");
@@ -498,7 +616,7 @@ private static String encodeObject(Object x) throws SQLException {
498616
StringBuilder mapString = new StringBuilder();
499617
mapString.append("{");
500618
for (Object key : tmpMap.keySet()) {
501-
mapString.append(encodeObject(key)).append(": ").append(encodeObject(tmpMap.get(key))).append(", ");
619+
mapString.append(encodeObject(key, false)).append(": ").append(encodeObject(tmpMap.get(key), false)).append(", ");
502620
}
503621
mapString.delete(mapString.length() - 2, mapString.length());
504622
mapString.append("}");

0 commit comments

Comments
 (0)