|
2 | 2 |
|
3 | 3 | import org.tarantool.util.SQLStates; |
4 | 4 |
|
| 5 | +import java.io.ByteArrayOutputStream; |
| 6 | +import java.io.IOException; |
5 | 7 | import java.io.InputStream; |
6 | 8 | import java.io.Reader; |
| 9 | +import java.io.UnsupportedEncodingException; |
7 | 10 | import java.math.BigDecimal; |
8 | 11 | import java.net.URL; |
9 | 12 | import java.sql.Array; |
|
31 | 34 | public class SQLPreparedStatement extends SQLStatement implements PreparedStatement { |
32 | 35 |
|
33 | 36 | 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; |
34 | 38 |
|
35 | 39 | private final String sql; |
36 | 40 | private final Map<Integer, Object> parameters; |
@@ -182,37 +186,40 @@ public void setTimestamp(int parameterIndex, Timestamp parameterValue, Calendar |
182 | 186 |
|
183 | 187 | @Override |
184 | 188 | public void setAsciiStream(int parameterIndex, InputStream parameterValue, int length) throws SQLException { |
185 | | - setParameter(parameterIndex, parameterValue); |
| 189 | + setAsciiStream(parameterIndex, parameterValue, (long) length); |
186 | 190 | } |
187 | 191 |
|
188 | 192 | @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"); |
191 | 195 | } |
192 | 196 |
|
193 | 197 | @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"); |
196 | 201 | } |
197 | 202 |
|
198 | 203 | @Override |
199 | 204 | 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"); |
201 | 207 | } |
202 | 208 |
|
203 | 209 | @Override |
204 | 210 | public void setBinaryStream(int parameterIndex, InputStream parameterValue, int length) throws SQLException { |
205 | | - setParameter(parameterIndex, parameterValue); |
| 211 | + setBinaryStream(parameterIndex, parameterValue, (long) length); |
206 | 212 | } |
207 | 213 |
|
208 | 214 | @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); |
211 | 218 | } |
212 | 219 |
|
213 | 220 | @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); |
216 | 223 | } |
217 | 224 |
|
218 | 225 | @Override |
@@ -257,17 +264,18 @@ public boolean execute(String sql) throws SQLException { |
257 | 264 |
|
258 | 265 | @Override |
259 | 266 | public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { |
260 | | - throw new SQLFeatureNotSupportedException(); |
| 267 | + setCharacterStream(parameterIndex, reader, (long) length); |
261 | 268 | } |
262 | 269 |
|
263 | 270 | @Override |
264 | 271 | public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { |
265 | | - throw new SQLFeatureNotSupportedException(); |
| 272 | + ensureLengthLowerBound(length); |
| 273 | + setCharStream(parameterIndex, reader, length); |
266 | 274 | } |
267 | 275 |
|
268 | 276 | @Override |
269 | 277 | public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { |
270 | | - throw new SQLFeatureNotSupportedException(); |
| 278 | + setCharStream(parameterIndex, reader, Integer.MAX_VALUE); |
271 | 279 | } |
272 | 280 |
|
273 | 281 | @Override |
@@ -343,12 +351,12 @@ public void setNString(int parameterIndex, String parameterValue) throws SQLExce |
343 | 351 |
|
344 | 352 | @Override |
345 | 353 | public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { |
346 | | - throw new SQLFeatureNotSupportedException(); |
| 354 | + setCharacterStream(parameterIndex, value, length); |
347 | 355 | } |
348 | 356 |
|
349 | 357 | @Override |
350 | 358 | public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { |
351 | | - throw new SQLFeatureNotSupportedException(); |
| 359 | + setCharacterStream(parameterIndex, value); |
352 | 360 | } |
353 | 361 |
|
354 | 362 | @Override |
@@ -417,4 +425,73 @@ private Object[] toParametersList(Map<Integer, Object> parameters) throws SQLExc |
417 | 425 | return objects; |
418 | 426 | } |
419 | 427 |
|
| 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, |
| 462 | + (int) Math.min(length - totalRead, STREAM_WRITE_CHUNK_SIZE))) != -1) { |
| 463 | + value.append(buffer, 0, charsRead); |
| 464 | + totalRead += charsRead; |
| 465 | + } |
| 466 | + setParameter(parameterIndex, value.toString()); |
| 467 | + } catch (IOException e) { |
| 468 | + throw new SQLException("Cannot read from the reader", SQLStates.INVALID_PARAMETER_VALUE.getSqlState(), e); |
| 469 | + } |
| 470 | + } |
| 471 | + |
| 472 | + private void setBinStream(int parameterIndex, |
| 473 | + InputStream parameterValue, |
| 474 | + long length) throws SQLException { |
| 475 | + ensureLengthUpperBound(length); |
| 476 | + setBytes(parameterIndex, convertToBytes(parameterValue, length)); |
| 477 | + } |
| 478 | + |
| 479 | + private byte[] convertToBytes(InputStream parameterValue, long length) throws SQLException { |
| 480 | + try { |
| 481 | + int bytesRead; |
| 482 | + int totalRead = 0; |
| 483 | + byte[] buffer = new byte[STREAM_WRITE_CHUNK_SIZE]; |
| 484 | + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(STREAM_WRITE_CHUNK_SIZE); |
| 485 | + while (totalRead < length && |
| 486 | + (bytesRead = parameterValue.read(buffer, 0, |
| 487 | + (int) Math.min(length - totalRead, STREAM_WRITE_CHUNK_SIZE))) != -1) { |
| 488 | + outputStream.write(buffer, 0, bytesRead); |
| 489 | + totalRead += bytesRead; |
| 490 | + } |
| 491 | + return outputStream.toByteArray(); |
| 492 | + } catch (IOException e) { |
| 493 | + throw new SQLException("Cannot read stream", SQLStates.INVALID_PARAMETER_VALUE.getSqlState(), e); |
| 494 | + } |
| 495 | + } |
| 496 | + |
420 | 497 | } |
0 commit comments