|
32 | 32 | import java.sql.Savepoint;
|
33 | 33 | import java.sql.Statement;
|
34 | 34 | import java.sql.Struct;
|
35 |
| -import java.util.Arrays; |
| 35 | +import java.util.ArrayList; |
36 | 36 | import java.util.Collection;
|
37 | 37 | import java.util.Collections;
|
38 | 38 | import java.util.HashMap;
|
39 | 39 | import java.util.List;
|
40 | 40 | import java.util.Map;
|
41 | 41 | import java.util.Properties;
|
42 | 42 | import java.util.concurrent.Executor;
|
| 43 | +import java.util.concurrent.Future; |
43 | 44 | import java.util.concurrent.TimeoutException;
|
| 45 | +import java.util.function.Function; |
44 | 46 |
|
45 | 47 | /**
|
46 | 48 | * Tarantool {@link Connection} implementation.
|
@@ -525,46 +527,59 @@ public int getNetworkTimeout() throws SQLException {
|
525 | 527 | return (int) client.getOperationTimeout();
|
526 | 528 | }
|
527 | 529 |
|
528 |
| - protected SQLResultHolder execute(long timeout, String sql, Object... args) throws SQLException { |
| 530 | + protected SQLResultHolder execute(long timeout, SQLQueryHolder query) throws SQLException { |
529 | 531 | checkNotClosed();
|
| 532 | + return (useNetworkTimeout(timeout)) |
| 533 | + ? executeWithNetworkTimeout(query) |
| 534 | + : executeWithQueryTimeout(timeout, query); |
| 535 | + } |
| 536 | + |
| 537 | + protected SQLBatchResultHolder executeBatch(long timeout, List<SQLQueryHolder> queries) throws SQLException { |
| 538 | + checkNotClosed(); |
| 539 | + SQLTarantoolClientImpl.SQLRawOps sqlOps = client.sqlRawOps(); |
| 540 | + SQLBatchResultHolder batchResult = useNetworkTimeout(timeout) |
| 541 | + ? sqlOps.executeBatch(queries) |
| 542 | + : sqlOps.executeBatch(timeout, queries); |
| 543 | + |
| 544 | + return batchResult; |
| 545 | + } |
| 546 | + |
| 547 | + private boolean useNetworkTimeout(long timeout) throws SQLException { |
530 | 548 | int networkTimeout = getNetworkTimeout();
|
531 |
| - return (timeout == 0 || (networkTimeout > 0 && networkTimeout < timeout)) |
532 |
| - ? executeWithNetworkTimeout(sql, args) |
533 |
| - : executeWithStatementTimeout(timeout, sql, args); |
| 549 | + return timeout == 0 || (networkTimeout > 0 && networkTimeout < timeout); |
534 | 550 | }
|
535 | 551 |
|
536 |
| - private SQLResultHolder executeWithNetworkTimeout(String sql, Object... args) throws SQLException { |
| 552 | + private SQLResultHolder executeWithNetworkTimeout(SQLQueryHolder query) throws SQLException { |
537 | 553 | try {
|
538 |
| - return client.sqlRawOps().execute(sql, args); |
| 554 | + return client.sqlRawOps().execute(query); |
539 | 555 | } catch (Exception e) {
|
540 | 556 | handleException(e);
|
541 |
| - throw new SQLException(formatError(sql, args), e); |
| 557 | + throw new SQLException(formatError(query), e); |
542 | 558 | }
|
543 | 559 | }
|
544 | 560 |
|
545 | 561 | /**
|
546 | 562 | * Executes a query using a custom timeout.
|
547 | 563 | *
|
548 | 564 | * @param timeout query timeout
|
549 |
| - * @param sql query |
550 |
| - * @param args query bindings |
| 565 | + * @param query query |
551 | 566 | *
|
552 | 567 | * @return SQL result holder
|
553 | 568 | *
|
554 | 569 | * @throws StatementTimeoutException if query execution took more than query timeout
|
555 | 570 | * @throws SQLException if any other errors occurred
|
556 | 571 | */
|
557 |
| - private SQLResultHolder executeWithStatementTimeout(long timeout, String sql, Object... args) throws SQLException { |
| 572 | + private SQLResultHolder executeWithQueryTimeout(long timeout, SQLQueryHolder query) throws SQLException { |
558 | 573 | try {
|
559 |
| - return client.sqlRawOps().execute(timeout, sql, args); |
| 574 | + return client.sqlRawOps().execute(timeout, query); |
560 | 575 | } catch (Exception e) {
|
561 | 576 | // statement timeout should not affect the current connection
|
562 | 577 | // but can be handled by the caller side
|
563 | 578 | if (e.getCause() instanceof TimeoutException) {
|
564 |
| - throw new StatementTimeoutException(formatError(sql, args), e.getCause()); |
| 579 | + throw new StatementTimeoutException(formatError(query), e.getCause()); |
565 | 580 | }
|
566 | 581 | handleException(e);
|
567 |
| - throw new SQLException(formatError(sql, args), e); |
| 582 | + throw new SQLException(formatError(query), e); |
568 | 583 | }
|
569 | 584 | }
|
570 | 585 |
|
@@ -708,28 +723,74 @@ private void checkHoldabilitySupport(int holdability) throws SQLException {
|
708 | 723 | /**
|
709 | 724 | * Provides error message that contains parameters of failed SQL statement.
|
710 | 725 | *
|
711 |
| - * @param sql SQL Text. |
712 |
| - * @param params Parameters of the SQL statement. |
| 726 | + * @param query SQL query |
713 | 727 | *
|
714 | 728 | * @return Formatted error message.
|
715 | 729 | */
|
716 |
| - private static String formatError(String sql, Object... params) { |
717 |
| - return "Failed to execute SQL: " + sql + ", params: " + Arrays.deepToString(params); |
| 730 | + private static String formatError(SQLQueryHolder query) { |
| 731 | + return "Failed to execute SQL: " + query.getQuery() + ", params: " + query.getParams(); |
718 | 732 | }
|
719 | 733 |
|
720 | 734 | static class SQLTarantoolClientImpl extends TarantoolClientImpl {
|
721 | 735 |
|
| 736 | + private Future<?> executeQuery(SQLQueryHolder queryHolder) { |
| 737 | + return exec(Code.EXECUTE, Key.SQL_TEXT, queryHolder.getQuery(), Key.SQL_BIND, queryHolder.getParams()); |
| 738 | + } |
| 739 | + |
| 740 | + private Future<?> executeQuery(SQLQueryHolder queryHolder, long timeoutMillis) { |
| 741 | + return exec( |
| 742 | + timeoutMillis, Code.EXECUTE, Key.SQL_TEXT, queryHolder.getQuery(), Key.SQL_BIND, queryHolder.getParams() |
| 743 | + ); |
| 744 | + } |
| 745 | + |
722 | 746 | final SQLRawOps sqlRawOps = new SQLRawOps() {
|
723 | 747 | @Override
|
724 |
| - public SQLResultHolder execute(String sql, Object... binds) { |
725 |
| - return (SQLResultHolder) syncGet(exec(Code.EXECUTE, Key.SQL_TEXT, sql, Key.SQL_BIND, binds)); |
| 748 | + public SQLResultHolder execute(SQLQueryHolder query) { |
| 749 | + return (SQLResultHolder) syncGet(executeQuery(query)); |
726 | 750 | }
|
727 | 751 |
|
728 | 752 | @Override
|
729 |
| - public SQLResultHolder execute(long timeoutMillis, String sql, Object... binds) { |
730 |
| - return (SQLResultHolder) syncGet( |
731 |
| - exec(timeoutMillis, Code.EXECUTE, Key.SQL_TEXT, sql, Key.SQL_BIND, binds) |
732 |
| - ); |
| 753 | + public SQLResultHolder execute(long timeoutMillis, SQLQueryHolder query) { |
| 754 | + return (SQLResultHolder) syncGet(executeQuery(query, timeoutMillis)); |
| 755 | + } |
| 756 | + |
| 757 | + @Override |
| 758 | + public SQLBatchResultHolder executeBatch(List<SQLQueryHolder> queries) { |
| 759 | + return executeInternal(queries, (query) -> executeQuery(query)); |
| 760 | + } |
| 761 | + |
| 762 | + @Override |
| 763 | + public SQLBatchResultHolder executeBatch(long timeoutMillis, List<SQLQueryHolder> queries) { |
| 764 | + return executeInternal(queries, (query) -> executeQuery(query, timeoutMillis)); |
| 765 | + } |
| 766 | + |
| 767 | + private SQLBatchResultHolder executeInternal(List<SQLQueryHolder> queries, |
| 768 | + Function<SQLQueryHolder, Future<?>> fetcher) { |
| 769 | + List<Future<?>> sqlFutures = new ArrayList<>(); |
| 770 | + // using queries pipelining to emulate a batch request |
| 771 | + for (SQLQueryHolder query : queries) { |
| 772 | + sqlFutures.add(fetcher.apply(query)); |
| 773 | + } |
| 774 | + // wait for all the results |
| 775 | + Exception lastError = null; |
| 776 | + List<SQLResultHolder> items = new ArrayList<>(queries.size()); |
| 777 | + for (Future<?> future : sqlFutures) { |
| 778 | + try { |
| 779 | + SQLResultHolder result = (SQLResultHolder) syncGet(future); |
| 780 | + if (result.isQueryResult()) { |
| 781 | + lastError = new SQLException( |
| 782 | + "Result set is not allowed in the batch response", |
| 783 | + SQLStates.TOO_MANY_RESULTS.getSqlState() |
| 784 | + ); |
| 785 | + } |
| 786 | + items.add(result); |
| 787 | + } catch (RuntimeException e) { |
| 788 | + // empty result set will be treated as a wrong result |
| 789 | + items.add(SQLResultHolder.ofEmptyQuery()); |
| 790 | + lastError = e; |
| 791 | + } |
| 792 | + } |
| 793 | + return new SQLBatchResultHolder(items, lastError); |
733 | 794 | }
|
734 | 795 | };
|
735 | 796 |
|
@@ -758,9 +819,13 @@ protected void completeSql(TarantoolOp<?> future, TarantoolPacket pack) {
|
758 | 819 |
|
759 | 820 | interface SQLRawOps {
|
760 | 821 |
|
761 |
| - SQLResultHolder execute(String sql, Object... binds); |
| 822 | + SQLResultHolder execute(SQLQueryHolder query); |
| 823 | + |
| 824 | + SQLResultHolder execute(long timeoutMillis, SQLQueryHolder query); |
| 825 | + |
| 826 | + SQLBatchResultHolder executeBatch(List<SQLQueryHolder> queries); |
762 | 827 |
|
763 |
| - SQLResultHolder execute(long timeoutMillis, String sql, Object... binds); |
| 828 | + SQLBatchResultHolder executeBatch(long timeoutMillis, List<SQLQueryHolder> queries); |
764 | 829 |
|
765 | 830 | }
|
766 | 831 |
|
|
0 commit comments