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

Commit 5d723d0

Browse files
committed
Prepared queries for PLPerl, plus fixing a small plperl memory leak. Patch and docs from Dmitry Karasik, slightly editorialised.
1 parent f2f5b05 commit 5d723d0

File tree

6 files changed

+724
-20
lines changed

6 files changed

+724
-20
lines changed

doc/src/sgml/plperl.sgml

+68-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.50 2006/03/01 06:30:32 neilc Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.51 2006/03/05 16:40:51 adunstan Exp $
33
-->
44

55
<chapter id="plperl">
@@ -296,7 +296,7 @@ BEGIN { strict->import(); }
296296
</para>
297297

298298
<para>
299-
PL/Perl provides three additional Perl commands:
299+
PL/Perl provides additional Perl commands:
300300

301301
<variablelist>
302302
<varlistentry>
@@ -306,9 +306,13 @@ BEGIN { strict->import(); }
306306
</indexterm>
307307

308308
<term><literal><function>spi_exec_query</>(<replaceable>query</replaceable> [, <replaceable>max-rows</replaceable>])</literal></term>
309-
<term><literal><function>spi_exec_query</>(<replaceable>command</replaceable>)</literal></term>
310309
<term><literal><function>spi_query</>(<replaceable>command</replaceable>)</literal></term>
311-
<term><literal><function>spi_fetchrow</>(<replaceable>command</replaceable>)</literal></term>
310+
<term><literal><function>spi_fetchrow</>(<replaceable>cursor</replaceable>)</literal></term>
311+
<term><literal><function>spi_prepare</>(<replaceable>command</replaceable>, <replaceable>argument types</replaceable>)</literal></term>
312+
<term><literal><function>spi_exec_prepared</>(<replaceable>plan</replaceable>)</literal></term>
313+
<term><literal><function>spi_query_prepared</>(<replaceable>plan</replaceable> [, <replaceable>attributes</replaceable>], <replaceable>arguments</replaceable>)</literal></term>
314+
<term><literal><function>spi_cursor_close</>(<replaceable>cursor</replaceable>)</literal></term>
315+
<term><literal><function>spi_freeplan</>(<replaceable>plan</replaceable>)</literal></term>
312316

313317
<listitem>
314318
<para>
@@ -419,6 +423,66 @@ $$ LANGUAGE plperlu;
419423
SELECT * from lotsa_md5(500);
420424
</programlisting>
421425
</para>
426+
427+
<para>
428+
<literal>spi_prepare</literal>, <literal>spi_query_prepared</literal>, <literal>spi_exec_prepared</literal>,
429+
and <literal>spi_freeplan</literal> implement the same functionality but for prepared queries. Once
430+
a query plan is prepared by a call to <literal>spi_prepare</literal>, the plan can be used instead
431+
of the string query, either in <literal>spi_exec_prepared</literal>, where the result is the same as returned
432+
by <literal>spi_exec_query</literal>, or in <literal>spi_query_prepared</literal> which returns a cursor
433+
exactly as <literal>spi_query</literal> does, which can be later passed to <literal>spi_fetchrow</literal>.
434+
</para>
435+
436+
<para>
437+
The advantage of prepared queries is that is it possible to use one prepared plan for more
438+
than one query execution. After the plan is not needed anymore, it must be freed with
439+
<literal>spi_freeplan</literal>:
440+
</para>
441+
442+
<para>
443+
<programlisting>
444+
CREATE OR REPLACE FUNCTION init() RETURNS INTEGER AS $$
445+
$_SHARED{my_plan} = spi_prepare( 'SELECT (now() + $1)::date AS now', 'INTERVAL');
446+
$$ LANGUAGE plperl;
447+
448+
CREATE OR REPLACE FUNCTION add_time( INTERVAL ) RETURNS TEXT AS $$
449+
return spi_exec_prepared(
450+
$_SHARED{my_plan},
451+
$_[0],
452+
)->{rows}->[0]->{now};
453+
$$ LANGUAGE plperl;
454+
455+
CREATE OR REPLACE FUNCTION done() RETURNS INTEGER AS $$
456+
spi_freeplan( $_SHARED{my_plan});
457+
undef $_SHARED{my_plan};
458+
$$ LANGUAGE plperl;
459+
460+
SELECT init();
461+
SELECT add_time('1 day'), add_time('2 days'), add_time('3 days');
462+
SELECT done();
463+
464+
add_time | add_time | add_time
465+
------------+------------+------------
466+
2005-12-10 | 2005-12-11 | 2005-12-12
467+
</programlisting>
468+
</para>
469+
470+
<para>
471+
Note that the parameter subscript in <literal>spi_prepare</literal> is defined via
472+
$1, $2, $3, etc, so avoid declaring query strings in double quotes that might easily
473+
lead to hard-to-catch bugs.
474+
</para>
475+
476+
<para>
477+
<literal>spi_cursor_close</literal> can be used to abort sequence of
478+
<literal>spi_fetchrow</literal> calls. Normally, the call to
479+
<literal>spi_fetchrow</literal> that returns <literal>undef</literal> is
480+
the signal that there are no more rows to read. Also
481+
that call automatically frees the cursor associated with the query. If it is desired not
482+
to read all retuned rows, <literal>spi_cursor_close</literal> must be
483+
called to avoid memory leaks.
484+
</para>
485+
422486

