Обсуждение: POC: PLpgSQL FOREACH IN JSON ARRAY
Hi
I wrote PoC for previously proposed plpgsql statement FOREACH IN JSON ARRAY
It looks like:
do $$
declare x int;
begin
foreach x in json array '[1,2,3,4]'
loop
raise notice 'x: %', x;
end loop;
end;
$$
do $$
declare x int; y int;
begin
foreach x, y in json array '[{"x": 100, "y": 1000}, {"y": 1000, "x": 100}]'
loop
raise notice 'x: %, y: %', x, y;
end loop;
end
$$
My first motivation for this patch is performance. This is faster (3 - 4 x) than using FOR IN SELECT FROM json_array_elements, because there is no overhead of SQL executor. Second motivation is a little bit better readability, because inside plpgsql' statements we have info about used variables and we can use it.
The behavior is very similar to FOREACH IN ARRAY with one significant difference - the values of JSON objects are assigned to the composite variable or lists of variables by names (not by position). It made this decision because jsonb doesn't preserve the position of the field in object, and then assignment based on position cannot work.
The code is relatively short now - about 400 lines +/- and the code is simple without risks.
There are some open questions - mainly if default mode for mapping json fields to plpgsql variables should be in lax or strict mode. Now, it is something between (cast errors are raised) - it is consistent with jsonb_populate_record - but it should not be the final design. I cannot say what is better - currently implemented behavior is consistent with common plpgsql behaviour, but SQL/JSON is different. I can imagine that default behaviour will be lax, and with some optional clauses we can push behave to strict mode. I have no strong opinion about it. Maybe I prefer the current "strict" behaviour a little bit, because it is more "safe", but it is only my personal opinion. But again, I have no strong opinion about this question and I very much invite any discussion about it.
This is proof of concept patch - casting between plpgsql arrays and json arrays is not supported, documentation and regress tests are minimalistic, but it is good enough for testing and good enough for decision, if this feature is wanted or not (or if it needs some modifications).
This is a new feature (and proprietary feature). There should not be any compatibility issues.
What do you think about this feature?
Regards
Pavel
Вложения
Hi
small update
Now assignment to plpgsql array variable and json array works. Some minor code cleaning + more regress tests.
Regards
Pavel
Вложения
ne 1. 3. 2026 v 6:44 odesílatel Pavel Stehule <pavel.stehule@gmail.com> napsal:
Hismall updateNow assignment to plpgsql array variable and json array works. Some minor code cleaning + more regress tests.
fix uninitialized argument of json_populate_type function
regards
Pavel
RegardsPavel
Вложения
Hi Pavel,
I quickly tested the patch, and I also could observe a ~3x performance
improvement!
A few first impressions:
## in exec_stmt_foreach_json_a the boolean variable found is declared as
false, bit its value is never set to true until exec_set_found() is called:
/*
* Set the FOUND variable to indicate the result of executing the loop
* (namely, whether we looped one or more times). This must be set here so
* that it does not interfere with the value of the FOUND variable inside
* the loop processing itself.
*/
exec_set_found(estate, found);
Test:
DO $$
DECLARE
x int;
BEGIN
FOREACH x IN JSON ARRAY '[1,2,3]'
LOOP
RAISE NOTICE 'x: %', x;
END LOOP;
IF FOUND THEN
RAISE NOTICE 'FOUND is true';
ELSE
RAISE NOTICE 'FOUND is false';
END IF;
END;
$$;
NOTICE: x: 1
NOTICE: x: 2
NOTICE: x: 3
NOTICE: FOUND is false
## Suggestion in the plpgsql.sgml
The <literal>FOREACH</literal> loop is much like a
<literal>FOREACH</literal> loop,
to
"much like a regular <literal>FOREACH</literal> loop over arrays"
## Typo in comment
/*
* We cannot to use fieldnames for tupdescentry, because
* these names can be suffixed by name of row variable.
...
We cannot to use > We cannot use
## Nit pick
These error messages are not wrong, but IMO a errhint/errdetail could
add some value here:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot extract elements from a scalar")));
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot extract elements from an object")));
Something like this perhaps?
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot extract elements from a scalar"),
errhint("FOREACH IN JSON ARRAY requires an array value.")));
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("FOREACH expression must evaluate to a JSON array"),
errdetail("Cannot iterate over a scalar value.")));
Thanks for the patch!
Best, Jim
Hi
út 3. 3. 2026 v 8:43 odesílatel Jim Jones <jim.jones@uni-muenster.de> napsal:
Hi Pavel,
I quickly tested the patch, and I also could observe a ~3x performance
improvement!
A few first impressions:
## in exec_stmt_foreach_json_a the boolean variable found is declared as
false, bit its value is never set to true until exec_set_found() is called:
/*
* Set the FOUND variable to indicate the result of executing the loop
* (namely, whether we looped one or more times). This must be set here so
* that it does not interfere with the value of the FOUND variable inside
* the loop processing itself.
*/
exec_set_found(estate, found);
Test:
DO $$
DECLARE
x int;
BEGIN
FOREACH x IN JSON ARRAY '[1,2,3]'
LOOP
RAISE NOTICE 'x: %', x;
END LOOP;
IF FOUND THEN
RAISE NOTICE 'FOUND is true';
ELSE
RAISE NOTICE 'FOUND is false';
END IF;
END;
$$;
NOTICE: x: 1
NOTICE: x: 2
NOTICE: x: 3
NOTICE: FOUND is false
fixed + regress tests
## Suggestion in the plpgsql.sgml
The <literal>FOREACH</literal> loop is much like a
<literal>FOREACH</literal> loop,
to
"much like a regular <literal>FOREACH</literal> loop over arrays"
done
## Typo in comment
/*
* We cannot to use fieldnames for tupdescentry, because
* these names can be suffixed by name of row variable.
...
We cannot to use > We cannot use
fixed
## Nit pick
These error messages are not wrong, but IMO a errhint/errdetail could
add some value here:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot extract elements from a scalar")));
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot extract elements from an object")));
Something like this perhaps?
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot extract elements from a scalar"),
errhint("FOREACH IN JSON ARRAY requires an array value.")));
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("FOREACH expression must evaluate to a JSON array"),
errdetail("Cannot iterate over a scalar value.")));
I rewrote it to
if (JB_ROOT_IS_SCALAR(jb))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("FOREACH expression must evaluate to a JSON array"),
errhint("Cannot iterate over a scalar value.")));
else if (JB_ROOT_IS_OBJECT(jb))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("FOREACH expression must evaluate to a JSON array"),
errdetail("Cannot iterate over a object value.")));
Assert(JB_ROOT_IS_ARRAY(jb));
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("FOREACH expression must evaluate to a JSON array"),
errhint("Cannot iterate over a scalar value.")));
else if (JB_ROOT_IS_OBJECT(jb))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("FOREACH expression must evaluate to a JSON array"),
errdetail("Cannot iterate over a object value.")));
Assert(JB_ROOT_IS_ARRAY(jb));
+ regress tests
Thanks for the patch!
Thank you for check
Regards
Pavel
Best, Jim
Вложения
I reviewed the code I have nothing to add at this point. LGTM!
The tests touch a lot of different scenarios, but for the sake of
completeness I'd like to suggest adding these three cases:
-- EXIT and CONTINUE can be triggered by LOOP_RC_PROCESSING
DO $$
DECLARE x int;
BEGIN
FOREACH x IN JSON ARRAY '[1,2,3,4,5]'
LOOP
EXIT WHEN x = 3;
RAISE NOTICE '%', x;
END LOOP;
END;
$$;
DO $$
DECLARE x int;
BEGIN
FOREACH x IN JSON ARRAY '[1,2,3,4,5]'
LOOP
CONTINUE WHEN x % 2 = 0;
RAISE NOTICE '%', x;
END LOOP;
END;
$$;
-- Variable instead of string
DO $$
DECLARE x int; arr jsonb;
BEGIN
SELECT jsonb_agg(i) INTO arr
FROM generate_series(1,3) i;
FOREACH x IN JSON ARRAY arr
LOOP
RAISE NOTICE '%', x;
END LOOP;
END;
$$;
Thanks!
Best, Jim
Hi
st 4. 3. 2026 v 12:35 odesílatel Jim Jones <jim.jones@uni-muenster.de> napsal:
I reviewed the code I have nothing to add at this point. LGTM!
The tests touch a lot of different scenarios, but for the sake of
completeness I'd like to suggest adding these three cases:
-- EXIT and CONTINUE can be triggered by LOOP_RC_PROCESSING
DO $$
DECLARE x int;
BEGIN
FOREACH x IN JSON ARRAY '[1,2,3,4,5]'
LOOP
EXIT WHEN x = 3;
RAISE NOTICE '%', x;
END LOOP;
END;
$$;
DO $$
DECLARE x int;
BEGIN
FOREACH x IN JSON ARRAY '[1,2,3,4,5]'
LOOP
CONTINUE WHEN x % 2 = 0;
RAISE NOTICE '%', x;
END LOOP;
END;
$$;
-- Variable instead of string
DO $$
DECLARE x int; arr jsonb;
BEGIN
SELECT jsonb_agg(i) INTO arr
FROM generate_series(1,3) i;
FOREACH x IN JSON ARRAY arr
LOOP
RAISE NOTICE '%', x;
END LOOP;
END;
$$;
I merged these examples to tests
Thank you for review
Regards
Pavel
Thanks!
Best, Jim
Вложения
Status: SUCCESS Applied against: /Users/shreeyasharma/postgresql (Current HEAD) Build status: PASS Test status: PASS Notes: - Compilation passed: True - Regression tests (make check) passed: True
The following review has been posted through the commitfest application: make installcheck-world: tested, passed Implements feature: tested, passed Spec compliant: tested, passed Documentation: tested, passed The changes are good The new status of this patch is: Ready for Committer
po 9. 3. 2026 v 7:12 odesílatel Shreeya Sharma <shreeya2304@gmail.com> napsal:
The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, passed
Spec compliant: tested, passed
Documentation: tested, passed
The changes are good
The new status of this patch is: Ready for Committer
Thank you very much
Regards
Pavel
On 28.02.26 08:10, Pavel Stehule wrote: > I wrote PoC for previously proposed plpgsql statement FOREACH IN JSON ARRAY Maybe this could be written in such a way that it doesn't hardcode JSON arrays specifically, but a type could have an iteration helper function that would feed this feature?
> On Mar 5, 2026, at 02:50, Pavel Stehule <pavel.stehule@gmail.com> wrote:
>
> Hi
>
> st 4. 3. 2026 v 12:35 odesílatel Jim Jones <jim.jones@uni-muenster.de> napsal:
> I reviewed the code I have nothing to add at this point. LGTM!
>
> The tests touch a lot of different scenarios, but for the sake of
> completeness I'd like to suggest adding these three cases:
>
> -- EXIT and CONTINUE can be triggered by LOOP_RC_PROCESSING
> DO $$
> DECLARE x int;
> BEGIN
> FOREACH x IN JSON ARRAY '[1,2,3,4,5]'
> LOOP
> EXIT WHEN x = 3;
> RAISE NOTICE '%', x;
> END LOOP;
> END;
> $$;
>
> DO $$
> DECLARE x int;
> BEGIN
> FOREACH x IN JSON ARRAY '[1,2,3,4,5]'
> LOOP
> CONTINUE WHEN x % 2 = 0;
> RAISE NOTICE '%', x;
> END LOOP;
> END;
> $$;
>
>
> -- Variable instead of string
> DO $$
> DECLARE x int; arr jsonb;
> BEGIN
> SELECT jsonb_agg(i) INTO arr
> FROM generate_series(1,3) i;
>
> FOREACH x IN JSON ARRAY arr
> LOOP
> RAISE NOTICE '%', x;
> END LOOP;
> END;
> $$;
>
>
> I merged these examples to tests
>
> Thank you for review
>
> Regards
>
> Pavel
>
> Thanks!
>
> Best, Jim
> <v20260304-5-0001-FOREACH-scalar-IN-JSON-ARRAY.patch>
I just reviewed and tested the patch. Here comes my comments:
1 - pl_gram.y
```
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("not zero slice is allowed only for arrays"),
+ parser_errposition(@4)));
```
* () around errcode and errmsg are no longer needed. This comment is general, and I saw other ereport() also use () in
thispatch.
* parser_errposition should have the same indention as errmsg.
2 - pl_exec.c
```
+ errdetail("Cannot iterate over a object value.")));
```
Typo: a -> an
3 - pl_exec.c
```
+ tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
+ "FOREACH IN JSON ARRAY temporary cxt",
+ ALLOCSET_DEFAULT_SIZES);
```
Do we need to destroy tmp_cxt after the loop?
4 Looks like record type of loop var is not supported:
```
evantest=# do $$
declare
r record;
begin
foreach r in json array '[{"x":1,"y":"hi"},{"x":2,"y":"hello"}]’
loop
raise notice 'x: %, y: %', r.x, r.y;
end loop;
end;
$$;
ERROR: record type has not been registered
CONTEXT: PL/pgSQL function inline_code_block line 5 at FOREACH over json array
```
So, I want to check if you intentionally don’t want to support that or just missed that? If it’s not supported, then
maybedocument that.
5 I tried that composite type of loop var is supported, maybe add a test case for that. What I tested:
```
create type t_foreach_json_row as (
x int,
y text,
z numeric
);
do $$
declare
r t_foreach_json_row;
begin
foreach r in json array
'[{"x":1,"y":"one","z":1.5},
{"x":2,"y":"two"},
{"y":"three","z":3.14},
{}]'
loop
raise notice 'x=%, y=%, z=%', r.x, r.y, r.z;
end loop;
end;
$$;
drop type t_foreach_json_row;
```
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Peter Eisentraut <peter@eisentraut.org> writes:
> Maybe this could be written in such a way that it doesn't hardcode JSON
> arrays specifically, but a type could have an iteration helper function
> that would feed this feature?
+1. ISTM that this feature would make sense for subscriptable types,
so one way to shoehorn it into the system without a lot of new overhead
could be to extend struct SubscriptRoutines to offer optional support
function(s) for iterating through all the elements of a subscriptable
object.
regards, tom lane
Hi
čt 12. 3. 2026 v 5:30 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
Peter Eisentraut <peter@eisentraut.org> writes:
> Maybe this could be written in such a way that it doesn't hardcode JSON
> arrays specifically, but a type could have an iteration helper function
> that would feed this feature?
+1. ISTM that this feature would make sense for subscriptable types,
so one way to shoehorn it into the system without a lot of new overhead
could be to extend struct SubscriptRoutines to offer optional support
function(s) for iterating through all the elements of a subscriptable
object.
I'll try to write second patch in this way
Regards
Pavel
regards, tom lane
Hi
čt 12. 3. 2026 v 4:55 odesílatel Chao Li <li.evan.chao@gmail.com> napsal:
> On Mar 5, 2026, at 02:50, Pavel Stehule <pavel.stehule@gmail.com> wrote:
>
> Hi
>
> st 4. 3. 2026 v 12:35 odesílatel Jim Jones <jim.jones@uni-muenster.de> napsal:
> I reviewed the code I have nothing to add at this point. LGTM!
>
> The tests touch a lot of different scenarios, but for the sake of
> completeness I'd like to suggest adding these three cases:
>
> -- EXIT and CONTINUE can be triggered by LOOP_RC_PROCESSING
> DO $$
> DECLARE x int;
> BEGIN
> FOREACH x IN JSON ARRAY '[1,2,3,4,5]'
> LOOP
> EXIT WHEN x = 3;
> RAISE NOTICE '%', x;
> END LOOP;
> END;
> $$;
>
> DO $$
> DECLARE x int;
> BEGIN
> FOREACH x IN JSON ARRAY '[1,2,3,4,5]'
> LOOP
> CONTINUE WHEN x % 2 = 0;
> RAISE NOTICE '%', x;
> END LOOP;
> END;
> $$;
>
>
> -- Variable instead of string
> DO $$
> DECLARE x int; arr jsonb;
> BEGIN
> SELECT jsonb_agg(i) INTO arr
> FROM generate_series(1,3) i;
>
> FOREACH x IN JSON ARRAY arr
> LOOP
> RAISE NOTICE '%', x;
> END LOOP;
> END;
> $$;
>
>
> I merged these examples to tests
>
> Thank you for review
>
> Regards
>
> Pavel
>
> Thanks!
>
> Best, Jim
> <v20260304-5-0001-FOREACH-scalar-IN-JSON-ARRAY.patch>
I just reviewed and tested the patch. Here comes my comments:
1 - pl_gram.y
```
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("not zero slice is allowed only for arrays"),
+ parser_errposition(@4)));
```
* () around errcode and errmsg are no longer needed. This comment is general, and I saw other ereport() also use () in this patch.
* parser_errposition should have the same indention as errmsg.
fixed
2 - pl_exec.c
```
+ errdetail("Cannot iterate over a object value.")));
```
Typo: a -> an
fixed
3 - pl_exec.c
```
+ tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
+ "FOREACH IN JSON ARRAY temporary cxt",
+ ALLOCSET_DEFAULT_SIZES);
```
Do we need to destroy tmp_cxt after the loop?
no - it is cleaned by MemoryContextReset(stmt_mcontext);
4 Looks like record type of loop var is not supported:
```
evantest=# do $$
declare
r record;
begin
foreach r in json array '[{"x":1,"y":"hi"},{"x":2,"y":"hello"}]’
loop
raise notice 'x: %, y: %', r.x, r.y;
end loop;
end;
$$;
ERROR: record type has not been registered
CONTEXT: PL/pgSQL function inline_code_block line 5 at FOREACH over json array
```
So, I want to check if you intentionally don’t want to support that or just missed that? If it’s not supported, then maybe document that.
It is intentional at this moment (and I think so it will be in future too). For reading fields from a json object I use the json_populate_type function, and this function needs a known tupdesc. Generally JSON objects have no fixed structure, and when a record's variable has no assigned type, then we have to create new tupdesc for each value. This can be possibly slow and memory expensive. Probably - I never tested this case. It is valid use case, but it can be solved in later - and the support will be more invasive - requires support in json_populate_type
I enhanced doc
<para>
The target variable can be of type RECORD, but the real structure has to be
assigned before usage in FOREACH statement.
</para>
The target variable can be of type RECORD, but the real structure has to be
assigned before usage in FOREACH statement.
</para>
5 I tried that composite type of loop var is supported, maybe add a test case for that. What I tested:
```
create type t_foreach_json_row as (
x int,
y text,
z numeric
);
do $$
declare
r t_foreach_json_row;
begin
foreach r in json array
'[{"x":1,"y":"one","z":1.5},
{"x":2,"y":"two"},
{"y":"three","z":3.14},
{}]'
loop
raise notice 'x=%, y=%, z=%', r.x, r.y, r.z;
end loop;
end;
$$;
drop type t_foreach_json_row;
```
it is there already
create type t3 as (x int, y numeric, z varchar);
do $$
declare c t3;
begin
foreach c in json array '[{}, {"z":"Hi"}, {"y": 3.14}, {"z":"Hi", "x":10, "y":3.14}]'
loop
raise notice 'x: %, y: %, z: %', c.x, c.y, c.z;
end loop;
end;
$$;
do $$
declare c t3;
begin
foreach c in json array '[{}, {"z":"Hi"}, {"y": 3.14}, {"z":"Hi", "x":10, "y":3.14}]'
loop
raise notice 'x: %, y: %, z: %', c.x, c.y, c.z;
end loop;
end;
$$;
assigned updated version -
I'll try to modify this patch like Tom proposed in the next version. But the fundament behavior should be same
Thank you for check and testing
Regards
Pavel
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Вложения
Hi
čt 12. 3. 2026 v 8:00 odesílatel Pavel Stehule <pavel.stehule@gmail.com> napsal:
Hičt 12. 3. 2026 v 5:30 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:Peter Eisentraut <peter@eisentraut.org> writes:
> Maybe this could be written in such a way that it doesn't hardcode JSON
> arrays specifically, but a type could have an iteration helper function
> that would feed this feature?
+1. ISTM that this feature would make sense for subscriptable types,
so one way to shoehorn it into the system without a lot of new overhead
could be to extend struct SubscriptRoutines to offer optional support
function(s) for iterating through all the elements of a subscriptable
object.
attached patch do this - new interface has two methods: CreateForeachAIterator and iterate
diff --git a/src/include/nodes/subscripting.h b/src/include/nodes/subscripting.h
index 301f21dac2f..08bfe59ede4 100644
--- a/src/include/nodes/subscripting.h
+++ b/src/include/nodes/subscripting.h
@@ -154,6 +154,32 @@ typedef void (*SubscriptExecSetup) (const SubscriptingRef *sbsref,
SubscriptingRefState *sbsrefstate,
SubscriptExecSteps *methods);
+typedef struct _ForeachAIterator ForeachAIterator;
+
+/*
+ * ForeachAIiterator is used by PLpgSQL FOREACH IN ARRAY statement.
+ * Input value should not be null, and inside CreateForeachAIterator
+ * routine must be copied to current (statement) context. "iterate"
+ * routine is called under short life memory context, that is resetted
+ * after any call.
+ */
+struct _ForeachAIterator
+{
+ bool (*iterate) (ForeachAIterator *self,
+ Datum *value,
+ bool *isnull,
+ Oid *typid,
+ int32 *typmod);
+ /* Private fields might appear beyond this point... */
+};
+
+typedef ForeachAIterator * (*CreateForeachAIterator) (Datum value,
+ Oid typid,
+ int32 typmod,
+ int slice,
+ Oid target_typid,
+ int32 target_typmod);
+
/* Struct returned by the SQL-visible subscript handler function */
typedef struct SubscriptRoutines
{
@@ -163,6 +189,9 @@ typedef struct SubscriptRoutines
bool fetch_leakproof; /* is fetch SubscriptingRef leakproof? */
bool store_leakproof; /* is assignment SubscriptingRef
* leakproof? */
+
+ /* returns iterator used by PL/pgSQL FOREACH statement */
+ CreateForeachAIterator create_foreach_a_iterator;
} SubscriptRoutines;
#endif /* SUBSCRIPTING_H */
index 301f21dac2f..08bfe59ede4 100644
--- a/src/include/nodes/subscripting.h
+++ b/src/include/nodes/subscripting.h
@@ -154,6 +154,32 @@ typedef void (*SubscriptExecSetup) (const SubscriptingRef *sbsref,
SubscriptingRefState *sbsrefstate,
SubscriptExecSteps *methods);
+typedef struct _ForeachAIterator ForeachAIterator;
+
+/*
+ * ForeachAIiterator is used by PLpgSQL FOREACH IN ARRAY statement.
+ * Input value should not be null, and inside CreateForeachAIterator
+ * routine must be copied to current (statement) context. "iterate"
+ * routine is called under short life memory context, that is resetted
+ * after any call.
+ */
+struct _ForeachAIterator
+{
+ bool (*iterate) (ForeachAIterator *self,
+ Datum *value,
+ bool *isnull,
+ Oid *typid,
+ int32 *typmod);
+ /* Private fields might appear beyond this point... */
+};
+
+typedef ForeachAIterator * (*CreateForeachAIterator) (Datum value,
+ Oid typid,
+ int32 typmod,
+ int slice,
+ Oid target_typid,
+ int32 target_typmod);
+
/* Struct returned by the SQL-visible subscript handler function */
typedef struct SubscriptRoutines
{
@@ -163,6 +189,9 @@ typedef struct SubscriptRoutines
bool fetch_leakproof; /* is fetch SubscriptingRef leakproof? */
bool store_leakproof; /* is assignment SubscriptingRef
* leakproof? */
+
+ /* returns iterator used by PL/pgSQL FOREACH statement */
+ CreateForeachAIterator create_foreach_a_iterator;
} SubscriptRoutines;
#endif /* SUBSCRIPTING_H */
Regards
Pavel
RegardsPavel
regards, tom lane