Skip to content

Commit ecf6dd2

Browse files
committed
Support PreparedStatement stream parameters
Add capability to set streams as a parameter of ASCII or UNICODE text as well as SCALAR binary data. The input streams often are size unaware so it can be difficult to calculate the data size and, consequently, the iproto packet size. It leads the driver materializes the streams in proper data in advance and sends prepared the data as an ordinary text or raw bytes. Follow API is more important thing now than performance issues with streams. Closes: #190
1 parent 2cc1a09 commit ecf6dd2

File tree

2 files changed

+258
-24
lines changed

2 files changed

+258
-24
lines changed

Diff for: src/main/java/org/tarantool/jdbc/SQLPreparedStatement.java

+91-16
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
import org.tarantool.util.SQLStates;
44

5+
import java.io.ByteArrayOutputStream;
6+
import java.io.IOException;
57
import java.io.InputStream;
68
import java.io.Reader;
9+
import java.io.UnsupportedEncodingException;
710
import java.math.BigDecimal;
811
import java.net.URL;
912
import java.sql.Array;
@@ -31,6 +34,7 @@
3134
public class SQLPreparedStatement extends SQLStatement implements PreparedStatement {
3235

3336
private static final String INVALID_CALL_MESSAGE = "The method cannot be called on a PreparedStatement.";
37+
private static final int STREAM_WRITE_CHUNK_SIZE = 4096;
3438

3539
private final String sql;
3640
private final Map<Integer, Object> parameters;
@@ -182,37 +186,40 @@ public void setTimestamp(int parameterIndex, Timestamp parameterValue, Calendar
182186

183187
@Override
184188
public void setAsciiStream(int parameterIndex, InputStream parameterValue, int length) throws SQLException {
185-
setParameter(parameterIndex, parameterValue);
189+
setAsciiStream(parameterIndex, parameterValue, (long) length);
186190
}
187191

188192
@Override
189-
public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
190-
throw new SQLFeatureNotSupportedException();
193+
public void setAsciiStream(int parameterIndex, InputStream parameterValue) throws SQLException {
194+
setCharStream(parameterIndex, parameterValue, Integer.MAX_VALUE, "ASCII");
191195
}
192196

193197
@Override
194-
public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {
195-
throw new SQLFeatureNotSupportedException();
198+
public void setAsciiStream(int parameterIndex, InputStream parameterValue, long length) throws SQLException {
199+
ensureLengthLowerBound(length);
200+
setCharStream(parameterIndex, parameterValue, length, "ASCII");
196201
}
197202

198203
@Override
199204
public void setUnicodeStream(int parameterIndex, InputStream parameterValue, int length) throws SQLException {
200-
setParameter(parameterIndex, parameterValue);
205+
ensureLengthLowerBound(length);
206+
setCharStream(parameterIndex, parameterValue, length, "UTF-8");
201207
}
202208

203209
@Override
204210
public void setBinaryStream(int parameterIndex, InputStream parameterValue, int length) throws SQLException {
205-
setParameter(parameterIndex, parameterValue);
211+
setBinaryStream(parameterIndex, parameterValue, (long) length);
206212
}
207213

208214
@Override
209-
public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException {
210-
throw new SQLFeatureNotSupportedException();
215+
public void setBinaryStream(int parameterIndex, InputStream parameterValue, long length) throws SQLException {
216+
ensureLengthLowerBound(length);
217+
setBinStream(parameterIndex, parameterValue, length);
211218
}
212219

213220
@Override
214-
public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException {
215-
throw new SQLFeatureNotSupportedException();
221+
public void setBinaryStream(int parameterIndex, InputStream parameterValue) throws SQLException {
222+
setBinStream(parameterIndex, parameterValue, Integer.MAX_VALUE);
216223
}
217224

218225
@Override
@@ -257,17 +264,18 @@ public boolean execute(String sql) throws SQLException {
257264

258265
@Override
259266
public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException {
260-
throw new SQLFeatureNotSupportedException();
267+
setCharacterStream(parameterIndex, reader, (long) length);
261268
}
262269

263270
@Override
264271
public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException {
265-
throw new SQLFeatureNotSupportedException();
272+
ensureLengthLowerBound(length);
273+
setCharStream(parameterIndex, reader, length);
266274
}
267275

268276
@Override
269277
public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
270-
throw new SQLFeatureNotSupportedException();
278+
setCharStream(parameterIndex, reader, Integer.MAX_VALUE);
271279
}
272280

273281
@Override
@@ -343,12 +351,12 @@ public void setNString(int parameterIndex, String parameterValue) throws SQLExce
343351

344352
@Override
345353
public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException {
346-
throw new SQLFeatureNotSupportedException();
354+
setCharacterStream(parameterIndex, value, length);
347355
}
348356

349357
@Override
350358
public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
351-
throw new SQLFeatureNotSupportedException();
359+
setCharacterStream(parameterIndex, value);
352360
}
353361

354362
@Override
@@ -417,4 +425,71 @@ private Object[] toParametersList(Map<Integer, Object> parameters) throws SQLExc
417425
return objects;
418426
}
419427

428+
private void ensureLengthLowerBound(long length) throws SQLException {
429+
if (length < 0) {
430+
throw new SQLException("Stream size cannot be negative", SQLStates.INVALID_PARAMETER_VALUE.getSqlState());
431+
}
432+
}
433+
434+
private void ensureLengthUpperBound(long length) throws SQLException {
435+
if (length > Integer.MAX_VALUE) {
436+
throw new SQLException("Stream size is too large", SQLStates.INVALID_PARAMETER_VALUE.getSqlState());
437+
}
438+
}
439+
440+
private void setCharStream(int parameterIndex,
441+
InputStream parameterValue,
442+
long length,
443+
String encoding) throws SQLException {
444+
ensureLengthUpperBound(length);
445+
try {
446+
byte[] bytes = convertToBytes(parameterValue, length);
447+
setParameter(parameterIndex, new String(bytes, 0, bytes.length, encoding));
448+
} catch (UnsupportedEncodingException e) {
449+
throw new SQLException("Unsupported encoding", SQLStates.INVALID_PARAMETER_VALUE.getSqlState(), e);
450+
}
451+
}
452+
453+
private void setCharStream(int parameterIndex, Reader reader, long length) throws SQLException {
454+
ensureLengthUpperBound(length);
455+
try {
456+
StringBuilder value = new StringBuilder(STREAM_WRITE_CHUNK_SIZE);
457+
char[] buffer = new char[STREAM_WRITE_CHUNK_SIZE];
458+
int totalRead = 0;
459+
int charsRead;
460+
while (totalRead < length &&
461+
(charsRead = reader.read(buffer, 0, (int) Math.min(length - totalRead, STREAM_WRITE_CHUNK_SIZE))) != -1) {
462+
value.append(buffer, 0, charsRead);
463+
totalRead += charsRead;
464+
}
465+
setParameter(parameterIndex, value.toString());
466+
} catch (IOException e) {
467+
throw new SQLException("Cannot read from the reader", SQLStates.INVALID_PARAMETER_VALUE.getSqlState(), e);
468+
}
469+
}
470+
471+
private void setBinStream(int parameterIndex,
472+
InputStream parameterValue,
473+
long length) throws SQLException {
474+
ensureLengthUpperBound(length);
475+
setBytes(parameterIndex, convertToBytes(parameterValue, length));
476+
}
477+
478+
private byte[] convertToBytes(InputStream parameterValue, long length) throws SQLException {
479+
try {
480+
int bytesRead;
481+
int totalRead = 0;
482+
byte[] buffer = new byte[STREAM_WRITE_CHUNK_SIZE];
483+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(STREAM_WRITE_CHUNK_SIZE);
484+
while (totalRead < length &&
485+
(bytesRead = parameterValue.read(buffer, 0, (int) Math.min(length - totalRead, STREAM_WRITE_CHUNK_SIZE))) != -1) {
486+
outputStream.write(buffer, 0, bytesRead);
487+
totalRead += bytesRead;
488+
}
489+
return outputStream.toByteArray();
490+
} catch (IOException e) {
491+
throw new SQLException("Cannot read stream", SQLStates.INVALID_PARAMETER_VALUE.getSqlState(), e);
492+
}
493+
}
494+
420495
}

0 commit comments

Comments
 (0)