Mercurial > hg > monetdb-java
comparison src/main/java/org/monetdb/jdbc/MonetConnection.java @ 576:095e896f9d7a onclient
Updated comments. Improved code. Added final keywords
author | Martin van Dinther <martin.van.dinther@monetdbsolutions.com> |
---|---|
date | Wed, 06 Oct 2021 22:48:11 +0200 (2021-10-06) |
parents | b4d29515c22e |
children | 72f4437de9be |
comparison
equal
deleted
inserted
replaced
575:08c9918177b2 | 576:095e896f9d7a |
---|---|
66 * whole Connection interface. | 66 * whole Connection interface. |
67 *</pre> | 67 *</pre> |
68 * | 68 * |
69 * @author Fabian Groffen | 69 * @author Fabian Groffen |
70 * @author Martin van Dinther | 70 * @author Martin van Dinther |
71 * @version 1.6 | 71 * @version 1.7 |
72 */ | 72 */ |
73 public class MonetConnection | 73 public class MonetConnection |
74 extends MonetWrapper | 74 extends MonetWrapper |
75 implements Connection, AutoCloseable | 75 implements Connection, AutoCloseable |
76 { | 76 { |
1236 @Override | 1236 @Override |
1237 public java.sql.Array createArrayOf(final String typeName, final Object[] elements) throws SQLException { | 1237 public java.sql.Array createArrayOf(final String typeName, final Object[] elements) throws SQLException { |
1238 throw newSQLFeatureNotSupportedException("createArrayOf"); | 1238 throw newSQLFeatureNotSupportedException("createArrayOf"); |
1239 } | 1239 } |
1240 | 1240 |
1241 | |
1242 /** | 1241 /** |
1243 * Constructs an object that implements the Clob interface. The | 1242 * Constructs an object that implements the Clob interface. The |
1244 * object returned initially contains no data. The setAsciiStream, | 1243 * object returned initially contains no data. The setAsciiStream, |
1245 * setCharacterStream and setString methods of the Clob interface | 1244 * setCharacterStream and setString methods of the Clob interface |
1246 * may be used to add data to the Clob. | 1245 * may be used to add data to the Clob. |
1672 //== end methods of interface java.sql.Connection | 1671 //== end methods of interface java.sql.Connection |
1673 | 1672 |
1674 | 1673 |
1675 //== internal helper methods which do not belong to the JDBC interface | 1674 //== internal helper methods which do not belong to the JDBC interface |
1676 | 1675 |
1677 /** Handlers for ON CLIENT requests */ | 1676 /** Handler for COPY ... INTO ... FROM 'data-file-name' ON CLIENT requests */ |
1678 private UploadHandler uploadHandler; | 1677 private UploadHandler uploadHandler; |
1678 /** Handler for COPY ... INTO 'data-file-name' ON CLIENT requests */ | |
1679 private DownloadHandler downloadHandler; | 1679 private DownloadHandler downloadHandler; |
1680 | 1680 |
1681 /** | 1681 /** |
1682 * Registers a {@link UploadHandler} to support for example COPY INTO mytable FROM 'data.csv' ON CLIENT | 1682 * Registers a {@link UploadHandler} to support for example COPY INTO mytable FROM 'data.csv' ON CLIENT |
1683 * | 1683 * |
1684 * @param uploadHandler the handler to register, or null to deregister | 1684 * @param uploadHandler the handler to register, or null to deregister |
1685 */ | 1685 */ |
1686 public void setUploadHandler(UploadHandler uploadHandler) { | 1686 public void setUploadHandler(final UploadHandler uploadHandler) { |
1687 this.uploadHandler = uploadHandler; | 1687 this.uploadHandler = uploadHandler; |
1688 } | 1688 } |
1689 | 1689 |
1690 /** | 1690 /** |
1691 * Returns the currently registerered {@link UploadHandler}, or null | 1691 * Returns the currently registerered {@link UploadHandler}, or null |
1692 */ | 1692 */ |
1693 public UploadHandler getUploadHandler() { | 1693 public UploadHandler getUploadHandler() { |
1694 return uploadHandler; | 1694 return uploadHandler; |
1695 } | 1695 } |
1696 | |
1696 /** | 1697 /** |
1697 * Registers a {@link DownloadHandler} to support for example COPY select_result INTO 'data.csv' ON CLIENT | 1698 * Registers a {@link DownloadHandler} to support for example COPY select_result INTO 'data.csv' ON CLIENT |
1698 * | 1699 * |
1699 * @param downloadHandler the handler to register, or null to deregister | 1700 * @param downloadHandler the handler to register, or null to deregister |
1700 */ | 1701 */ |
1701 public void setDownloadHandler(DownloadHandler downloadHandler) { | 1702 public void setDownloadHandler(final DownloadHandler downloadHandler) { |
1702 this.downloadHandler = downloadHandler; | 1703 this.downloadHandler = downloadHandler; |
1703 } | 1704 } |
1704 | 1705 |
1705 /** | 1706 /** |
1706 * Returns the currently registerered {@link DownloadHandler} handler, or null | 1707 * Returns the currently registerered {@link DownloadHandler} handler, or null |
3018 curReplySize = size; | 3019 curReplySize = size; |
3019 } | 3020 } |
3020 // }}} set reply size | 3021 // }}} set reply size |
3021 | 3022 |
3022 // send query to the server | 3023 // send query to the server |
3023 String queryLine = templ[0] + query + templ[1]; | 3024 out.writeLine(templ[0] + query + templ[1]); |
3024 out.writeLine(queryLine); | |
3025 | 3025 |
3026 // go for new results | 3026 // go for new results |
3027 String tmpLine = in.readLine(); | 3027 String tmpLine = in.readLine(); |
3028 LineType linetype = in.getLineType(); | 3028 LineType linetype = in.getLineType(); |
3029 Response res = null; | 3029 Response res = null; |
3208 } | 3208 } |
3209 } | 3209 } |
3210 } | 3210 } |
3211 // }}} | 3211 // }}} |
3212 | 3212 |
3213 private String handleTransfer(String transferCommand) throws IOException { | 3213 private String handleTransfer(final String transferCommand) throws IOException { |
3214 if (transferCommand.startsWith("r ")) { | 3214 if (transferCommand.startsWith("r ")) { |
3215 String[] parts = transferCommand.split(" ", 3); | 3215 final String[] parts = transferCommand.split(" ", 3); |
3216 if (parts.length == 3) { | 3216 if (parts.length == 3) { |
3217 final long offset; | 3217 final long offset; |
3218 try { | 3218 try { |
3219 offset = Long.parseLong(parts[1]); | 3219 offset = Long.parseLong(parts[1]); |
3220 } catch (NumberFormatException e) { | 3220 } catch (NumberFormatException e) { |
3228 return handleDownload(transferCommand.substring(2)); | 3228 return handleDownload(transferCommand.substring(2)); |
3229 } | 3229 } |
3230 return "JDBC does not support this file transfer yet: " + transferCommand; | 3230 return "JDBC does not support this file transfer yet: " + transferCommand; |
3231 } | 3231 } |
3232 | 3232 |
3233 private String handleUpload(String path, boolean textMode, long offset) throws IOException { | 3233 private String handleUpload(final String path, final boolean textMode, final long offset) throws IOException { |
3234 if (uploadHandler == null) { | 3234 if (uploadHandler == null) { |
3235 return "No file upload handler has been registered with the JDBC driver"; | 3235 return "No file upload handler has been registered with the JDBC driver"; |
3236 } | 3236 } |
3237 | 3237 |
3238 long linesToSkip = offset >= 1 ? offset - 1 : 0; | 3238 final long linesToSkip = offset >= 1 ? offset - 1 : 0; |
3239 Upload handle = new Upload(server, uploadHandler::uploadCancelled); | 3239 final Upload handle = new Upload(server, uploadHandler::uploadCancelled); |
3240 boolean wasFaking = server.setInsertFakePrompts(false); | 3240 final boolean wasFaking = server.setInsertFakePrompts(false); |
3241 try { | 3241 try { |
3242 uploadHandler.handleUpload(handle, path, textMode, linesToSkip); | 3242 uploadHandler.handleUpload(handle, path, textMode, linesToSkip); |
3243 if (!handle.hasBeenUsed()) { | 3243 if (!handle.hasBeenUsed()) { |
3244 String message = "Call to " + uploadHandler.getClass().getCanonicalName() + ".handleUpload for path '" + path + "' sent neither data nor an error message"; | 3244 throw new IOException("Call to " + uploadHandler.getClass().getCanonicalName() + ".handleUpload for path '" + path + "' sent neither data nor an error message"); |
3245 throw new IOException(message); | |
3246 } | 3245 } |
3247 handle.close(); | 3246 handle.close(); |
3248 } finally { | 3247 } finally { |
3249 server.setInsertFakePrompts(wasFaking); | 3248 server.setInsertFakePrompts(wasFaking); |
3250 } | 3249 } |
3251 return handle.getError(); | 3250 return handle.getError(); |
3252 } | 3251 } |
3253 | 3252 |
3254 private String handleDownload(String path) throws IOException { | 3253 private String handleDownload(final String path) throws IOException { |
3255 if (downloadHandler == null) { | 3254 if (downloadHandler == null) { |
3256 return "No file download handler has been registered with the JDBC driver"; | 3255 return "No file download handler has been registered with the JDBC driver"; |
3257 } | 3256 } |
3258 | 3257 |
3259 Download handle = new Download(server); | 3258 final Download handle = new Download(server); |
3260 try { | 3259 try { |
3261 downloadHandler.handleDownload(handle, path, true); | 3260 downloadHandler.handleDownload(handle, path, true); |
3262 if (!handle.hasBeenUsed()) { | 3261 if (!handle.hasBeenUsed()) { |
3263 String message = "Call to " + downloadHandler.getClass().getSimpleName() + ".handleDownload sent neither data nor error"; | 3262 handle.sendError("Call to " + downloadHandler.getClass().getSimpleName() + ".handleDownload sent neither data nor error"); |
3264 handle.sendError(message); | |
3265 } | 3263 } |
3266 } finally { | 3264 } finally { |
3267 handle.close(); | 3265 handle.close(); |
3268 } | 3266 } |
3269 return handle.getError(); | 3267 return handle.getError(); |
3270 } | 3268 } |
3271 | 3269 |
3272 /** | 3270 /** |
3273 * Callback for sending files for COPY ON CLIENT | 3271 * Callback for sending files for COPY INTO "table" FROM 'file-name' ON CLIENT commands |
3274 * | 3272 * |
3275 * To be registered with {@link MonetConnection#setUploadHandler(UploadHandler)} | 3273 * To be registered with {@link MonetConnection#setUploadHandler(UploadHandler)} |
3274 * | |
3275 * An example implementation can be found at ../util/FileTransferHandler.java | |
3276 */ | 3276 */ |
3277 | 3277 |
3278 public interface UploadHandler { | 3278 public interface UploadHandler { |
3279 /** | 3279 /** |
3280 * Called if the server sends a request to read file data. | 3280 * Called if the server sends a request to read file data. |
3298 */ | 3298 */ |
3299 default void uploadCancelled() {} | 3299 default void uploadCancelled() {} |
3300 } | 3300 } |
3301 | 3301 |
3302 /** | 3302 /** |
3303 * Callback for receiving files with COPY ON CLIENT | 3303 * Callback for receiving files from COPY .. INTO 'file-name' ON CLIENT commands |
3304 * | 3304 * |
3305 * To be registered with {@link MonetConnection#setDownloadHandler(DownloadHandler)} | 3305 * To be registered with {@link MonetConnection#setDownloadHandler(DownloadHandler)} |
3306 * | |
3307 * An example implementation can be found at ../util/FileTransferHandler.java | |
3306 */ | 3308 */ |
3307 public interface DownloadHandler { | 3309 public interface DownloadHandler { |
3308 /** | 3310 /** |
3309 * Called if the server sends a request to write a file. | 3311 * Called if the server sends a request to write a file. |
3310 * | 3312 * |
3342 * This method can only be sent if no data has been sent to the server | 3344 * This method can only be sent if no data has been sent to the server |
3343 * yet. After data has been sent, you can still throw an | 3345 * yet. After data has been sent, you can still throw an |
3344 * {@link IOException} but this will terminate the connection. | 3346 * {@link IOException} but this will terminate the connection. |
3345 * @param errorMessage error message to send | 3347 * @param errorMessage error message to send |
3346 */ | 3348 */ |
3347 public void sendError(String errorMessage) throws IOException { | 3349 public void sendError(final String errorMessage) throws IOException { |
3348 if (error != null) { | 3350 if (error != null) { |
3349 throw new IOException("another error has already been sent: " + error); | 3351 throw new IOException("another error has already been sent: " + error); |
3350 } | 3352 } |
3351 error = errorMessage; | 3353 error = errorMessage; |
3352 } | 3354 } |
3353 | 3355 |
3354 /** | 3356 /** |
3355 * After every {@code chunkSize} bytes, the server gets the opportunity to | 3357 * After every {@code chunkSize} bytes, the server gets the opportunity to |
3356 * terminate the upload. | 3358 * terminate the upload. |
3357 */ | 3359 */ |
3358 public void setChunkSize(int chunkSize) { | 3360 public void setChunkSize(final int chunkSize) { |
3359 this.customChunkSize = chunkSize; | 3361 this.customChunkSize = chunkSize; |
3360 } | 3362 } |
3361 | 3363 |
3362 /** | 3364 /** |
3363 * Get a {@link PrintStream} to write data to. | 3365 * Get a {@link PrintStream} to write data to. |
3368 if (error != null) { | 3370 if (error != null) { |
3369 throw new IOException("Cannot send data after an error has been sent"); | 3371 throw new IOException("Cannot send data after an error has been sent"); |
3370 } | 3372 } |
3371 if (print == null) { | 3373 if (print == null) { |
3372 try { | 3374 try { |
3373 MapiSocket.UploadStream up = customChunkSize >= 0 ? server.uploadStream(customChunkSize) : server.uploadStream(); | 3375 final MapiSocket.UploadStream up = customChunkSize >= 0 ? server.uploadStream(customChunkSize) : server.uploadStream(); |
3374 up.setCancellationCallback(cancellationCallback); | 3376 up.setCancellationCallback(cancellationCallback); |
3375 print = new PrintStream(up, false, "UTF-8"); | 3377 print = new PrintStream(up, false, "UTF-8"); |
3376 up.write('\n'); | 3378 up.write('\n'); |
3377 } catch (UnsupportedEncodingException e) { | 3379 } catch (UnsupportedEncodingException e) { |
3378 throw new RuntimeException("The system is guaranteed to support the UTF-8 encoding but apparently it doesn't", e); | 3380 throw new RuntimeException("The system is guaranteed to support the UTF-8 encoding but apparently it doesn't", e); |
3398 /** | 3400 /** |
3399 * Read from the given input stream and write it to the server. | 3401 * Read from the given input stream and write it to the server. |
3400 * | 3402 * |
3401 * For text mode uploads, the data MUST be validly UTF-8 encoded. | 3403 * For text mode uploads, the data MUST be validly UTF-8 encoded. |
3402 */ | 3404 */ |
3403 public void uploadFrom(InputStream inputStream) throws IOException { | 3405 public void uploadFrom(final InputStream inputStream) throws IOException { |
3404 OutputStream s = getStream(); | 3406 final OutputStream s = getStream(); |
3405 byte[] buffer = new byte[64 * 1024]; | 3407 final byte[] buffer = new byte[64 * 1024]; |
3406 while (true) { | 3408 while (true) { |
3407 int nread = inputStream.read(buffer); | 3409 int nread = inputStream.read(buffer); |
3408 if (nread < 0) { | 3410 if (nread < 0) { |
3409 break; | 3411 break; |
3410 } | 3412 } |
3416 * Read data from the given buffered reader and send it to the server | 3418 * Read data from the given buffered reader and send it to the server |
3417 * @param reader reader to read from | 3419 * @param reader reader to read from |
3418 * @param linesToSkip start uploading at line {@code offset}. Value 0 and 1 | 3420 * @param linesToSkip start uploading at line {@code offset}. Value 0 and 1 |
3419 * both mean upload the whole file, value 2 means skip the first line, etc.q | 3421 * both mean upload the whole file, value 2 means skip the first line, etc.q |
3420 */ | 3422 */ |
3421 public void uploadFrom(BufferedReader reader, long linesToSkip) throws IOException { | 3423 public void uploadFrom(final BufferedReader reader, final long linesToSkip) throws IOException { |
3422 for (int i = 0; i < linesToSkip; i++) { | 3424 for (int i = 0; i < linesToSkip; i++) { |
3423 String line = reader.readLine(); | 3425 String line = reader.readLine(); |
3424 if (line == null) { | 3426 if (line == null) { |
3425 return; | 3427 return; |
3426 } | 3428 } |
3427 } | 3429 } |
3428 | 3430 |
3429 uploadFrom(reader); | 3431 uploadFrom(reader); |
3430 } | 3432 } |
3431 | 3433 |
3432 | |
3433 /** | 3434 /** |
3434 * Read data from the given buffered reader and send it to the server | 3435 * Read data from the given buffered reader and send it to the server |
3435 * @param reader reader to read from | 3436 * @param reader reader to read from |
3436 */ | 3437 */ |
3437 public void uploadFrom(Reader reader) throws IOException { | 3438 public void uploadFrom(final Reader reader) throws IOException { |
3438 OutputStream s = getStream(); | 3439 final OutputStream s = getStream(); |
3439 OutputStreamWriter writer = new OutputStreamWriter(s, StandardCharsets.UTF_8); | 3440 final OutputStreamWriter writer = new OutputStreamWriter(s, StandardCharsets.UTF_8); |
3440 char[] buffer = new char[64 * 1024]; | 3441 final char[] buffer = new char[64 * 1024]; |
3441 while (true) { | 3442 while (true) { |
3442 int nread = reader.read(buffer, 0, buffer.length); | 3443 int nread = reader.read(buffer, 0, buffer.length); |
3443 if (nread < 0) { | 3444 if (nread < 0) { |
3444 break; | 3445 break; |
3445 } | 3446 } |
3446 writer.write(buffer, 0, nread); | 3447 writer.write(buffer, 0, nread); |
3447 writer.close(); | 3448 writer.close(); |
3448 } | 3449 } |
3449 } | 3450 } |
3450 | 3451 |
3452 /** | |
3453 * Close opened {@link PrintStream}. | |
3454 */ | |
3451 public void close() { | 3455 public void close() { |
3452 if (print != null) { | 3456 if (print != null) { |
3453 print.close(); | 3457 print.close(); |
3458 print = null; | |
3454 } | 3459 } |
3455 } | 3460 } |
3456 } | 3461 } |
3457 | 3462 |
3458 /** | 3463 /** |
3461 public static class Download { | 3466 public static class Download { |
3462 private final MapiSocket server; | 3467 private final MapiSocket server; |
3463 private MapiSocket.DownloadStream stream = null; | 3468 private MapiSocket.DownloadStream stream = null; |
3464 private String error = null; | 3469 private String error = null; |
3465 | 3470 |
3466 boolean closed = false; | |
3467 | |
3468 Download(MapiSocket server) { | 3471 Download(MapiSocket server) { |
3469 this.server = server; | 3472 this.server = server; |
3470 } | 3473 } |
3471 | 3474 |
3472 /** | 3475 /** |
3506 } | 3509 } |
3507 | 3510 |
3508 /** | 3511 /** |
3509 * Write the data from the server to the given {@link OutputStream}. | 3512 * Write the data from the server to the given {@link OutputStream}. |
3510 */ | 3513 */ |
3511 public void downloadTo(OutputStream stream) throws IOException { | 3514 public void downloadTo(final OutputStream stream) throws IOException { |
3512 InputStream s = getStream(); | 3515 final InputStream s = getStream(); |
3513 byte[] buffer = new byte[65536]; | 3516 final byte[] buffer = new byte[65536]; |
3514 while (true) { | 3517 while (true) { |
3515 int nread = s.read(buffer); | 3518 int nread = s.read(buffer); |
3516 if (nread < 0) | 3519 if (nread < 0) { |
3517 break; | 3520 break; |
3521 } | |
3518 stream.write(buffer, 0, nread); | 3522 stream.write(buffer, 0, nread); |
3519 } | 3523 } |
3520 } | 3524 } |
3521 | 3525 |
3522 /** | 3526 /** |
3523 * @return true if data has been received or an error has been sent. | 3527 * @return true if data has been received or an error has been sent. |
3524 */ | 3528 */ |
3525 | |
3526 public boolean hasBeenUsed() { | 3529 public boolean hasBeenUsed() { |
3527 return error != null || stream != null; | 3530 return error != null || stream != null; |
3528 } | 3531 } |
3529 | 3532 |
3530 /** | 3533 /** |
3531 * @return the error that was sent, if any | 3534 * @return the error that was sent, if any |
3532 */ | 3535 */ |
3533 | |
3534 public String getError() { | 3536 public String getError() { |
3535 return error; | 3537 return error; |
3536 } | 3538 } |
3537 public void close() throws IOException { | 3539 |
3538 if (closed) { | 3540 /** |
3539 return; | 3541 * Close opened stream. |
3540 } | 3542 */ |
3543 public void close() { | |
3541 if (stream != null) { | 3544 if (stream != null) { |
3542 stream.close(); | 3545 try { |
3543 } | 3546 stream.close(); |
3544 closed = true; | 3547 stream = null; |
3548 } catch (IOException e) { | |
3549 /* ignore close error */ | |
3550 } | |
3551 } | |
3545 } | 3552 } |
3546 } | 3553 } |
3547 } | 3554 } |