Skip to content

Commit 878c71e

Browse files
committed
Add optional IN AUTONOMOUS TRANSACTION clause to USING statement
1 parent 3c5a73c commit 878c71e

File tree

6 files changed

+178
-33
lines changed

6 files changed

+178
-33
lines changed

doc/sql.extensions/README.using_statement.md

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,29 @@ input parameter in multiple places), the developer is currently forced to explic
1010
more tediously, all output fields.
1111

1212
The `USING` statement simplifies this workflow. It provides the ability to declare parameters, sub-routines and
13-
variables while allowing the engine to infer outputs automatically from the contained SQL command.
13+
variables, optionally execute the `DO` command in an autonomous transaction, and still allow the engine to infer
14+
outputs automatically from the contained SQL command.
1415

1516
## Syntax
1617

1718
```sql
1819
USING [ ( <input_parameter_list> ) ]
1920
[ <local_declarations> ]
21+
[ IN AUTONOMOUS TRANSACTION ]
2022
DO <sql_command>
2123
```
2224

23-
**Note:** At least one of `<input_parameter_list>` or `<local_declarations>` must be present. A `USING DO ...` statement
24-
without parameters and without local declarations is invalid.
25+
**Note:** At least one of `<input_parameter_list>`, `<local_declarations>` or `IN AUTONOMOUS TRANSACTION` must be
26+
present. A `USING DO ...` statement without parameters, without local declarations and without `IN AUTONOMOUS
27+
TRANSACTION` is invalid.
2528

2629
### Components
2730