423487
</listitem>
424488
</varlistentry>

src/pl/plperl/SPI.xs

+81-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ spi_spi_exec_query(query, ...)
111111
int limit = 0;
112112
CODE:
113113
if (items > 2)
114-
croak("Usage: spi_exec_query(query, limit) or spi_exec_query(query)");
114+
croak("Usage: spi_exec_query(query, limit) "
115+
"or spi_exec_query(query)");
115116
if (items == 2)
116117
limit = SvIV(ST(1));
117118
ret_hash = plperl_spi_exec(query, limit);
@@ -141,5 +142,84 @@ spi_spi_fetchrow(cursor)
141142
OUTPUT:
142143
RETVAL
143144

145+
SV*
146+
spi_spi_prepare(query, ...)
147+
char* query;
148+
CODE:
149+
int i;
150+
SV** argv;
151+
if (items < 1)
152+
Perl_croak(aTHX_ "Usage: spi_prepare(query, ...)");
153+
argv = ( SV**) palloc(( items - 1) * sizeof(SV*));
154+
if ( argv == NULL)
155+
Perl_croak(aTHX_ "spi_prepare: not enough memory");
156+
for ( i = 1; i < items; i++)
157+
argv[i - 1] = ST(i);
158+
RETVAL = plperl_spi_prepare(query, items - 1, argv);
159+
pfree( argv);
160+
OUTPUT:
161+
RETVAL
162+
163+
SV*
164+
spi_spi_exec_prepared(query, ...)
165+
char * query;
166+
PREINIT:
167+
HV *ret_hash;
168+
CODE:
169+
HV *attr = NULL;
170+
int i, offset = 1, argc;
171+
SV ** argv;
172+
if ( items < 1)
173+
Perl_croak(aTHX_ "Usage: spi_exec_prepared(query, [\\%%attr,] "
174+
"[\\@bind_values])");
175+
if ( items > 1 && SvROK( ST( 1)) && SvTYPE( SvRV( ST( 1))) == SVt_PVHV)
176+
{
177+
attr = ( HV*) SvRV(ST(1));
178+
offset++;
179+
}
180+
argc = items - offset;
181+
argv = ( SV**) palloc( argc * sizeof(SV*));
182+
if ( argv == NULL)
183+
Perl_croak(aTHX_ "spi_exec_prepared: not enough memory");
184+
for ( i = 0; offset < items; offset++, i++)
185+
argv[i] = ST(offset);
186+
ret_hash = plperl_spi_exec_prepared(query, attr, argc, argv);
187+
RETVAL = newRV_noinc((SV*)ret_hash);
188+
pfree( argv);
189+
OUTPUT:
190+
RETVAL
191+
192+
SV*
193+
spi_spi_query_prepared(query, ...)
194+
char * query;
195+
CODE:
196+
int i;
197+
SV ** argv;
198+
if ( items < 1)
199+
Perl_croak(aTHX_ "Usage: spi_query_prepared(query, "
200+
"[\\@bind_values])");
201+
argv = ( SV**) palloc(( items - 1) * sizeof(SV*));
202+
if ( argv == NULL)
203+
Perl_croak(aTHX_ "spi_query_prepared: not enough memory");
204+
for ( i = 1; i < items; i++)
205+
argv[i - 1] = ST(i);
206+
RETVAL = plperl_spi_query_prepared(query, items - 1, argv);
207+
pfree( argv);
208+
OUTPUT:
209+
RETVAL
210+
211+
void
212+
spi_spi_freeplan(query)
213+
char *query;
214+
CODE:
215+
plperl_spi_freeplan(query);
216+
217+
void
218+
spi_spi_cursor_close(cursor)
219+
char *cursor;
220+
CODE:
221+
plperl_spi_cursor_close(cursor);
222+
223+
144224
BOOT:
145225
items = 0; /* avoid 'unused variable' warning */

