пн, 17 авг. 2026 г. в 03:24, Andrey Rachitskiy <[email protected]>:
> The first patch tries to close every Perl-to-SQL path that still
> ignored FETCH on tied values.
>
> I have attached v2.
The previous version missed a few GETMAGIC call sites:
get_perl_array_ref(), the trigger return value, $_TD->{new} before
the SvOK() / SvROK() checks, and the start of return_next(). A tied
scalar whose FETCH returns an arrayref or a hashref still looked like
undef.
That also made the SETOF walker cleaner. The loop only walks with
av_len() and calls return_next(). FETCH lives in return_next() and
plperl_sv_to_datum(), so the loop does not run GETMAGIC on each
element itself.
The regress patch covers those cases. tied_scalar_setof and
tied_scalar_int_array wrap an arrayref in a tied scalar. tied_modify
uses tied scalars for $_TD->{new} and for the "MODIFY" return.
--
Regards,
Rachitskiy Andrey
From: Andrey Rachitskiy <[email protected]>
Subject: [PATCH 2/2] Add regress tests for tied Perl hashes and arrays
Inline tie classes without Tie::Hash / Tie::Array. Trusted plperl
cannot require those modules. Shared class setup is only in
plperl_tied, where several functions reuse it. Include tied scalars
that wrap arrayrefs and trigger return / $_TD->{new} values.
diff --git a/contrib/hstore_plperl/Makefile b/contrib/hstore_plperl/Makefile
index 9065f164088..efb0640902c 100644
--- a/contrib/hstore_plperl/Makefile
+++ b/contrib/hstore_plperl/Makefile
@@ -10,7 +10,7 @@ PGFILEDESC = "hstore_plperl - hstore transform for plperl"
EXTENSION = hstore_plperl hstore_plperlu
DATA = hstore_plperl--1.0.sql hstore_plperlu--1.0.sql
-REGRESS = hstore_plperl hstore_plperlu create_transform
+REGRESS = hstore_plperl hstore_plperlu hstore_plperl_tied create_transform
EXTRA_INSTALL = contrib/hstore
ifdef USE_PGXS
diff --git a/contrib/hstore_plperl/expected/hstore_plperl_tied.out b/contrib/hstore_plperl/expected/hstore_plperl_tied.out
new file mode 100644
index 00000000000..2cd85facd5a
--- /dev/null
+++ b/contrib/hstore_plperl/expected/hstore_plperl_tied.out
@@ -0,0 +1,26 @@
+CREATE EXTENSION hstore_plperl CASCADE;
+NOTICE: installing required extension "hstore"
+NOTICE: installing required extension "plperl"
+CREATE FUNCTION tied_hstore() RETURNS hstore
+LANGUAGE plperl TRANSFORM FOR TYPE hstore AS $$
+ {
+ package PLPerlTiedHash;
+ sub TIEHASH { bless {}, $_[0] }
+ sub STORE { $_[0]{$_[1]} = $_[2] }
+ sub FETCH { $_[0]{$_[1]} }
+ sub FIRSTKEY { my $a = keys %{$_[0]}; each %{$_[0]} }
+ sub NEXTKEY { each %{$_[0]} }
+ }
+ my %h;
+ tie %h, 'PLPerlTiedHash';
+ $h{a} = '1'; $h{b} = '2'; $h{c} = undef;
+ return \%h;
+$$;
+SELECT tied_hstore();
+ tied_hstore
+-------------------------------
+ "a"=>"1", "b"=>"2", "c"=>NULL
+(1 row)
+
+DROP FUNCTION tied_hstore();
+DROP EXTENSION hstore_plperl, hstore, plperl;
diff --git a/contrib/hstore_plperl/meson.build b/contrib/hstore_plperl/meson.build
index 705dbe69a46..0f12bdc7101 100644
--- a/contrib/hstore_plperl/meson.build
+++ b/contrib/hstore_plperl/meson.build
@@ -45,6 +45,7 @@ tests += {
'sql': [
'hstore_plperl',
'hstore_plperlu',
+ 'hstore_plperl_tied',
'create_transform',
],
},
diff --git a/contrib/hstore_plperl/sql/hstore_plperl_tied.sql b/contrib/hstore_plperl/sql/hstore_plperl_tied.sql
new file mode 100644
index 00000000000..229b7bbaecf
--- /dev/null
+++ b/contrib/hstore_plperl/sql/hstore_plperl_tied.sql
@@ -0,0 +1,21 @@
+CREATE EXTENSION hstore_plperl CASCADE;
+
+CREATE FUNCTION tied_hstore() RETURNS hstore
+LANGUAGE plperl TRANSFORM FOR TYPE hstore AS $$
+ {
+ package PLPerlTiedHash;
+ sub TIEHASH { bless {}, $_[0] }
+ sub STORE { $_[0]{$_[1]} = $_[2] }
+ sub FETCH { $_[0]{$_[1]} }
+ sub FIRSTKEY { my $a = keys %{$_[0]}; each %{$_[0]} }
+ sub NEXTKEY { each %{$_[0]} }
+ }
+ my %h;
+ tie %h, 'PLPerlTiedHash';
+ $h{a} = '1'; $h{b} = '2'; $h{c} = undef;
+ return \%h;
+$$;
+SELECT tied_hstore();
+
+DROP FUNCTION tied_hstore();
+DROP EXTENSION hstore_plperl, hstore, plperl;
diff --git a/contrib/jsonb_plperl/Makefile b/contrib/jsonb_plperl/Makefile
index ba9480e819b..8f8ea1ff2e7 100644
--- a/contrib/jsonb_plperl/Makefile
+++ b/contrib/jsonb_plperl/Makefile
@@ -11,7 +11,7 @@ PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plperl
EXTENSION = jsonb_plperlu jsonb_plperl
DATA = jsonb_plperlu--1.0.sql jsonb_plperl--1.0.sql
-REGRESS = jsonb_plperl jsonb_plperlu
+REGRESS = jsonb_plperl jsonb_plperlu jsonb_plperl_tied
SHLIB_LINK += $(filter -lm, $(LIBS))
diff --git a/contrib/jsonb_plperl/expected/jsonb_plperl_tied.out b/contrib/jsonb_plperl/expected/jsonb_plperl_tied.out
new file mode 100644
index 00000000000..74acace939c
--- /dev/null
+++ b/contrib/jsonb_plperl/expected/jsonb_plperl_tied.out
@@ -0,0 +1,45 @@
+CREATE EXTENSION jsonb_plperl CASCADE;
+NOTICE: installing required extension "plperl"
+CREATE FUNCTION tied_jsonb_hash() RETURNS jsonb
+LANGUAGE plperl TRANSFORM FOR TYPE jsonb AS $$
+ {
+ package PLPerlTiedHash;
+ sub TIEHASH { bless {}, $_[0] }
+ sub STORE { $_[0]{$_[1]} = $_[2] }
+ sub FETCH { $_[0]{$_[1]} }
+ sub FIRSTKEY { my $a = keys %{$_[0]}; each %{$_[0]} }
+ sub NEXTKEY { each %{$_[0]} }
+ }
+ my %h;
+ tie %h, 'PLPerlTiedHash';
+ $h{a} = 1; $h{b} = 'boo'; $h{c} = undef;
+ return \%h;
+$$;
+SELECT tied_jsonb_hash();
+ tied_jsonb_hash
+---------------------------------
+ {"a": 1, "b": "boo", "c": null}
+(1 row)
+
+CREATE FUNCTION tied_jsonb_array() RETURNS jsonb
+LANGUAGE plperl TRANSFORM FOR TYPE jsonb AS $$
+ {
+ package PLPerlTiedArray;
+ sub TIEARRAY { bless [], $_[0] }
+ sub STORE { $_[0][$_[1]] = $_[2] }
+ sub FETCH { $_[0][$_[1]] }
+ sub FETCHSIZE { scalar @{$_[0]} }
+ }
+ my @a;
+ tie @a, 'PLPerlTiedArray';
+ $a[0] = 1; $a[1] = 'boo';
+ return \@a;
+$$;
+SELECT tied_jsonb_array();
+ tied_jsonb_array
+------------------
+ [1, "boo"]
+(1 row)
+
+DROP FUNCTION tied_jsonb_hash(), tied_jsonb_array();
+DROP EXTENSION jsonb_plperl, plperl;
diff --git a/contrib/jsonb_plperl/meson.build b/contrib/jsonb_plperl/meson.build
index 8bfabee5455..27654ce8d55 100644
--- a/contrib/jsonb_plperl/meson.build
+++ b/contrib/jsonb_plperl/meson.build
@@ -46,6 +46,7 @@ tests += {
'sql': [
'jsonb_plperl',
'jsonb_plperlu',
+ 'jsonb_plperl_tied',
],
},
}
diff --git a/contrib/jsonb_plperl/sql/jsonb_plperl_tied.sql b/contrib/jsonb_plperl/sql/jsonb_plperl_tied.sql
new file mode 100644
index 00000000000..e687547d682
--- /dev/null
+++ b/contrib/jsonb_plperl/sql/jsonb_plperl_tied.sql
@@ -0,0 +1,37 @@
+CREATE EXTENSION jsonb_plperl CASCADE;
+
+CREATE FUNCTION tied_jsonb_hash() RETURNS jsonb
+LANGUAGE plperl TRANSFORM FOR TYPE jsonb AS $$
+ {
+ package PLPerlTiedHash;
+ sub TIEHASH { bless {}, $_[0] }
+ sub STORE { $_[0]{$_[1]} = $_[2] }
+ sub FETCH { $_[0]{$_[1]} }
+ sub FIRSTKEY { my $a = keys %{$_[0]}; each %{$_[0]} }
+ sub NEXTKEY { each %{$_[0]} }
+ }
+ my %h;
+ tie %h, 'PLPerlTiedHash';
+ $h{a} = 1; $h{b} = 'boo'; $h{c} = undef;
+ return \%h;
+$$;
+SELECT tied_jsonb_hash();
+
+CREATE FUNCTION tied_jsonb_array() RETURNS jsonb
+LANGUAGE plperl TRANSFORM FOR TYPE jsonb AS $$
+ {
+ package PLPerlTiedArray;
+ sub TIEARRAY { bless [], $_[0] }
+ sub STORE { $_[0][$_[1]] = $_[2] }
+ sub FETCH { $_[0][$_[1]] }
+ sub FETCHSIZE { scalar @{$_[0]} }
+ }
+ my @a;
+ tie @a, 'PLPerlTiedArray';
+ $a[0] = 1; $a[1] = 'boo';
+ return \@a;
+$$;
+SELECT tied_jsonb_array();
+
+DROP FUNCTION tied_jsonb_hash(), tied_jsonb_array();
+DROP EXTENSION jsonb_plperl, plperl;
diff --git a/src/pl/plperl/GNUmakefile b/src/pl/plperl/GNUmakefile
index d7c8917f822..85729434853 100644
--- a/src/pl/plperl/GNUmakefile
+++ b/src/pl/plperl/GNUmakefile
@@ -63,7 +63,7 @@ endif
REGRESS_OPTS = --dbname=$(PL_TESTDB) --dlpath=$(top_builddir)/src/test/regress
REGRESS = plperl_setup plperl plperl_lc plperl_trigger plperl_shared \
plperl_elog plperl_unicode plperl_util plperl_init plperlu plperl_array \
- plperl_call plperl_transaction plperl_env
+ plperl_call plperl_transaction plperl_env plperl_tied
# if Perl can support two interpreters in one backend,
# test plperl-and-plperlu cases
ifneq ($(PERL),)
diff --git a/src/pl/plperl/expected/plperl_tied.out b/src/pl/plperl/expected/plperl_tied.out
new file mode 100644
index 00000000000..3d09085b955
--- /dev/null
+++ b/src/pl/plperl/expected/plperl_tied.out
@@ -0,0 +1,137 @@
+-- Tied Perl values via inline classes (no Tie::Hash / Tie::Array).
+-- Trusted plperl cannot require those modules.
+DO $$
+{
+ package PLPerlTiedHash;
+ sub TIEHASH { bless {}, $_[0] }
+ sub STORE { $_[0]{$_[1]} = $_[2] }
+ sub FETCH { $_[0]{$_[1]} }
+ sub FIRSTKEY { my $a = keys %{$_[0]}; each %{$_[0]} }
+ sub NEXTKEY { each %{$_[0]} }
+
+ package PLPerlTiedArray;
+ sub TIEARRAY { bless [], $_[0] }
+ sub STORE { $_[0][$_[1]] = $_[2] }
+ sub FETCH { $_[0][$_[1]] }
+ sub FETCHSIZE { scalar @{$_[0]} }
+
+ package PLPerlTiedScalar;
+ sub TIESCALAR { my $s; bless \$s, $_[0] }
+ sub STORE { ${$_[0]} = $_[1] }
+ sub FETCH { ${$_[0]} }
+}
+$$ LANGUAGE plperl;
+CREATE FUNCTION tied_setof() RETURNS SETOF text AS $$
+ my @a;
+ tie @a, 'PLPerlTiedArray';
+ $a[0] = 'a'; $a[1] = 'b'; $a[2] = 'c';
+ return \@a;
+$$ LANGUAGE plperl;
+SELECT * FROM tied_setof();
+ tied_setof
+------------
+ a
+ b
+ c
+(3 rows)
+
+CREATE FUNCTION tied_scalar() RETURNS text AS $$
+ my $s;
+ tie $s, 'PLPerlTiedScalar';
+ $s = 'hello';
+ return $s;
+$$ LANGUAGE plperl;
+SELECT tied_scalar();
+ tied_scalar
+-------------
+ hello
+(1 row)
+
+CREATE TYPE tiedrowperl AS (f1 integer, f2 text, f3 text);
+CREATE FUNCTION tied_composite() RETURNS tiedrowperl AS $$
+ my %h;
+ tie %h, 'PLPerlTiedHash';
+ $h{f1} = 1; $h{f2} = 'hello'; $h{f3} = 'world';
+ return \%h;
+$$ LANGUAGE plperl;
+SELECT tied_composite();
+ tied_composite
+-----------------
+ (1,hello,world)
+(1 row)
+
+CREATE FUNCTION tied_int_array() RETURNS integer[] AS $$
+ my @a;
+ tie @a, 'PLPerlTiedArray';
+ $a[0] = 1; $a[1] = 2; $a[2] = 3;
+ return \@a;
+$$ LANGUAGE plperl;
+SELECT tied_int_array();
+ tied_int_array
+----------------
+ {1,2,3}
+(1 row)
+
+-- Tied scalar whose FETCH returns an arrayref.
+CREATE FUNCTION tied_scalar_setof() RETURNS SETOF text AS $$
+ my @a;
+ tie @a, 'PLPerlTiedArray';
+ $a[0] = 'x'; $a[1] = 'y';
+ my $s;
+ tie $s, 'PLPerlTiedScalar';
+ $s = \@a;
+ return $s;
+$$ LANGUAGE plperl;
+SELECT * FROM tied_scalar_setof();
+ tied_scalar_setof
+-------------------
+ x
+ y
+(2 rows)
+
+CREATE FUNCTION tied_scalar_int_array() RETURNS integer[] AS $$
+ my @a;
+ tie @a, 'PLPerlTiedArray';
+ $a[0] = 4; $a[1] = 5;
+ my $s;
+ tie $s, 'PLPerlTiedScalar';
+ $s = \@a;
+ return $s;
+$$ LANGUAGE plperl;
+SELECT tied_scalar_int_array();
+ tied_scalar_int_array
+-----------------------
+ {4,5}
+(1 row)
+
+-- Artificial: $_TD->{new} and the trigger return value are tied scalars.
+CREATE TABLE tied_trigger_test (i int, v text);
+CREATE FUNCTION tied_modify() RETURNS trigger AS $$
+ my %h;
+ tie %h, 'PLPerlTiedHash';
+ $h{i} = $_TD->{new}{i};
+ $h{v} = 'from_tie';
+ my $new;
+ tie $new, 'PLPerlTiedScalar';
+ $new = \%h;
+ $_TD->{new} = $new;
+ my $ret;
+ tie $ret, 'PLPerlTiedScalar';
+ $ret = 'MODIFY';
+ return $ret;
+$$ LANGUAGE plperl;
+CREATE TRIGGER tied_modify_trig BEFORE INSERT ON tied_trigger_test
+FOR EACH ROW EXECUTE PROCEDURE tied_modify();
+INSERT INTO tied_trigger_test (i, v) VALUES (1, 'orig');
+SELECT i, v FROM tied_trigger_test;
+ i | v
+---+----------
+ 1 | from_tie
+(1 row)
+
+DROP TRIGGER tied_modify_trig ON tied_trigger_test;
+DROP FUNCTION tied_modify(), tied_setof(), tied_scalar(),
+ tied_composite(), tied_int_array(),
+ tied_scalar_setof(), tied_scalar_int_array();
+DROP TABLE tied_trigger_test;
+DROP TYPE tiedrowperl;
diff --git a/src/pl/plperl/meson.build b/src/pl/plperl/meson.build
index ff41812ca46..5c055f4f662 100644
--- a/src/pl/plperl/meson.build
+++ b/src/pl/plperl/meson.build
@@ -96,6 +96,7 @@ tests += {
'plperl_call',
'plperl_transaction',
'plperl_env',
+ 'plperl_tied',
],
'regress_args': ['--dlpath', meson.project_build_root() / 'src/test/regress'],
},
diff --git a/src/pl/plperl/sql/plperl_tied.sql b/src/pl/plperl/sql/plperl_tied.sql
new file mode 100644
index 00000000000..7701a53839b
--- /dev/null
+++ b/src/pl/plperl/sql/plperl_tied.sql
@@ -0,0 +1,108 @@
+-- Tied Perl values via inline classes (no Tie::Hash / Tie::Array).
+-- Trusted plperl cannot require those modules.
+
+DO $$
+{
+ package PLPerlTiedHash;
+ sub TIEHASH { bless {}, $_[0] }
+ sub STORE { $_[0]{$_[1]} = $_[2] }
+ sub FETCH { $_[0]{$_[1]} }
+ sub FIRSTKEY { my $a = keys %{$_[0]}; each %{$_[0]} }
+ sub NEXTKEY { each %{$_[0]} }
+
+ package PLPerlTiedArray;
+ sub TIEARRAY { bless [], $_[0] }
+ sub STORE { $_[0][$_[1]] = $_[2] }
+ sub FETCH { $_[0][$_[1]] }
+ sub FETCHSIZE { scalar @{$_[0]} }
+
+ package PLPerlTiedScalar;
+ sub TIESCALAR { my $s; bless \$s, $_[0] }
+ sub STORE { ${$_[0]} = $_[1] }
+ sub FETCH { ${$_[0]} }
+}
+$$ LANGUAGE plperl;
+
+CREATE FUNCTION tied_setof() RETURNS SETOF text AS $$
+ my @a;
+ tie @a, 'PLPerlTiedArray';
+ $a[0] = 'a'; $a[1] = 'b'; $a[2] = 'c';
+ return \@a;
+$$ LANGUAGE plperl;
+SELECT * FROM tied_setof();
+
+CREATE FUNCTION tied_scalar() RETURNS text AS $$
+ my $s;
+ tie $s, 'PLPerlTiedScalar';
+ $s = 'hello';
+ return $s;
+$$ LANGUAGE plperl;
+SELECT tied_scalar();
+
+CREATE TYPE tiedrowperl AS (f1 integer, f2 text, f3 text);
+CREATE FUNCTION tied_composite() RETURNS tiedrowperl AS $$
+ my %h;
+ tie %h, 'PLPerlTiedHash';
+ $h{f1} = 1; $h{f2} = 'hello'; $h{f3} = 'world';
+ return \%h;
+$$ LANGUAGE plperl;
+SELECT tied_composite();
+
+CREATE FUNCTION tied_int_array() RETURNS integer[] AS $$
+ my @a;
+ tie @a, 'PLPerlTiedArray';
+ $a[0] = 1; $a[1] = 2; $a[2] = 3;
+ return \@a;
+$$ LANGUAGE plperl;
+SELECT tied_int_array();
+
+-- Tied scalar whose FETCH returns an arrayref.
+CREATE FUNCTION tied_scalar_setof() RETURNS SETOF text AS $$
+ my @a;
+ tie @a, 'PLPerlTiedArray';
+ $a[0] = 'x'; $a[1] = 'y';
+ my $s;
+ tie $s, 'PLPerlTiedScalar';
+ $s = \@a;
+ return $s;
+$$ LANGUAGE plperl;
+SELECT * FROM tied_scalar_setof();
+
+CREATE FUNCTION tied_scalar_int_array() RETURNS integer[] AS $$
+ my @a;
+ tie @a, 'PLPerlTiedArray';
+ $a[0] = 4; $a[1] = 5;
+ my $s;
+ tie $s, 'PLPerlTiedScalar';
+ $s = \@a;
+ return $s;
+$$ LANGUAGE plperl;
+SELECT tied_scalar_int_array();
+
+-- Artificial: $_TD->{new} and the trigger return value are tied scalars.
+CREATE TABLE tied_trigger_test (i int, v text);
+CREATE FUNCTION tied_modify() RETURNS trigger AS $$
+ my %h;
+ tie %h, 'PLPerlTiedHash';
+ $h{i} = $_TD->{new}{i};
+ $h{v} = 'from_tie';
+ my $new;
+ tie $new, 'PLPerlTiedScalar';
+ $new = \%h;
+ $_TD->{new} = $new;
+ my $ret;
+ tie $ret, 'PLPerlTiedScalar';
+ $ret = 'MODIFY';
+ return $ret;
+$$ LANGUAGE plperl;
+CREATE TRIGGER tied_modify_trig BEFORE INSERT ON tied_trigger_test
+FOR EACH ROW EXECUTE PROCEDURE tied_modify();
+INSERT INTO tied_trigger_test (i, v) VALUES (1, 'orig');
+SELECT i, v FROM tied_trigger_test;
+
+DROP TRIGGER tied_modify_trig ON tied_trigger_test;
+DROP FUNCTION tied_modify(), tied_setof(), tied_scalar(),
+ tied_composite(), tied_int_array(),
+ tied_scalar_setof(), tied_scalar_int_array();
+DROP TABLE tied_trigger_test;
+DROP TYPE tiedrowperl;
From: Andrey Rachitskiy <[email protected]>
Subject: [PATCH 1/2] Honor Perl FETCH for tied hashes and arrays
Tied hashes leave HeVAL() unset after hv_iternext(). Call hv_iterval()
and SvGETMAGIC() so FETCH actually runs. For SETOF arrays, walk with
av_len() and run GETMAGIC via return_next / plperl_sv_to_datum.
Apply the same pattern in get_perl_array_ref(), plperl_sv_to_datum(),
jsonb_plperl, composite and trigger hash walkers, and the remaining
SvOK() checks on trigger / return_next entry points.
diff --git a/contrib/hstore_plperl/hstore_plperl.c b/contrib/hstore_plperl/hstore_plperl.c
index d7f1b8ddb48..ad205c8bcfc 100644
--- a/contrib/hstore_plperl/hstore_plperl.c
+++ b/contrib/hstore_plperl/hstore_plperl.c
@@ -138,7 +138,14 @@ plperl_to_hstore(PG_FUNCTION_ARGS)
while ((he = hv_iternext(hv)))
{
char *key = sv2cstr(HeSVKEY_force(he));
- SV *value = HeVAL(he);
+ SV *value;
+
+ /*
+ * Tied hashes leave HeVAL() unset. hv_iterval() plus
+ * SvGETMAGIC() runs FETCH.
+ */
+ value = hv_iterval(hv, he);
+ SvGETMAGIC(value);
if (i >= pcount)
{
diff --git a/contrib/jsonb_plperl/jsonb_plperl.c b/contrib/jsonb_plperl/jsonb_plperl.c
index 97d147cc65a..097c43cfb08 100644
--- a/contrib/jsonb_plperl/jsonb_plperl.c
+++ b/contrib/jsonb_plperl/jsonb_plperl.c
@@ -186,6 +186,9 @@ SV_to_JsonbValue(SV *in, JsonbInState *jsonb_state, bool is_elem)
/* this can recurse via AV_to_JsonbValue() or HV_to_JsonbValue() */
check_stack_depth();
+ /* Tied values leave SvOK() false until FETCH. */
+ SvGETMAGIC(in);
+
/* Dereference references recursively. */
while (SvROK(in))
{
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index eba91f2d7d6..874e7e310b6 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1092,11 +1092,19 @@ plperl_build_tuple_result(HV *perlhash, TupleDesc td)
hv_iterinit(perlhash);
while ((he = hv_iternext(perlhash)))
{
- SV *val = HeVAL(he);
- char *key = hek2cstr(he);
- int attn = SPI_fnumber(td, key);
+ char *key;
+ SV *val;
+ int attn;
Form_pg_attribute attr;
+ /*
+ * Tied hashes leave HeVAL() unset. hek2cstr() uses FREETMPS, so
+ * it must run before hv_iterval().
+ */
+ key = hek2cstr(he);
+ val = hv_iterval(perlhash, he);
+ attn = SPI_fnumber(td, key);
+
if (attn == SPI_ERROR_NOATTRIBUTE)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
@@ -1145,6 +1153,12 @@ get_perl_array_ref(SV *sv)
{
dTHX;
+ if (!sv)
+ return NULL;
+
+ /* Tied scalars leave SvOK()/SvROK() false until FETCH. */
+ SvGETMAGIC(sv);
+
if (SvOK(sv) && SvROK(sv))
{
if (SvTYPE(SvRV(sv)) == SVt_PVAV)
@@ -1154,9 +1168,13 @@ get_perl_array_ref(SV *sv)
HV *hv = (HV *) SvRV(sv);
SV **sav = hv_fetch_string(hv, "array");
- if (sav && *sav && SvOK(*sav) && SvROK(*sav) &&
- SvTYPE(SvRV(*sav)) == SVt_PVAV)
- return *sav;
+ if (sav && *sav)
+ {
+ SvGETMAGIC(*sav);
+ if (SvOK(*sav) && SvROK(*sav) &&
+ SvTYPE(SvRV(*sav)) == SVt_PVAV)
+ return *sav;
+ }
elog(ERROR, "could not get array reference from PostgreSQL::InServer::ARRAY object");
}
@@ -1348,6 +1366,12 @@ plperl_sv_to_datum(SV *sv, Oid typid, int32 typmod,
* VOID. In the latter case, we should pay no attention to the last Perl
* statement's result, and this is a convenient means to ensure that.
*/
+ if (sv)
+ {
+ dTHX;
+ SvGETMAGIC(sv);
+ }
+
if (!sv || !SvOK(sv) || typid == VOIDOID)
{
/* look up type info if they did not pass it */
@@ -1787,6 +1811,7 @@ plperl_modify_tuple(HV *hvTD, TriggerData *tdata, HeapTuple otup)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("$_TD->{new} does not exist")));
+ SvGETMAGIC(*svp);
if (!SvOK(*svp) || !SvROK(*svp) || SvTYPE(SvRV(*svp)) != SVt_PVHV)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -1803,11 +1828,16 @@ plperl_modify_tuple(HV *hvTD, TriggerData *tdata, HeapTuple otup)
hv_iterinit(hvNew);
while ((he = hv_iternext(hvNew)))
{
- char *key = hek2cstr(he);
- SV *val = HeVAL(he);
- int attn = SPI_fnumber(tupdesc, key);
+ char *key;
+ SV *val;
+ int attn;
Form_pg_attribute attr;
+ /* hek2cstr() uses FREETMPS, so it must run before hv_iterval(). */
+ key = hek2cstr(he);
+ val = hv_iterval(hvNew, he);
+ attn = SPI_fnumber(tupdesc, key);
+
if (attn == SPI_ERROR_NOATTRIBUTE)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
@@ -2478,14 +2508,15 @@ plperl_func_handler(PG_FUNCTION_ARGS)
if (sav)
{
dTHX;
- int i = 0;
- SV **svp = 0;
AV *rav = (AV *) SvRV(sav);
+ int alen = av_len(rav) + 1;
- while ((svp = av_fetch(rav, i, FALSE)) != NULL)
+ for (int i = 0; i < alen; i++)
{
- plperl_return_next_internal(*svp);
- i++;
+ SV **svp = av_fetch(rav, i, FALSE);
+
+ if (svp)
+ plperl_return_next_internal(*svp);
}
}
else if (SvOK(perlret))
@@ -2573,6 +2604,13 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish() failed");
+ if (perlret)
+ {
+ dTHX;
+
+ SvGETMAGIC(perlret);
+ }
+
if (perlret == NULL || !SvOK(perlret))
{
/* undef result means go ahead with original tuple */
@@ -3293,6 +3331,13 @@ plperl_return_next_internal(SV *sv)
if (!sv)
return;
+ {
+ dTHX;
+
+ /* Tied scalars leave SvOK()/SvROK() false until FETCH. */
+ SvGETMAGIC(sv);
+ }
+
prodesc = current_call_data->prodesc;
fcinfo = current_call_data->fcinfo;
rsi = (ReturnSetInfo *) fcinfo->resultinfo;