2831
* **`<input_parameter_list>`**: A strictly typed list of parameters. These can be bound to values using the `?`
2932
placeholder.
3033
* **`<local_declarations>`**: Standard PSQL local declarations (variables, sub-functions and sub-procedures).
34+
* **`IN AUTONOMOUS TRANSACTION`**: Executes the `DO` command in a separate autonomous transaction, reusing the same
35+
semantics already supported by PSQL.
3136
* **`<sql_command>`**: The DSQL statement to execute. Supported statements include:
3237
* `SELECT`
3338
* `INSERT` (with or without `RETURNING`)
@@ -121,12 +126,21 @@ begin
121126
end
122127
```
123128

129+
### 5. Autonomous Transaction
130+
131+
```sql
132+
using in autonomous transaction
133+
do insert into audit_log (log_time, message)
134+
values (current_timestamp, 'Entry written in autonomous transaction');
135+
```
136+
124137
## Comparison
125138

126-
| Feature | Standard DSQL | `EXECUTE BLOCK` | `USING` |
127-
| :---------------------- | :------------------------------ | :-------------------------------------------- | :-------------------------------------------------------- |
128-
| **Local Declarations** | No | Yes | Yes |
129-
| **Input Declarations** | Implicit (Positional) | Explicit | Hybrid (implicit and explicit) |
130-
| **Output Declarations** | Inferred | Explicit (`RETURNS`) | Inferred |
131-
| **Verbosity** | Low | High | Medium |
132-
| **Use Case** | Simple queries | Complex logic, loops, no result set inference | Reusing params, variables, sub-routines, standard queries |
139+
| Feature | Standard DSQL | `EXECUTE BLOCK` | `USING` |
140+
| :------------------------- | :------------------------------ | :-------------------------------------------- | :-------------------------------------------------------- |
141+
| **Local Declarations** | No | Yes | Yes |
142+
| **Input Declarations** | Implicit (Positional) | Explicit | Hybrid (implicit and explicit) |
143+
| **Output Declarations** | Inferred | Explicit (`RETURNS`) | Inferred |
144+
| **Verbosity** | Low | High | Medium |
145+
| **Use Case** | Simple queries | Complex logic, loops, no result set inference | Reusing params, variables, sub-routines, standard queries |
146+
| **Autonomous transaction** | No | Yes (without `SUSPEND`) | Yes |

src/dsql/StmtNodes.cpp

Lines changed: 124 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2410,6 +2410,9 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
24102410
GEN_port(dsqlScratch, dsqlScratch->recordKeyMessage);
24112411

24122412
std::optional<USHORT> tableNumber;
2413+
const bool useInternalAutoTrans =
2414+
(dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK) &&
2415+
dsqlReturning && !dsqlScratch->isPsql() && dsqlCursorName.isEmpty();
24132416

24142417
const bool skipLocked = dsqlRse && dsqlRse->hasSkipLocked();
24152418

@@ -2430,6 +2433,13 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
24302433
}
24312434
}
24322435

2436+
if (useInternalAutoTrans)
2437+
{
2438+
dsqlScratch->appendUChar(blr_auto_trans);
2439+
dsqlScratch->appendUChar(0);
2440+
dsqlScratch->appendUChar(blr_begin);
2441+
}
2442+
24332443
if (dsqlRse)
24342444
{
24352445
dsqlScratch->appendUChar(blr_for);
@@ -2460,6 +2470,9 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch)
24602470

24612471
if (!dsqlScratch->isPsql() && dsqlCursorName.isEmpty())
24622472
{
2473+
if (useInternalAutoTrans)
2474+
dsqlScratch->appendUChar(blr_end);
2475+
24632476
dsqlGenReturningLocalTableCursor(dsqlScratch, dsqlReturning, tableNumber.value());
24642477

24652478
if (!skipLocked)
@@ -4713,16 +4726,12 @@ DmlNode* InAutonomousTransactionNode::parse(thread_db* tdbb, MemoryPool& pool, C
47134726

47144727
InAutonomousTransactionNode* InAutonomousTransactionNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
47154728
{
4716-
const bool autoTrans = dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK;
4717-
dsqlScratch->flags |= DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK;
4729+
AutoSetRestoreFlag autoTrans(&dsqlScratch->flags, DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK, true);
47184730

47194731
InAutonomousTransactionNode* node = FB_NEW_POOL(dsqlScratch->getPool()) InAutonomousTransactionNode(
47204732
dsqlScratch->getPool());
47214733
node->action = action->dsqlPass(dsqlScratch);
47224734

4723-
if (!autoTrans)
4724-
dsqlScratch->flags &= ~DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK;
4725-
47264735
return node;
47274736
}
47284737

@@ -4737,9 +4746,33 @@ string InAutonomousTransactionNode::internalPrint(NodePrinter& printer) const
47374746

47384747
void InAutonomousTransactionNode::genBlr(DsqlCompilerScratch* dsqlScratch)
47394748
{
4740-
dsqlScratch->appendUChar(blr_auto_trans);
4741-
dsqlScratch->appendUChar(0); // to extend syntax in the future
4742-
action->genBlr(dsqlScratch);
4749+
bool useInternalAutoTrans = false;
4750+
4751+
if (dsqlScratch && !dsqlScratch->isPsql())
4752+
{
4753+
if (const auto modifyNode = nodeAs<ModifyNode>(action))
4754+
useInternalAutoTrans = modifyNode->dsqlReturning && modifyNode->dsqlCursorName.isEmpty();
4755+
else if (const auto eraseNode = nodeAs<EraseNode>(action))
4756+
useInternalAutoTrans = eraseNode->dsqlReturning && eraseNode->dsqlCursorName.isEmpty();
4757+
else if (const auto storeNode = nodeAs<StoreNode>(action))
4758+
useInternalAutoTrans = storeNode->dsqlReturning != nullptr;
4759+
else if (const auto updateOrInsertNode = nodeAs<UpdateOrInsertNode>(action))
4760+
useInternalAutoTrans = updateOrInsertNode->returning != nullptr;
4761+
else if (const auto mergeNode = nodeAs<MergeNode>(action))
4762+
useInternalAutoTrans = mergeNode->returning != nullptr;
4763+
}
4764+
4765+
if (useInternalAutoTrans)
4766+
{
4767+
AutoSetRestoreFlag autoTrans(&dsqlScratch->flags, DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK, true);
4768+
action->genBlr(dsqlScratch);
4769+
}
4770+
else
4771+
{
4772+
dsqlScratch->appendUChar(blr_auto_trans);
4773+
dsqlScratch->appendUChar(0); // to extend syntax in the future
4774+
action->genBlr(dsqlScratch);
4775+
}
47434776
}
47444777

47454778
InAutonomousTransactionNode* InAutonomousTransactionNode::pass1(thread_db* tdbb, CompilerScratch* csb)
@@ -7246,6 +7279,9 @@ string MergeNode::internalPrint(NodePrinter& printer) const
72467279
void MergeNode::genBlr(DsqlCompilerScratch* dsqlScratch)
72477280
{
72487281
std::optional<USHORT> tableNumber;
7282+
const bool useInternalAutoTrans =
7283+
(dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK) &&
7284+
returning && !dsqlScratch->isPsql();
72497285

72507286
if (returning && !dsqlScratch->isPsql())
72517287
{
@@ -7255,6 +7291,13 @@ void MergeNode::genBlr(DsqlCompilerScratch* dsqlScratch)
72557291
dsqlGenReturningLocalTableDecl(dsqlScratch, tableNumber.value());
72567292
}
72577293

7294+
if (useInternalAutoTrans)
7295+
{
7296+
dsqlScratch->appendUChar(blr_auto_trans);
7297+
dsqlScratch->appendUChar(0);
7298+
dsqlScratch->appendUChar(blr_begin);
7299+
}
7300+
72587301
// Put src info for blr_for.
72597302
if (hasLineColumn)
72607303
dsqlScratch->putDebugSrcInfo(line, column);
@@ -7493,6 +7536,9 @@ void MergeNode::genBlr(DsqlCompilerScratch* dsqlScratch)
74937536
dsqlScratch->appendUChar(blr_end);
74947537
}
74957538

7539+
if (useInternalAutoTrans)
7540+
dsqlScratch->appendUChar(blr_end);
7541+
74967542
if (returning && !dsqlScratch->isPsql())
74977543
{
74987544
dsqlGenReturningLocalTableCursor(dsqlScratch, returning, tableNumber.value());
@@ -7949,6 +7995,15 @@ string ModifyNode::internalPrint(NodePrinter& printer) const
79497995

79507996
void ModifyNode::genBlr(DsqlCompilerScratch* dsqlScratch)
79517997
{
7998+
const bool useInternalAutoTrans =
7999+
(dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK) &&
8000+
dsqlReturning && !dsqlScratch->isPsql() && dsqlCursorName.isEmpty() &&
8001+
dsqlReturningLocalTableNumber.has_value() &&
8002+
!(dsqlScratch->flags & DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT);
8003+
const bool deferUpdateOrInsertLocalTableDecl =
8004+
(dsqlScratch->flags & DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT) &&
8005+
(dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK);
8006+
79528007
if (dsqlScratch->recordKeyMessage)
79538008
{
79548009
GEN_port(dsqlScratch, dsqlScratch->recordKeyMessage);
@@ -7957,14 +8012,24 @@ void ModifyNode::genBlr(DsqlCompilerScratch* dsqlScratch)
79578012
if (dsqlReturning && !dsqlScratch->isPsql())
79588013
{
79598014
if (dsqlCursorName.isEmpty())
7960-
dsqlGenReturningLocalTableDecl(dsqlScratch, dsqlReturningLocalTableNumber.value());
8015+
{
8016+
if (!deferUpdateOrInsertLocalTableDecl)
8017+
dsqlGenReturningLocalTableDecl(dsqlScratch, dsqlReturningLocalTableNumber.value());
8018+
}
79618019
else
79628020
{
79638021
dsqlScratch->appendUChar(blr_send);
79648022
dsqlScratch->appendUChar(dsqlScratch->getDsqlStatement()->getReceiveMsg()->msg_number);
79658023
}
79668024
}
79678025

8026+
if (useInternalAutoTrans)
8027+
{
8028+
dsqlScratch->appendUChar(blr_auto_trans);
8029+
dsqlScratch->appendUChar(0);
8030+
dsqlScratch->appendUChar(blr_begin);
8031+
}
8032+
79688033
if (dsqlRse)
79698034
{
79708035
dsqlScratch->appendUChar(blr_for);
@@ -8002,6 +8067,9 @@ void ModifyNode::genBlr(DsqlCompilerScratch* dsqlScratch)
80028067
!(dsqlScratch->flags & DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT) &&
80038068
dsqlCursorName.isEmpty())
80048069
{
8070+
if (useInternalAutoTrans)
8071+
dsqlScratch->appendUChar(blr_end);
8072+
80058073
dsqlGenReturningLocalTableCursor(dsqlScratch, dsqlReturning, dsqlReturningLocalTableNumber.value());
80068074
}
80078075
}
@@ -9002,6 +9070,11 @@ string StoreNode::internalPrint(NodePrinter& printer) const
90029070

90039071
void StoreNode::genBlr(DsqlCompilerScratch* dsqlScratch)
90049072
{
9073+
const bool useInternalAutoTrans =
9074+
(dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK) &&
9075+
dsqlReturning && !dsqlScratch->isPsql() && dsqlReturningLocalTableNumber.has_value() &&
9076+
!(dsqlScratch->flags & DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT);
9077+
90059078
if (dsqlReturning && !dsqlScratch->isPsql())
90069079
{
90079080
if (dsqlRse)
@@ -9013,6 +9086,13 @@ void StoreNode::genBlr(DsqlCompilerScratch* dsqlScratch)
90139086
}
90149087
}
90159088

9089+
if (useInternalAutoTrans)
9090+
{
9091+
dsqlScratch->appendUChar(blr_auto_trans);
9092+
dsqlScratch->appendUChar(0);
9093+
dsqlScratch->appendUChar(blr_begin);
9094+
}
9095+
90169096
if (dsqlRse)
90179097
{
90189098
dsqlScratch->appendUChar(blr_for);
@@ -9035,10 +9115,18 @@ void StoreNode::genBlr(DsqlCompilerScratch* dsqlScratch)
90359115

90369116
if (dsqlReturningLocalTableNumber.has_value())
90379117
{
9118+
const bool deferUpdateOrInsertCursor =
9119+
(dsqlScratch->flags & DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT) &&
9120+
(dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK);
9121+
90389122
if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT)
90399123
dsqlScratch->appendUChar(blr_end); // close blr_if (blr_eql, blr_internal_info)
90409124

9041-
dsqlGenReturningLocalTableCursor(dsqlScratch, dsqlReturning, dsqlReturningLocalTableNumber.value());
9125+
if (useInternalAutoTrans)
9126+
dsqlScratch->appendUChar(blr_end);
9127+
9128+
if (!deferUpdateOrInsertCursor)
9129+
dsqlGenReturningLocalTableCursor(dsqlScratch, dsqlReturning, dsqlReturningLocalTableNumber.value());
90429130
}
90439131
}
90449132
else if (overrideClause.has_value())
@@ -10745,6 +10833,19 @@ string UpdateOrInsertNode::internalPrint(NodePrinter& printer) const
1074510833

1074610834
void UpdateOrInsertNode::genBlr(DsqlCompilerScratch* dsqlScratch)
1074710835
{
10836+
const bool useInternalAutoTrans =
10837+
(dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK) &&
10838+
storeNode->dsqlReturningLocalTableNumber.has_value() && !dsqlScratch->isPsql();
10839+
10840+
if (useInternalAutoTrans)
10841+
{
10842+
dsqlGenReturningLocalTableDecl(dsqlScratch, storeNode->dsqlReturningLocalTableNumber.value());
10843+
10844+
dsqlScratch->appendUChar(blr_auto_trans);
10845+
dsqlScratch->appendUChar(0);
10846+
dsqlScratch->appendUChar(blr_begin);
10847+
}
10848+
1074810849
dsqlScratch->appendUChar(blr_begin);
1074910850

1075010851
for (auto& varAssign : varAssignments)
@@ -10777,6 +10878,13 @@ void UpdateOrInsertNode::genBlr(DsqlCompilerScratch* dsqlScratch)
1077710878
dsqlScratch->appendUChar(blr_end); // blr_if
1077810879

1077910880
dsqlScratch->appendUChar(blr_end);
10881+
10882+
if (useInternalAutoTrans)
10883+
{
10884+
dsqlScratch->appendUChar(blr_end);
10885+
dsqlGenReturningLocalTableCursor(dsqlScratch, storeNode->dsqlReturning,
10886+
storeNode->dsqlReturningLocalTableNumber.value());
10887+
}
1078010888
}
1078110889

1078210890

@@ -10959,21 +11067,23 @@ void UserSavepointNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra**
1095911067

1096011068
StmtNode* UsingNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
1096111069
{
10962-
// USING without parameters and subroutines is useless
10963-
if (parameters.isEmpty() && !localDeclList)
11070+
// USING without parameters, subroutines or autonomous transaction is useless
11071+
if (parameters.isEmpty() && !localDeclList && !inAutonomousTransaction)
1096411072
{
1096511073
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
10966-
Arg::Gds(isc_dsql_using_requires_params_subroutines));
11074+
Arg::Gds(isc_dsql_using_statement_must_contain_clause));
1096711075
}
1096811076

1096911077
dsqlScratch->flags |= DsqlCompilerScratch::FLAG_USING_STATEMENT;
11078+
dsqlScratch->reserveInitialVarNumbers(parameters.getCount());
1097011079

1097111080
const auto statement = dsqlScratch->getDsqlStatement();
1097211081
unsigned index = 0;
1097311082

1097411083
const auto node = FB_NEW_POOL(dsqlScratch->getPool()) UsingNode(dsqlScratch->getPool());
1097511084

1097611085
node->parameters = parameters;
11086+
node->inAutonomousTransaction = inAutonomousTransaction;
1097711087

1097811088
for (auto newParam : node->parameters)
1097911089
{
@@ -11036,6 +11146,7 @@ string UsingNode::internalPrint(NodePrinter& printer) const
1103611146
NODE_PRINT(printer, parameters);
1103711147
NODE_PRINT(printer, localDeclList);
1103811148
NODE_PRINT(printer, body);
11149+
NODE_PRINT(printer, inAutonomousTransaction);
1103911150

1104011151
return "UsingNode";
1104111152
}

src/dsql/StmtNodes.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,14 @@ class SavepointEncloseNode final : public TypedNode<StmtNode, StmtNode::TYPE_SAV
15441544

15451545
static StmtNode* make(MemoryPool& pool, DsqlCompilerScratch* dsqlScratch, StmtNode* node, bool force = false);
15461546

1547+
private:
1548+
explicit SavepointEncloseNode(MemoryPool& pool, StmtNode* stmt)
1549+
: TypedNode<StmtNode, StmtNode::TYPE_SAVEPOINT>(pool),
1550+
statement(stmt)
1551+
{
1552+
}
1553+
1554+
public:
15471555
Firebird::string internalPrint(NodePrinter& printer) const override;
15481556
SavepointEncloseNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) override;
15491557
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
@@ -1553,13 +1561,7 @@ class SavepointEncloseNode final : public TypedNode<StmtNode, StmtNode::TYPE_SAV
15531561

15541562
const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const override;
15551563

1556-
private:
1557-
explicit SavepointEncloseNode(MemoryPool& pool, StmtNode* stmt)
1558-
: TypedNode<StmtNode, StmtNode::TYPE_SAVEPOINT>(pool),
1559-
statement(stmt)
1560-
{
1561-
}
1562-
1564+
public:
15631565
NestConst<StmtNode> statement;
15641566
};
15651567

@@ -2116,6 +2118,7 @@ class UsingNode final : public TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_USING>
21162118
Firebird::Array<NestConst<ParameterClause>> parameters;
21172119
NestConst<LocalDeclarationsNode> localDeclList;
21182120
NestConst<StmtNode> body;
2121+
bool inAutonomousTransaction = false;
21192122
};
21202123

21212124

0 commit comments

Comments
 (0)