src/pl/plperl/expected/plperl.out

+48
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,20 @@ SELECT * from perl_spi_func();
367367
2
368368
(2 rows)
369369

370+
--
371+
-- Test spi_fetchrow abort
372+
--
373+
CREATE OR REPLACE FUNCTION perl_spi_func2() RETURNS INTEGER AS $$
374+
my $x = spi_query("select 1 as a union select 2 as a");
375+
spi_cursor_close( $x);
376+
return 0;
377+
$$ LANGUAGE plperl;
378+
SELECT * from perl_spi_func2();
379+
perl_spi_func2
380+
----------------
381+
0
382+
(1 row)
383+
370384
---
371385
--- Test recursion via SPI
372386
---
@@ -420,3 +434,37 @@ SELECT array_of_text();
420434
{{"a\"b",NULL,"c,d"},{"e\\f",NULL,g}}
421435
(1 row)
422436

437+
--
438+
-- Test spi_prepare/spi_exec_prepared/spi_freeplan
439+
--
440+
CREATE OR REPLACE FUNCTION perl_spi_prepared(INTEGER) RETURNS INTEGER AS $$
441+
my $x = spi_prepare('select $1 AS a', 'INT4');
442+
my $q = spi_exec_prepared( $x, $_[0] + 1);
443+
spi_freeplan($x);
444+
return $q->{rows}->[0]->{a};
445+
$$ LANGUAGE plperl;
446+
SELECT * from perl_spi_prepared(42);
447+
perl_spi_prepared
448+
-------------------
449+
43
450+
(1 row)
451+
452+
--
453+
-- Test spi_prepare/spi_query_prepared/spi_freeplan
454+
--
455+
CREATE OR REPLACE FUNCTION perl_spi_prepared_set(INTEGER, INTEGER) RETURNS SETOF INTEGER AS $$
456+
my $x = spi_prepare('SELECT $1 AS a union select $2 as a', 'INT4', 'INT4');
457+
my $q = spi_query_prepared( $x, 1+$_[0], 2+$_[1]);
458+
while (defined (my $y = spi_fetchrow($q))) {
459+
return_next $y->{a};
460+
}
461+
spi_freeplan($x);
462+
return;
463+
$$ LANGUAGE plperl;
464+
SELECT * from perl_spi_prepared_set(1,2);
465+
perl_spi_prepared_set
466+
-----------------------
467+
2
468+
4
469+
(2 rows)
470+

0 commit comments

Comments
 (0)