Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c863327

Browse files
author
Nikita Glukhov
committed
Add subtransactions for JsonExpr execution
1 parent 7247297 commit c863327

File tree

5 files changed

+96
-1
lines changed

5 files changed

+96
-1
lines changed

src/backend/executor/execExprInterp.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
#include "postgres.h"
5858

5959
#include "access/heaptoast.h"
60+
#include "access/xact.h"
61+
#include "catalog/pg_proc.h"
6062
#include "catalog/pg_type.h"
6163
#include "commands/sequence.h"
6264
#include "executor/execExpr.h"
@@ -79,6 +81,7 @@
7981
#include "utils/jsonpath.h"
8082
#include "utils/lsyscache.h"
8183
#include "utils/memutils.h"
84+
#include "utils/resowner.h"
8285
#include "utils/timestamp.h"
8386
#include "utils/typcache.h"
8487
#include "utils/xml.h"
@@ -4816,6 +4819,12 @@ ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
48164819
return res;
48174820
}
48184821

4822+
bool
4823+
ExecEvalJsonNeedsSubTransaction(JsonExpr *jsexpr)
4824+
{
4825+
return jsexpr->on_error->btype != JSON_BEHAVIOR_ERROR;
4826+
}
4827+
48194828
/* ----------------------------------------------------------------
48204829
* ExecEvalJson
48214830
* ----------------------------------------------------------------
@@ -4855,20 +4864,34 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
48554864
var->evaluated = false;
48564865
}
48574866

4858-
if (jexpr->on_error->btype == JSON_BEHAVIOR_ERROR)
4867+
if (!ExecEvalJsonNeedsSubTransaction(jexpr))
48594868
{
48604869
/* No need to use PG_TRY/PG_CATCH with subtransactions. */
48614870
res = ExecEvalJsonExpr(state, op, econtext, jexpr, path, item,
48624871
op->resnull);
48634872
}
48644873
else
48654874
{
4875+
/*
4876+
* We should catch exceptions of category ERRCODE_DATA_EXCEPTION and
4877+
* execute corresponding ON ERROR behavior.
4878+
*/
48664879
MemoryContext oldcontext = CurrentMemoryContext;
4880+
ResourceOwner oldowner = CurrentResourceOwner;
4881+
4882+
BeginInternalSubTransaction(NULL);
4883+
/* Want to execute expressions inside function's memory context */
4884+
MemoryContextSwitchTo(oldcontext);
48674885

48684886
PG_TRY();
48694887
{
48704888
res = ExecEvalJsonExpr(state, op, econtext, jexpr, path, item,
48714889
op->resnull);
4890+
4891+
/* Commit the inner transaction, return to outer xact context */
4892+
ReleaseCurrentSubTransaction();
4893+
MemoryContextSwitchTo(oldcontext);
4894+
CurrentResourceOwner = oldowner;
48724895
}
48734896
PG_CATCH();
48744897
{
@@ -4879,6 +4902,11 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
48794902
edata = CopyErrorData();
48804903
FlushErrorState();
48814904

4905+
/* Abort the inner transaction */
4906+
RollbackAndReleaseCurrentSubTransaction();
4907+
MemoryContextSwitchTo(oldcontext);
4908+
CurrentResourceOwner = oldowner;
4909+
48824910
if (ERRCODE_TO_CATEGORY(edata->sqlerrcode) != ERRCODE_DATA_EXCEPTION)
48834911
ReThrowError(edata);
48844912

src/backend/optimizer/util/clauses.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "catalog/pg_type.h"
2929
#include "executor/executor.h"
3030
#include "executor/functions.h"
31+
#include "executor/execExpr.h"
3132
#include "funcapi.h"
3233
#include "miscadmin.h"
3334
#include "nodes/makefuncs.h"
@@ -1092,6 +1093,18 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
10921093
context, 0);
10931094
}
10941095

