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

Commit d631ce1

Browse files
author
Nikita Glukhov
committed
Add subtransactions for JsonExpr execution
1 parent 39b5797 commit d631ce1

File tree

5 files changed

+94
-1
lines changed

5 files changed

+94
-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/tuptoaster.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"
@@ -77,6 +79,7 @@
7779
#include "utils/jsonb.h"
7880
#include "utils/jsonpath.h"
7981
#include "utils/lsyscache.h"
82+
#include "utils/resowner.h"
8083
#include "utils/timestamp.h"
8184
#include "utils/typcache.h"
8285
#include "utils/xml.h"
@@ -4501,6 +4504,12 @@ ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
45014504
return res;
45024505
}
45034506

4507+
bool
4508+
ExecEvalJsonNeedsSubTransaction(JsonExpr *jsexpr)
4509+
{
4510+
return jsexpr->on_error.btype != JSON_BEHAVIOR_ERROR;
4511+
}
4512+
45044513
/* ----------------------------------------------------------------
45054514
* ExecEvalJson
45064515
* ----------------------------------------------------------------
@@ -4540,20 +4549,34 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
45404549
var->evaluated = false;
45414550
}
45424551

4543-
if (jexpr->on_error.btype == JSON_BEHAVIOR_ERROR)
4552+
if (!ExecEvalJsonNeedsSubTransaction(jexpr))
45444553
{
45454554
/* No need to use PG_TRY/PG_CATCH with subtransactions. */
45464555
res = ExecEvalJsonExpr(state, op, econtext, jexpr, path, item,
45474556
op->resnull);
45484557
}
45494558
else
45504559
{
4560+
/*
4561+
* We should catch exceptions of category ERRCODE_DATA_EXCEPTION and
4562+
* execute corresponding ON ERROR behavior.
4563+
*/
45514564
MemoryContext oldcontext = CurrentMemoryContext;
4565+
ResourceOwner oldowner = CurrentResourceOwner;
4566+
4567+
BeginInternalSubTransaction(NULL);
4568+
/* Want to execute expressions inside function's memory context */
4569+
MemoryContextSwitchTo(oldcontext);
45524570

45534571
PG_TRY();
45544572
{
45554573
res = ExecEvalJsonExpr(state, op, econtext, jexpr, path, item,
45564574
op->resnull);
4575+
4576+
/* Commit the inner transaction, return to outer xact context */
4577+
ReleaseCurrentSubTransaction();
4578+
MemoryContextSwitchTo(oldcontext);
4579+
CurrentResourceOwner = oldowner;
45574580
}
45584581
PG_CATCH();
45594582
{
@@ -4564,6 +4587,11 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
45644587
edata = CopyErrorData();
45654588
FlushErrorState();
45664589

4590+
/* Abort the inner transaction */
4591+
RollbackAndReleaseCurrentSubTransaction();
4592+
MemoryContextSwitchTo(oldcontext);
4593+
CurrentResourceOwner = oldowner;
4594+
45674595
if (ERRCODE_TO_CATEGORY(edata->sqlerrcode) != ERRCODE_DATA_EXCEPTION)
45684596
ReThrowError(edata);
45694597

src/backend/optimizer/util/clauses.c

Lines changed: 11 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"
@@ -1068,6 +1069,16 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
10681069
context, 0);
10691070
}
10701071

1072+
/* JsonExpr is parallel-unsafe if subtransactions can be used. */
1073+
else if (IsA(node, JsonExpr))
1074+
{
1075+
JsonExpr *jsexpr = (JsonExpr *) node;
1076+
1077+
if (ExecEvalJsonNeedsSubTransaction(jsexpr))
1078+
context->max_hazard = PROPARALLEL_UNSAFE;
1079+
return true;
1080+
}
1081+
10711082
/* Recurse to check arguments */
10721083
return expression_tree_walker(node,
10731084
max_parallel_hazard_walker,

src/include/executor/execExpr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,7 @@ extern Datum ExecPrepareJsonItemCoercion(struct JsonItem *item,
809809
JsonReturning *returning,
810810
struct JsonCoercionsState *coercions,
811811
struct JsonCoercionState **pjcstate);
812+
extern bool ExecEvalJsonNeedsSubTransaction(JsonExpr *jsexpr);
812813

813814
extern void ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup);
814815
extern Datum ExecAggTransReparent(AggState *aggstate, AggStatePerTrans pertrans,

src/test/regress/expected/jsonb_sqljson.out

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

src/test/regress/sql/jsonb_sqljson.sql

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

0 commit comments

Comments
 (0)