10
10
import com .clickhouse .client .api .data_formats .ClickHouseBinaryFormatReader ;
11
11
import com .clickhouse .client .api .data_formats .NativeFormatReader ;
12
12
import com .clickhouse .client .api .data_formats .RowBinaryFormatReader ;
13
+ import com .clickhouse .client .api .data_formats .RowBinaryFormatSerializer ;
13
14
import com .clickhouse .client .api .data_formats .RowBinaryWithNamesAndTypesFormatReader ;
14
15
import com .clickhouse .client .api .data_formats .RowBinaryWithNamesFormatReader ;
15
16
import com .clickhouse .client .api .data_formats .internal .BinaryStreamReader ;
43
44
import com .clickhouse .client .api .query .Records ;
44
45
import com .clickhouse .client .config .ClickHouseClientOption ;
45
46
import com .clickhouse .data .ClickHouseColumn ;
46
- import com .clickhouse .data .ClickHouseDataType ;
47
47
import com .clickhouse .data .ClickHouseFormat ;
48
48
import org .apache .hc .client5 .http .ConnectTimeoutException ;
49
49
import org .apache .hc .core5 .concurrent .DefaultThreadFactory ;
59
59
import java .io .ByteArrayOutputStream ;
60
60
import java .io .IOException ;
61
61
import java .io .InputStream ;
62
+ import java .io .OutputStream ;
62
63
import java .lang .reflect .InvocationTargetException ;
63
64
import java .lang .reflect .Method ;
64
65
import java .net .ConnectException ;
@@ -623,6 +624,11 @@ public Builder useHttpCompression(boolean enabled) {
623
624
return this ;
624
625
}
625
626
627
+ public Builder appCompressedData (boolean enabled ) {
628
+ this .configuration .put (ClientConfigProperties .APP_COMPRESSED_DATA .getKey (), String .valueOf (enabled ));
629
+ return this ;
630
+ }
631
+
626
632
/**
627
633
* Sets buffer size for uncompressed data in LZ4 compression.
628
634
* For outgoing data it is the size of a buffer that will be compressed.
@@ -1066,6 +1072,11 @@ public Client build() {
1066
1072
1067
1073
private static final int DEFAULT_NETWORK_BUFFER_SIZE = 300_000 ;
1068
1074
1075
+ /**
1076
+ * Default size for a buffers used in networking.
1077
+ */
1078
+ public static final int DEFAULT_BUFFER_SIZE = 8192 ;
1079
+
1069
1080
private void setDefaults () {
1070
1081
1071
1082
// set default database name if not specified
@@ -1154,6 +1165,10 @@ private void setDefaults() {
1154
1165
if (!configuration .containsKey (ClientConfigProperties .USE_HTTP_COMPRESSION .getKey ())) {
1155
1166
useHttpCompression (false );
1156
1167
}
1168
+
1169
+ if (!configuration .containsKey (ClientConfigProperties .APP_COMPRESSED_DATA .getKey ())) {
1170
+ appCompressedData (false );
1171
+ }
1157
1172
}
1158
1173
}
1159
1174
@@ -1236,45 +1251,9 @@ public synchronized void register(Class<?> clazz, TableSchema schema) {
1236
1251
schemaSerializers .put (column .getColumnName (), (obj , stream ) -> {
1237
1252
Object value = getterMethod .invoke (obj );
1238
1253
1239
- if (defaultsSupport ) {
1240
- if (value != null ) {//Because we now support defaults, we have to send nonNull
1241
- SerializerUtils .writeNonNull (stream );//Write 0 for no default
1242
-
1243
- if (column .isNullable ()) {//If the column is nullable
1244
- SerializerUtils .writeNonNull (stream );//Write 0 for not null
1245
- }
1246
- } else {//So if the object is null
1247
- if (column .hasDefault ()) {
1248
- SerializerUtils .writeNull (stream );//Send 1 for default
1249
- return ;
1250
- } else if (column .isNullable ()) {//And the column is nullable
1251
- SerializerUtils .writeNonNull (stream );
1252
- SerializerUtils .writeNull (stream );//Then we send null, write 1
1253
- return ;//And we're done
1254
- } else if (column .getDataType () == ClickHouseDataType .Array ) {//If the column is an array
1255
- SerializerUtils .writeNonNull (stream );//Then we send nonNull
1256
- } else {
1257
- throw new IllegalArgumentException (String .format ("An attempt to write null into not nullable column '%s'" , column .getColumnName ()));
1258
- }
1259
- }
1260
- } else {
1261
- if (column .isNullable ()) {
1262
- if (value == null ) {
1263
- SerializerUtils .writeNull (stream );
1264
- return ;
1265
- }
1266
- SerializerUtils .writeNonNull (stream );
1267
- } else if (value == null ) {
1268
- if (column .getDataType () == ClickHouseDataType .Array ) {
1269
- SerializerUtils .writeNonNull (stream );
1270
- } else {
1271
- throw new IllegalArgumentException (String .format ("An attempt to write null into not nullable column '%s'" , column .getColumnName ()));
1272
- }
1273
- }
1254
+ if (RowBinaryFormatSerializer .writeValuePreamble (stream , defaultsSupport , column , value )) {
1255
+ SerializerUtils .serializeData (stream , value , column );
1274
1256
}
1275
-
1276
- //Handle the different types
1277
- SerializerUtils .serializeData (stream , value , column );
1278
1257
});
1279
1258
} else {
1280
1259
LOG .warn ("No getter method found for column: {}" , propertyName );
@@ -1473,7 +1452,7 @@ public CompletableFuture<InsertResponse> insert(String tableName, InputStream da
1473
1452
}
1474
1453
1475
1454
/**
1476
- * <p> Sends write request to database. Input data is read from the input stream.</p>
1455
+ * Sends write request to database. Input data is read from the input stream.
1477
1456
*
1478
1457
* @param tableName - destination table name
1479
1458
* @param data - data stream to insert
@@ -1482,7 +1461,49 @@ public CompletableFuture<InsertResponse> insert(String tableName, InputStream da
1482
1461
* @return {@code CompletableFuture<InsertResponse>} - a promise to insert response
1483
1462
*/
1484
1463
public CompletableFuture <InsertResponse > insert (String tableName ,
1485
- InputStream data ,
1464
+ InputStream data ,
1465
+ ClickHouseFormat format ,
1466
+ InsertSettings settings ) {
1467
+
1468
+ final int writeBufferSize = settings .getInputStreamCopyBufferSize () <= 0 ?
1469
+ Integer .parseInt (configuration .getOrDefault (ClientConfigProperties .CLIENT_NETWORK_BUFFER_SIZE .getKey (),
1470
+ ClientConfigProperties .CLIENT_NETWORK_BUFFER_SIZE .getDefaultValue ())) :
1471
+ settings .getInputStreamCopyBufferSize ();
1472
+
1473
+ if (writeBufferSize <= 0 ) {
1474
+ throw new IllegalArgumentException ("Buffer size must be greater than 0" );
1475
+ }
1476
+
1477
+ return insert (tableName , new DataStreamWriter () {
1478
+ @ Override
1479
+ public void onOutput (OutputStream out ) throws IOException {
1480
+ byte [] buffer = new byte [writeBufferSize ];
1481
+ int bytesRead ;
1482
+ while ((bytesRead = data .read (buffer )) > 0 ) {
1483
+ out .write (buffer , 0 , bytesRead );
1484
+ }
1485
+ out .close ();
1486
+ }
1487
+
1488
+ @ Override
1489
+ public void onRetry () throws IOException {
1490
+ data .reset ();
1491
+ }
1492
+ },
1493
+ format , settings );
1494
+ }
1495
+
1496
+ /**
1497
+ * Does an insert request to a server. Data is pushed when a {@link DataStreamWriter#onOutput(OutputStream)} is called.
1498
+ *
1499
+ * @param tableName - target table name
1500
+ * @param writer - {@link DataStreamWriter} implementation
1501
+ * @param format - source format
1502
+ * @param settings - operation settings
1503
+ * @return {@code CompletableFuture<InsertResponse>} - a promise to insert response
1504
+ */
1505
+ public CompletableFuture <InsertResponse > insert (String tableName ,
1506
+ DataStreamWriter writer ,
1486
1507
ClickHouseFormat format ,
1487
1508
InsertSettings settings ) {
1488
1509
@@ -1513,6 +1534,8 @@ public CompletableFuture<InsertResponse> insert(String tableName,
1513
1534
1514
1535
settings .setOption (ClientConfigProperties .INPUT_OUTPUT_FORMAT .getKey (), format .name ());
1515
1536
final InsertSettings finalSettings = settings ;
1537
+ final String sqlStmt = "INSERT INTO \" " + tableName + "\" FORMAT " + format .name ();
1538
+ finalSettings .serverSetting (ClickHouseHttpProto .QPARAM_QUERY_STMT , sqlStmt );
1516
1539
responseSupplier = () -> {
1517
1540
// Selecting some node
1518
1541
ClickHouseNode selectedNode = getNextAliveNode ();
@@ -1523,17 +1546,7 @@ public CompletableFuture<InsertResponse> insert(String tableName,
1523
1546
try (ClassicHttpResponse httpResponse =
1524
1547
httpClientHelper .executeRequest (selectedNode , finalSettings .getAllSettings (),
1525
1548
out -> {
1526
- out .write ("INSERT INTO " .getBytes ());
1527
- out .write (tableName .getBytes ());
1528
- out .write (" FORMAT " .getBytes ());
1529
- out .write (format .name ().getBytes ());
1530
- out .write (" \n " .getBytes ());
1531
-
1532
- byte [] buffer = new byte [writeBufferSize ];
1533
- int bytesRead ;
1534
- while ((bytesRead = data .read (buffer )) > 0 ) {
1535
- out .write (buffer , 0 , bytesRead );
1536
- }
1549
+ writer .onOutput (out );
1537
1550
out .close ();
1538
1551
})) {
1539
1552
@@ -1566,7 +1579,7 @@ public CompletableFuture<InsertResponse> insert(String tableName,
1566
1579
1567
1580
if (i < maxRetries ) {
1568
1581
try {
1569
- data . reset ();
1582
+ writer . onRetry ();
1570
1583
} catch (IOException ioe ) {
1571
1584
throw new ClientException ("Failed to reset stream before next attempt" , ioe );
1572
1585
}
@@ -1581,12 +1594,7 @@ public CompletableFuture<InsertResponse> insert(String tableName,
1581
1594
1582
1595
CompletableFuture <ClickHouseResponse > future = null ;
1583
1596
future = request .data (output -> {
1584
- //Copy the data from the input stream to the output stream
1585
- byte [] buffer = new byte [settings .getInputStreamCopyBufferSize ()];
1586
- int bytesRead ;
1587
- while ((bytesRead = data .read (buffer )) != -1 ) {
1588
- output .write (buffer , 0 , bytesRead );
1589
- }
1597
+ writer .onOutput (output );
1590
1598
output .close ();
1591
1599
}).option (ClickHouseClientOption .ASYNC , false ).execute ();
1592
1600
0 commit comments