| 515 | } |
| 516 | |
| 517 | @Nullable |
| 518 | public CompletableFuture<?> execute(String portalName, int maxRows, ResultReceiver<?> resultReceiver) { |
| 519 | if (LOGGER.isDebugEnabled()) { |
| 520 | LOGGER.debug("method=execute portalName={} maxRows={}", portalName, maxRows); |
| 521 | } |
| 522 | Portal portal = getSafePortal(portalName); |
| 523 | var activeConsumer = portal.activeConsumer(); |
| 524 | if (activeConsumer != null && activeConsumer.suspended()) { |
| 525 | activeConsumer.replaceResultReceiver(resultReceiver, maxRows); |
| 526 | activeConsumer.resume(); |
| 527 | return resultReceiver.completionFuture(); |
| 528 | } |
| 529 | |
| 530 | var analyzedStmt = portal.analyzedStatement(); |
| 531 | if (isReadOnly && analyzedStmt.isWriteOperation()) { |
| 532 | throw new ReadOnlyException(portal.preparedStmt().rawStatement()); |
| 533 | } |
| 534 | if (analyzedStmt instanceof AnalyzedBegin) { |
| 535 | currentTransactionState = TransactionState.IN_TRANSACTION; |
| 536 | resultReceiver.allFinished(); |
| 537 | } else if (analyzedStmt instanceof AnalyzedCommit) { |
| 538 | currentTransactionState = TransactionState.IDLE; |
| 539 | cursors.close(cursor -> cursor.hold() == Hold.WITHOUT); |
| 540 | resultReceiver.allFinished(); |
| 541 | return resultReceiver.completionFuture(); |
| 542 | } else if (analyzedStmt instanceof AnalyzedDeallocate ad) { |
| 543 | String stmtToDeallocate = ad.preparedStmtName(); |
| 544 | if (stmtToDeallocate != null) { |
| 545 | close((byte) 'S', stmtToDeallocate); |
| 546 | } else { |
| 547 | if (LOGGER.isDebugEnabled()) { |
| 548 | LOGGER.debug("deallocating all prepared statements"); |
| 549 | } |
| 550 | preparedStatements.clear(); |
| 551 | } |
| 552 | resultReceiver.allFinished(); |
| 553 | } else if (analyzedStmt instanceof AnalyzedDiscard discard) { |
| 554 | // We don't cache plans, don't have sequences or temporary tables |
| 555 | // See https://www.postgresql.org/docs/current/sql-discard.html |
| 556 | if (discard.target() == Target.ALL) { |
| 557 | close(); |
| 558 | } |
| 559 | resultReceiver.allFinished(); |
| 560 | } else if (analyzedStmt.isWriteOperation()) { |
| 561 | /* We defer the execution for any other statements to `sync` messages so that we can efficiently process |
| 562 | * bulk operations. E.g. If we receive `INSERT INTO (x) VALUES (?)` bindings/execute multiple times |
| 563 | * We want to create bulk requests internally: / |
| 564 | * - To reduce network overhead |
| 565 | * - To have 1 disk flush per shard instead of 1 disk flush per item |
| 566 | * |
| 567 | * Many clients support this by doing something like this: |
| 568 | * |
| 569 | * var preparedStatement = conn.prepareStatement("...") |
| 570 | * for (var args in manyArgs): |
| 571 | * preparedStatement.execute(args) |
| 572 | * conn.commit() |
| 573 | */ |
| 574 | deferredExecutionsByStmt.computeIfAbsent( |