|
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,71 @@ 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, (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 | + |
420 | 495 | }
|
0 commit comments