1096+
/* JsonExpr is parallel-unsafe if subtransactions can be used. */
1097+
else if (IsA(node, JsonExpr))
1098+
{
1099+
JsonExpr *jsexpr = (JsonExpr *) node;
1100+
1101+
if (ExecEvalJsonNeedsSubTransaction(jsexpr))
1102+
{
1103+
context->max_hazard = PROPARALLEL_UNSAFE;
1104+
return true;
1105+
}
1106+
}
1107+
10951108
/* Recurse to check arguments */
10961109
return expression_tree_walker(node,
10971110
max_parallel_hazard_walker,

src/include/executor/execExpr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,7 @@ extern Datum ExecPrepareJsonItemCoercion(struct JsonbValue *item,
814814
JsonReturning *returning,
815815
struct JsonCoercionsState *coercions,
816816
struct JsonCoercionState **pjcstate);
817+
extern bool ExecEvalJsonNeedsSubTransaction(JsonExpr *jsexpr);
817818

818819
extern void ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup,
819820
ExprContext *aggcontext);

src/test/regress/expected/jsonb_sqljson.out

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,3 +909,40 @@ SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
909909
-- Should fail (invalid path)
910910
SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error');
911911
ERROR: syntax error, unexpected IDENT_P at or near " " of jsonpath input
912+
-- Test parallel JSON_VALUE()
913+
CREATE TABLE test_parallel_jsonb_value AS
914+
SELECT i::text::jsonb AS js
915+
FROM generate_series(1, 1000000) i;
916+
-- Should be non-parallel due to subtransactions
917+
EXPLAIN (COSTS OFF)
918+
SELECT sum(JSON_VALUE(js, '$' RETURNING numeric)) FROM test_parallel_jsonb_value;
919+
QUERY PLAN
920+
---------------------------------------------
921+
Aggregate
922+
-> Seq Scan on test_parallel_jsonb_value
923+
(2 rows)
924+
925+
SELECT sum(JSON_VALUE(js, '$' RETURNING numeric)) FROM test_parallel_jsonb_value;
926+
sum
927+
--------------
928+
500000500000
929+
(1 row)
930+
931+
-- Should be parallel
932+
EXPLAIN (COSTS OFF)
933+
SELECT sum(JSON_VALUE(js, '$' RETURNING numeric ERROR ON ERROR)) FROM test_parallel_jsonb_value;
934+
QUERY PLAN
935+
------------------------------------------------------------------
936+
Finalize Aggregate
937+
-> Gather
938+
Workers Planned: 2
939+
-> Partial Aggregate
940+
-> Parallel Seq Scan on test_parallel_jsonb_value
941+
(5 rows)
942+
943+
SELECT sum(JSON_VALUE(js, '$' RETURNING numeric ERROR ON ERROR)) FROM test_parallel_jsonb_value;
944+
sum
945+
--------------
946+
500000500000
947+
(1 row)
948+

src/test/regress/sql/jsonb_sqljson.sql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,19 @@ SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a');
274274
SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
275275
-- Should fail (invalid path)
276276
SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error');
277+
278+
-- Test parallel JSON_VALUE()
279+
CREATE TABLE test_parallel_jsonb_value AS
280+
SELECT i::text::jsonb AS js
281+
FROM generate_series(1, 1000000) i;
282+
283+
-- Should be non-parallel due to subtransactions
284+
EXPLAIN (COSTS OFF)
285+
SELECT sum(JSON_VALUE(js, '$' RETURNING numeric)) FROM test_parallel_jsonb_value;
286+
SELECT sum(JSON_VALUE(js, '$' RETURNING numeric)) FROM test_parallel_jsonb_value;
287+
288+
-- Should be parallel
289+
EXPLAIN (COSTS OFF)
290+
SELECT sum(JSON_VALUE(js, '$' RETURNING numeric ERROR ON ERROR)) FROM test_parallel_jsonb_value;
291+
SELECT sum(JSON_VALUE(js, '$' RETURNING numeric ERROR ON ERROR)) FROM test_parallel_jsonb_value;
292+

0 commit comments

Comments
 (0)