summaryrefslogtreecommitdiff
path: root/src/pl
diff options
context:
space:
mode:
authorPavan Deolasee2017-06-14 05:42:18 +0000
committerPavan Deolasee2017-06-14 05:42:18 +0000
commit15dd5274c323fb93e4e3ea9ad2185aaaec10f79c (patch)
tree9dafb4c7f735d9429ea461dc792933af87493c33 /src/pl
parentdfbb88e3bbb526dcb204b456b9e5cfd9d10d0d0a (diff)
parentd5cb3bab564e0927ffac7c8729eacf181a12dd40 (diff)
Merge from PG master upto d5cb3bab564e0927ffac7c8729eacf181a12dd40
This is the result of the "git merge remotes/PGSQL/master" upto the said commit point. We have done some basic analysis, fixed compilation problems etc, but bulk of the logical problems in conflict resolution etc will be handled by subsequent commits.
Diffstat (limited to 'src/pl')
-rw-r--r--src/pl/plperl/Util.xs10
-rw-r--r--src/pl/plperl/expected/plperl_trigger.out29
-rw-r--r--src/pl/plperl/plc_perlboot.pl7
-rw-r--r--src/pl/plperl/plc_trusted.pl3
-rw-r--r--src/pl/plperl/plperl.c406
-rw-r--r--src/pl/plperl/plperl.h2
-rw-r--r--src/pl/plperl/po/de.po104
-rw-r--r--src/pl/plperl/po/fr.po82
-rw-r--r--src/pl/plperl/po/ko.po107
-rw-r--r--src/pl/plperl/po/pl.po106
-rw-r--r--src/pl/plperl/po/pt_BR.po95
-rw-r--r--src/pl/plperl/po/ru.po115
-rw-r--r--src/pl/plperl/ppport.h12
-rw-r--r--src/pl/plperl/sql/plperl_trigger.sql32
-rw-r--r--src/pl/plperl/text2macro.pl8
-rw-r--r--src/pl/plpgsql/src/Makefile2
-rw-r--r--src/pl/plpgsql/src/generate-plerrcodes.pl4
-rw-r--r--src/pl/plpgsql/src/pl_comp.c45
-rw-r--r--src/pl/plpgsql/src/pl_exec.c727
-rw-r--r--src/pl/plpgsql/src/pl_funcs.c18
-rw-r--r--src/pl/plpgsql/src/pl_gram.y45
-rw-r--r--src/pl/plpgsql/src/pl_handler.c11
-rw-r--r--src/pl/plpgsql/src/pl_scanner.c6
-rw-r--r--src/pl/plpgsql/src/plpgsql.h550
-rw-r--r--src/pl/plpgsql/src/po/fr.po316
-rw-r--r--src/pl/plpgsql/src/po/ko.po399
-rw-r--r--src/pl/plpgsql/src/po/pt_BR.po369
-rw-r--r--src/pl/plpgsql/src/po/ru.po363
-rw-r--r--src/pl/plpython/Makefile4
-rw-r--r--src/pl/plpython/expected/plpython_composite.out29
-rw-r--r--src/pl/plpython/expected/plpython_ereport.out28
-rw-r--r--src/pl/plpython/expected/plpython_setof.out7
-rw-r--r--src/pl/plpython/expected/plpython_spi.out23
-rw-r--r--src/pl/plpython/expected/plpython_test.out29
-rw-r--r--src/pl/plpython/expected/plpython_trigger.out21
-rw-r--r--src/pl/plpython/expected/plpython_types.out129
-rw-r--r--src/pl/plpython/expected/plpython_types_3.out129
-rw-r--r--src/pl/plpython/generate-spiexceptions.pl4
-rw-r--r--src/pl/plpython/plpy_cursorobject.c16
-rw-r--r--src/pl/plpython/plpy_cursorobject.h1
-rw-r--r--src/pl/plpython/plpy_elog.c2
-rw-r--r--src/pl/plpython/plpy_exec.c96
-rw-r--r--src/pl/plpython/plpy_main.c8
-rw-r--r--src/pl/plpython/plpy_planobject.c34
-rw-r--r--src/pl/plpython/plpy_plpymodule.c89
-rw-r--r--src/pl/plpython/plpy_procedure.c6
-rw-r--r--src/pl/plpython/plpy_spi.c29
-rw-r--r--src/pl/plpython/plpy_spi.h1
-rw-r--r--src/pl/plpython/plpy_subxactobject.c7
-rw-r--r--src/pl/plpython/plpy_typeio.c412
-rw-r--r--src/pl/plpython/plpy_typeio.h19
-rw-r--r--src/pl/plpython/plpython.h2
-rw-r--r--src/pl/plpython/po/de.po185
-rw-r--r--src/pl/plpython/po/fr.po271
-rw-r--r--src/pl/plpython/po/ko.po220
-rw-r--r--src/pl/plpython/po/pl.po246
-rw-r--r--src/pl/plpython/po/pt_BR.po185
-rw-r--r--src/pl/plpython/po/ru.po227
-rw-r--r--src/pl/plpython/sql/plpython_composite.sql21
-rw-r--r--src/pl/plpython/sql/plpython_ereport.sql8
-rw-r--r--src/pl/plpython/sql/plpython_spi.sql22
-rw-r--r--src/pl/plpython/sql/plpython_test.sql4
-rw-r--r--src/pl/plpython/sql/plpython_trigger.sql24
-rw-r--r--src/pl/plpython/sql/plpython_types.sql72
-rw-r--r--src/pl/tcl/Makefile9
-rw-r--r--src/pl/tcl/expected/pltcl_queries.out392
-rw-r--r--src/pl/tcl/expected/pltcl_setup.out159
-rw-r--r--src/pl/tcl/expected/pltcl_start_proc.out31
-rw-r--r--src/pl/tcl/expected/pltcl_subxact.out143
-rw-r--r--src/pl/tcl/generate-pltclerrcodes.pl4
-rw-r--r--src/pl/tcl/modules/.gitignore3
-rw-r--r--src/pl/tcl/modules/Makefile28
-rw-r--r--src/pl/tcl/modules/README18
-rw-r--r--src/pl/tcl/modules/pltcl_delmod.in117
-rw-r--r--src/pl/tcl/modules/pltcl_listmod.in123
-rw-r--r--src/pl/tcl/modules/pltcl_loadmod.in501
-rw-r--r--src/pl/tcl/modules/unknown.pltcl63
-rw-r--r--src/pl/tcl/pltcl.c1177
-rw-r--r--src/pl/tcl/po/de.po90
-rw-r--r--src/pl/tcl/po/pl.po120
-rw-r--r--src/pl/tcl/po/ru.po118
-rw-r--r--src/pl/tcl/sql/pltcl_queries.sql139
-rw-r--r--src/pl/tcl/sql/pltcl_setup.sql155
-rw-r--r--src/pl/tcl/sql/pltcl_start_proc.sql21
-rw-r--r--src/pl/tcl/sql/pltcl_subxact.sql95
85 files changed, 5949 insertions, 4140 deletions
diff --git a/src/pl/plperl/Util.xs b/src/pl/plperl/Util.xs
index 8c3c47fec9..dbba0d7b87 100644
--- a/src/pl/plperl/Util.xs
+++ b/src/pl/plperl/Util.xs
@@ -116,7 +116,7 @@ util_quote_literal(sv)
}
else {
text *arg = sv2text(sv);
- text *quoted = DatumGetTextP(DirectFunctionCall1(quote_literal, PointerGetDatum(arg)));
+ text *quoted = DatumGetTextPP(DirectFunctionCall1(quote_literal, PointerGetDatum(arg)));
char *str;
pfree(arg);
@@ -138,7 +138,7 @@ util_quote_nullable(sv)
else
{
text *arg = sv2text(sv);
- text *quoted = DatumGetTextP(DirectFunctionCall1(quote_nullable, PointerGetDatum(arg)));
+ text *quoted = DatumGetTextPP(DirectFunctionCall1(quote_nullable, PointerGetDatum(arg)));
char *str;
pfree(arg);
@@ -158,7 +158,7 @@ util_quote_ident(sv)
char *str;
CODE:
arg = sv2text(sv);
- quoted = DatumGetTextP(DirectFunctionCall1(quote_ident, PointerGetDatum(arg)));
+ quoted = DatumGetTextPP(DirectFunctionCall1(quote_ident, PointerGetDatum(arg)));
pfree(arg);
str = text_to_cstring(quoted);
@@ -175,9 +175,9 @@ util_decode_bytea(sv)
text *ret;
CODE:
arg = SvPVbyte_nolen(sv);
- ret = DatumGetTextP(DirectFunctionCall1(byteain, PointerGetDatum(arg)));
+ ret = DatumGetTextPP(DirectFunctionCall1(byteain, PointerGetDatum(arg)));
/* not cstr2sv because this is raw bytes not utf8'able */
- RETVAL = newSVpvn(VARDATA(ret), (VARSIZE(ret) - VARHDRSZ));
+ RETVAL = newSVpvn(VARDATA_ANY(ret), VARSIZE_ANY_EXHDR(ret));
OUTPUT:
RETVAL
diff --git a/src/pl/plperl/expected/plperl_trigger.out b/src/pl/plperl/expected/plperl_trigger.out
index 5e3860ef97..28011cd9f6 100644
--- a/src/pl/plperl/expected/plperl_trigger.out
+++ b/src/pl/plperl/expected/plperl_trigger.out
@@ -241,6 +241,35 @@ $$ LANGUAGE plperl;
SELECT direct_trigger();
ERROR: trigger functions can only be called as triggers
CONTEXT: compilation of PL/Perl function "direct_trigger"
+-- check that SQL run in trigger code can see transition tables
+CREATE TABLE transition_table_test (id int, name text);
+INSERT INTO transition_table_test VALUES (1, 'a');
+CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plperl AS
+$$
+ my $cursor = spi_query("SELECT * FROM old_table");
+ my $row = spi_fetchrow($cursor);
+ defined($row) || die "expected a row";
+ elog(INFO, "old: " . $row->{id} . " -> " . $row->{name});
+ my $row = spi_fetchrow($cursor);
+ !defined($row) || die "expected no more rows";
+
+ my $cursor = spi_query("SELECT * FROM new_table");
+ my $row = spi_fetchrow($cursor);
+ defined($row) || die "expected a row";
+ elog(INFO, "new: " . $row->{id} . " -> " . $row->{name});
+ my $row = spi_fetchrow($cursor);
+ !defined($row) || die "expected no more rows";
+
+ return undef;
+$$;
+CREATE TRIGGER a_t AFTER UPDATE ON transition_table_test
+ REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
+ FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_test_f();
+UPDATE transition_table_test SET name = 'b';
+INFO: old: 1 -> a
+INFO: new: 1 -> b
+DROP TABLE transition_table_test;
+DROP FUNCTION transition_table_test_f();
-- test plperl command triggers
create or replace function perlsnitch() returns event_trigger language plperl as $$
elog(NOTICE, "perlsnitch: " . $_TD->{event} . " " . $_TD->{tag} . " ");
diff --git a/src/pl/plperl/plc_perlboot.pl b/src/pl/plperl/plc_perlboot.pl
index d506d01163..ff05964869 100644
--- a/src/pl/plperl/plc_perlboot.pl
+++ b/src/pl/plperl/plc_perlboot.pl
@@ -1,5 +1,7 @@
# src/pl/plperl/plc_perlboot.pl
+use strict;
+
use 5.008001;
use vars qw(%_SHARED $_TD);
@@ -50,7 +52,8 @@ sub ::encode_array_constructor
{
- package PostgreSQL::InServer;
+ package PostgreSQL::InServer
+ ; ## no critic (RequireFilenameMatchesPackage);
use strict;
use warnings;
@@ -84,11 +87,13 @@ sub ::encode_array_constructor
sub mkfunc
{
+ ## no critic (ProhibitNoStrict, ProhibitStringyEval);
no strict; # default to no strict for the eval
no warnings; # default to no warnings for the eval
my $ret = eval(mkfuncsrc(@_));
$@ =~ s/\(eval \d+\) //g if $@;
return $ret;
+ ## use critic
}
1;
diff --git a/src/pl/plperl/plc_trusted.pl b/src/pl/plperl/plc_trusted.pl
index cd61882eb6..7b11a3f52b 100644
--- a/src/pl/plperl/plc_trusted.pl
+++ b/src/pl/plperl/plc_trusted.pl
@@ -1,6 +1,7 @@
# src/pl/plperl/plc_trusted.pl
-package PostgreSQL::InServer::safe;
+package PostgreSQL::InServer::safe
+ ; ## no critic (RequireFilenameMatchesPackage);
# Load widely useful pragmas into plperl to make them available.
#
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 82bde6e442..ce72dd488e 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -13,7 +13,6 @@
#include <ctype.h>
#include <fcntl.h>
#include <limits.h>
-#include <locale.h>
#include <unistd.h>
/* postgreSQL stuff */
@@ -98,17 +97,19 @@ typedef struct plperl_interp_desc
/**********************************************************************
* The information we cache about loaded procedures
*
- * The refcount field counts the struct's reference from the hash table shown
- * below, plus one reference for each function call level that is using the
- * struct. We can release the struct, and the associated Perl sub, when the
- * refcount goes to zero.
+ * The fn_refcount field counts the struct's reference from the hash table
+ * shown below, plus one reference for each function call level that is using
+ * the struct. We can release the struct, and the associated Perl sub, when
+ * the fn_refcount goes to zero. Releasing the struct itself is done by
+ * deleting the fn_cxt, which also gets rid of all subsidiary data.
**********************************************************************/
typedef struct plperl_proc_desc
{
char *proname; /* user name of procedure */
+ MemoryContext fn_cxt; /* memory context for this procedure */
+ unsigned long fn_refcount; /* number of active references */
TransactionId fn_xmin; /* xmin/TID of procedure's pg_proc tuple */
ItemPointerData fn_tid;
- int refcount; /* reference count of this struct */
SV *reference; /* CODE reference for Perl sub */
plperl_interp_desc *interp; /* interpreter it's created in */
bool fn_readonly; /* is function readonly (not volatile)? */
@@ -122,18 +123,19 @@ typedef struct plperl_proc_desc
Oid result_oid; /* Oid of result type */
FmgrInfo result_in_func; /* I/O function and arg for result type */
Oid result_typioparam;
- /* Conversion info for function's argument types: */
+ /* Per-argument info for function's argument types: */
int nargs;
- FmgrInfo arg_out_func[FUNC_MAX_ARGS];
- bool arg_is_rowtype[FUNC_MAX_ARGS];
- Oid arg_arraytype[FUNC_MAX_ARGS]; /* InvalidOid if not an array */
+ FmgrInfo *arg_out_func; /* output fns for arg types */
+ bool *arg_is_rowtype; /* is each arg composite? */
+ Oid *arg_arraytype; /* InvalidOid if not an array */
} plperl_proc_desc;
#define increment_prodesc_refcount(prodesc) \
- ((prodesc)->refcount++)
+ ((prodesc)->fn_refcount++)
#define decrement_prodesc_refcount(prodesc) \
do { \
- if (--((prodesc)->refcount) <= 0) \
+ Assert((prodesc)->fn_refcount > 0); \
+ if (--((prodesc)->fn_refcount) == 0) \
free_plperl_function(prodesc); \
} while(0)
@@ -353,23 +355,6 @@ hek2cstr(HE *he)
return ret;
}
-/*
- * This routine is a crock, and so is everyplace that calls it. The problem
- * is that the cached form of plperl functions/queries is allocated permanently
- * (mostly via malloc()) and never released until backend exit. Subsidiary
- * data structures such as fmgr info records therefore must live forever
- * as well. A better implementation would store all this stuff in a per-
- * function memory context that could be reclaimed at need. In the meantime,
- * fmgr_info_cxt must be called specifying TopMemoryContext so that whatever
- * it might allocate, and whatever the eventual function might allocate using
- * fn_mcxt, will live forever too.
- */
-static void
-perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
-{
- fmgr_info_cxt(functionId, finfo, TopMemoryContext);
-}
-
/*
* _PG_init() - library load-time initialization
@@ -1076,11 +1061,16 @@ plperl_build_tuple_result(HV *perlhash, TupleDesc td)
char *key = hek2cstr(he);
int attn = SPI_fnumber(td, key);
- if (attn <= 0 || td->attrs[attn - 1]->attisdropped)
+ if (attn == SPI_ERROR_NOATTRIBUTE)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("Perl hash contains nonexistent column \"%s\"",
key)));
+ if (attn <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot set system attribute \"%s\"",
+ key)));
values[attn - 1] = plperl_sv_to_datum(val,
td->attrs[attn - 1]->atttypid,
@@ -1433,6 +1423,10 @@ plperl_ref_from_pg_array(Datum arg, Oid typid)
SV *av;
HV *hv;
+ /*
+ * Currently we make no effort to cache any of the stuff we look up here,
+ * which is bad.
+ */
info = palloc0(sizeof(plperl_array_info));
/* get element type information, including output conversion function */
@@ -1440,10 +1434,16 @@ plperl_ref_from_pg_array(Datum arg, Oid typid)
&typlen, &typbyval, &typalign,
&typdelim, &typioparam, &typoutputfunc);
- if ((transform_funcid = get_transform_fromsql(elementtype, current_call_data->prodesc->lang_oid, current_call_data->prodesc->trftypes)))
- perm_fmgr_info(transform_funcid, &info->transform_proc);
+ /* Check for a transform function */
+ transform_funcid = get_transform_fromsql(elementtype,
+ current_call_data->prodesc->lang_oid,
+ current_call_data->prodesc->trftypes);
+
+ /* Look up transform or output function as appropriate */
+ if (OidIsValid(transform_funcid))
+ fmgr_info(transform_funcid, &info->transform_proc);
else
- perm_fmgr_info(typoutputfunc, &info->proc);
+ fmgr_info(typoutputfunc, &info->proc);
info->elem_is_rowtype = type_is_rowtype(elementtype);
@@ -1674,8 +1674,7 @@ plperl_event_trigger_build_args(FunctionCallInfo fcinfo)
return newRV_noinc((SV *) hv);
}
-/* Set up the new tuple returned from a trigger. */
-
+/* Construct the modified new tuple to be returned from a trigger. */
static HeapTuple
plperl_modify_tuple(HV *hvTD, TriggerData *tdata, HeapTuple otup)
{
@@ -1683,14 +1682,11 @@ plperl_modify_tuple(HV *hvTD, TriggerData *tdata, HeapTuple otup)
HV *hvNew;
HE *he;
HeapTuple rtup;
- int slotsused;
- int *modattrs;
- Datum *modvalues;
- char *modnulls;
-
TupleDesc tupdesc;
-
- tupdesc = tdata->tg_relation->rd_att;
+ int natts;
+ Datum *modvalues;
+ bool *modnulls;
+ bool *modrepls;
svp = hv_fetch_string(hvTD, "new");
if (!svp)
@@ -1703,51 +1699,49 @@ plperl_modify_tuple(HV *hvTD, TriggerData *tdata, HeapTuple otup)
errmsg("$_TD->{new} is not a hash reference")));
hvNew = (HV *) SvRV(*svp);
- modattrs = palloc(tupdesc->natts * sizeof(int));
- modvalues = palloc(tupdesc->natts * sizeof(Datum));
- modnulls = palloc(tupdesc->natts * sizeof(char));
- slotsused = 0;
+ tupdesc = tdata->tg_relation->rd_att;
+ natts = tupdesc->natts;
+
+ modvalues = (Datum *) palloc0(natts * sizeof(Datum));
+ modnulls = (bool *) palloc0(natts * sizeof(bool));
+ modrepls = (bool *) palloc0(natts * sizeof(bool));
hv_iterinit(hvNew);
while ((he = hv_iternext(hvNew)))
{
- bool isnull;
char *key = hek2cstr(he);
SV *val = HeVAL(he);
int attn = SPI_fnumber(tupdesc, key);
- if (attn <= 0 || tupdesc->attrs[attn - 1]->attisdropped)
+ if (attn == SPI_ERROR_NOATTRIBUTE)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("Perl hash contains nonexistent column \"%s\"",
key)));
+ if (attn <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot set system attribute \"%s\"",
+ key)));
- modvalues[slotsused] = plperl_sv_to_datum(val,
+ modvalues[attn - 1] = plperl_sv_to_datum(val,
tupdesc->attrs[attn - 1]->atttypid,
tupdesc->attrs[attn - 1]->atttypmod,
- NULL,
- NULL,
- InvalidOid,
- &isnull);
-
- modnulls[slotsused] = isnull ? 'n' : ' ';
- modattrs[slotsused] = attn;
- slotsused++;
+ NULL,
+ NULL,
+ InvalidOid,
+ &modnulls[attn - 1]);
+ modrepls[attn - 1] = true;
pfree(key);
}
hv_iterinit(hvNew);
- rtup = SPI_modifytuple(tdata->tg_relation, otup, slotsused,
- modattrs, modvalues, modnulls);
+ rtup = heap_modify_tuple(otup, tupdesc, modvalues, modnulls, modrepls);
- pfree(modattrs);
pfree(modvalues);
pfree(modnulls);
-
- if (rtup == NULL)
- elog(ERROR, "SPI_modifytuple failed: %s",
- SPI_result_code_string(SPI_result));
+ pfree(modrepls);
return rtup;
}
@@ -1768,8 +1762,8 @@ Datum
plperl_call_handler(PG_FUNCTION_ARGS)
{
Datum retval;
- plperl_call_data *save_call_data = current_call_data;
- plperl_interp_desc *oldinterp = plperl_active_interp;
+ plperl_call_data *volatile save_call_data = current_call_data;
+ plperl_interp_desc *volatile oldinterp = plperl_active_interp;
plperl_call_data this_call_data;
/* Initialize current-call status record */
@@ -1791,18 +1785,18 @@ plperl_call_handler(PG_FUNCTION_ARGS)
}
PG_CATCH();
{
- if (this_call_data.prodesc)
- decrement_prodesc_refcount(this_call_data.prodesc);
current_call_data = save_call_data;
activate_interpreter(oldinterp);
+ if (this_call_data.prodesc)
+ decrement_prodesc_refcount(this_call_data.prodesc);
PG_RE_THROW();
}
PG_END_TRY();
- if (this_call_data.prodesc)
- decrement_prodesc_refcount(this_call_data.prodesc);
current_call_data = save_call_data;
activate_interpreter(oldinterp);
+ if (this_call_data.prodesc)
+ decrement_prodesc_refcount(this_call_data.prodesc);
return retval;
}
@@ -1818,8 +1812,8 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
FunctionCallInfoData fake_fcinfo;
FmgrInfo flinfo;
plperl_proc_desc desc;
- plperl_call_data *save_call_data = current_call_data;
- plperl_interp_desc *oldinterp = plperl_active_interp;
+ plperl_call_data *volatile save_call_data = current_call_data;
+ plperl_interp_desc *volatile oldinterp = plperl_active_interp;
plperl_call_data this_call_data;
ErrorContextCallback pl_error_context;
@@ -2448,11 +2442,18 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
SV *svTD;
HV *hvTD;
ErrorContextCallback pl_error_context;
+ TriggerData *tdata;
+ int rc PG_USED_FOR_ASSERTS_ONLY;
/* Connect to SPI manager */
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "could not connect to SPI manager");
+ /* Make transition tables visible to this SPI connection */
+ tdata = (TriggerData *) fcinfo->context;
+ rc = SPI_register_trigger_data(tdata);
+ Assert(rc >= 0);
+
/* Find or compile the function */
prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, true, false);
current_call_data->prodesc = prodesc;
@@ -2616,7 +2617,7 @@ validate_plperl_function(plperl_proc_ptr *proc_ptr, HeapTuple procTup)
static void
free_plperl_function(plperl_proc_desc *prodesc)
{
- Assert(prodesc->refcount <= 0);
+ Assert(prodesc->fn_refcount == 0);
/* Release CODE reference, if we have one, from the appropriate interp */
if (prodesc->reference)
{
@@ -2626,12 +2627,8 @@ free_plperl_function(plperl_proc_desc *prodesc)
SvREFCNT_dec(prodesc->reference);
activate_interpreter(oldinterp);
}
- /* Get rid of what we conveniently can of our own structs */
- /* (FmgrInfo subsidiary info will get leaked ...) */
- if (prodesc->proname)
- free(prodesc->proname);
- list_free(prodesc->trftypes);
- free(prodesc);
+ /* Release all PG-owned data for this proc */
+ MemoryContextDelete(prodesc->fn_cxt);
}
@@ -2642,8 +2639,8 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
Form_pg_proc procStruct;
plperl_proc_key proc_key;
plperl_proc_ptr *proc_ptr;
- plperl_proc_desc *prodesc = NULL;
- int i;
+ plperl_proc_desc *volatile prodesc = NULL;
+ volatile MemoryContext proc_cxt = NULL;
plperl_interp_desc *oldinterp = plperl_active_interp;
ErrorContextCallback plperl_error_context;
@@ -2653,41 +2650,50 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
elog(ERROR, "cache lookup failed for function %u", fn_oid);
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
- /* Set a callback for reporting compilation errors */
- plperl_error_context.callback = plperl_compile_callback;
- plperl_error_context.previous = error_context_stack;
- plperl_error_context.arg = NameStr(procStruct->proname);
- error_context_stack = &plperl_error_context;
-
- /* Try to find function in plperl_proc_hash */
+ /*
+ * Try to find function in plperl_proc_hash. The reason for this
+ * overcomplicated-seeming lookup procedure is that we don't know whether
+ * it's plperl or plperlu, and don't want to spend a lookup in pg_language
+ * to find out.
+ */
proc_key.proc_id = fn_oid;
proc_key.is_trigger = is_trigger;
proc_key.user_id = GetUserId();
-
proc_ptr = hash_search(plperl_proc_hash, &proc_key,
HASH_FIND, NULL);
+ if (validate_plperl_function(proc_ptr, procTup))
+ {
+ /* Found valid plperl entry */
+ ReleaseSysCache(procTup);
+ return proc_ptr->proc_ptr;
+ }
+ /* If not found or obsolete, maybe it's plperlu */
+ proc_key.user_id = InvalidOid;
+ proc_ptr = hash_search(plperl_proc_hash, &proc_key,
+ HASH_FIND, NULL);
if (validate_plperl_function(proc_ptr, procTup))
- prodesc = proc_ptr->proc_ptr;
- else
{
- /* If not found or obsolete, maybe it's plperlu */
- proc_key.user_id = InvalidOid;
- proc_ptr = hash_search(plperl_proc_hash, &proc_key,
- HASH_FIND, NULL);
- if (validate_plperl_function(proc_ptr, procTup))
- prodesc = proc_ptr->proc_ptr;
+ /* Found valid plperlu entry */
+ ReleaseSysCache(procTup);
+ return proc_ptr->proc_ptr;
}
/************************************************************
* If we haven't found it in the hashtable, we analyze
* the function's arguments and return type and store
- * the in-/out-functions in the prodesc block and create
- * a new hashtable entry for it.
- *
- * Then we load the procedure into the Perl interpreter.
+ * the in-/out-functions in the prodesc block,
+ * then we load the procedure into the Perl interpreter,
+ * and last we create a new hashtable entry for it.
************************************************************/
- if (prodesc == NULL)
+
+ /* Set a callback for reporting compilation errors */
+ plperl_error_context.callback = plperl_compile_callback;
+ plperl_error_context.previous = error_context_stack;
+ plperl_error_context.arg = NameStr(procStruct->proname);
+ error_context_stack = &plperl_error_context;
+
+ PG_TRY();
{
HeapTuple langTup;
HeapTuple typeTup;
@@ -2697,42 +2703,42 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
Datum prosrcdatum;
bool isnull;
char *proc_source;
+ MemoryContext oldcontext;
/************************************************************
- * Allocate a new procedure description block
+ * Allocate a context that will hold all PG data for the procedure.
************************************************************/
- prodesc = (plperl_proc_desc *) malloc(sizeof(plperl_proc_desc));
- if (prodesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("out of memory")));
- /* Initialize all fields to 0 so free_plperl_function is safe */
- MemSet(prodesc, 0, sizeof(plperl_proc_desc));
+ proc_cxt = AllocSetContextCreate(TopMemoryContext,
+ NameStr(procStruct->proname),
+ ALLOCSET_SMALL_SIZES);
- prodesc->proname = strdup(NameStr(procStruct->proname));
- if (prodesc->proname == NULL)
- {
- free_plperl_function(prodesc);
- ereport(ERROR,
- (errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("out of memory")));
- }
+ /************************************************************
+ * Allocate and fill a new procedure description block.
+ * struct prodesc and subsidiary data must all live in proc_cxt.
+ ************************************************************/
+ oldcontext = MemoryContextSwitchTo(proc_cxt);
+ prodesc = (plperl_proc_desc *) palloc0(sizeof(plperl_proc_desc));
+ prodesc->proname = pstrdup(NameStr(procStruct->proname));
+ prodesc->fn_cxt = proc_cxt;
+ prodesc->fn_refcount = 0;
prodesc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
prodesc->fn_tid = procTup->t_self;
+ prodesc->nargs = procStruct->pronargs;
+ prodesc->arg_out_func = (FmgrInfo *) palloc0(prodesc->nargs * sizeof(FmgrInfo));
+ prodesc->arg_is_rowtype = (bool *) palloc0(prodesc->nargs * sizeof(bool));
+ prodesc->arg_arraytype = (Oid *) palloc0(prodesc->nargs * sizeof(Oid));
+ MemoryContextSwitchTo(oldcontext);
/* Remember if function is STABLE/IMMUTABLE */
prodesc->fn_readonly =
(procStruct->provolatile != PROVOLATILE_VOLATILE);
- {
- MemoryContext oldcxt;
-
- protrftypes_datum = SysCacheGetAttr(PROCOID, procTup,
+ /* Fetch protrftypes */
+ protrftypes_datum = SysCacheGetAttr(PROCOID, procTup,
Anum_pg_proc_protrftypes, &isnull);
- oldcxt = MemoryContextSwitchTo(TopMemoryContext);
- prodesc->trftypes = isnull ? NIL : oid_array_to_list(protrftypes_datum);
- MemoryContextSwitchTo(oldcxt);
- }
+ MemoryContextSwitchTo(proc_cxt);
+ prodesc->trftypes = isnull ? NIL : oid_array_to_list(protrftypes_datum);
+ MemoryContextSwitchTo(oldcontext);
/************************************************************
* Lookup the pg_language tuple by Oid
@@ -2740,11 +2746,8 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
langTup = SearchSysCache1(LANGOID,
ObjectIdGetDatum(procStruct->prolang));
if (!HeapTupleIsValid(langTup))
- {
- free_plperl_function(prodesc);
elog(ERROR, "cache lookup failed for language %u",
procStruct->prolang);
- }
langStruct = (Form_pg_language) GETSTRUCT(langTup);
prodesc->lang_oid = HeapTupleGetOid(langTup);
prodesc->lanpltrusted = langStruct->lanpltrusted;
@@ -2760,11 +2763,8 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
SearchSysCache1(TYPEOID,
ObjectIdGetDatum(procStruct->prorettype));
if (!HeapTupleIsValid(typeTup))
- {
- free_plperl_function(prodesc);
elog(ERROR, "cache lookup failed for type %u",
procStruct->prorettype);
- }
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
/* Disallow pseudotype result, except VOID or RECORD */
@@ -2775,21 +2775,15 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
/* okay */ ;
else if (procStruct->prorettype == TRIGGEROID ||
procStruct->prorettype == EVTTRIGGEROID)
- {
- free_plperl_function(prodesc);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("trigger functions can only be called "
"as triggers")));
- }
else
- {
- free_plperl_function(prodesc);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("PL/Perl functions cannot return type %s",
format_type_be(procStruct->prorettype))));
- }
}
prodesc->result_oid = procStruct->prorettype;
@@ -2800,7 +2794,9 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
prodesc->fn_retisarray =
(typeStruct->typlen == -1 && typeStruct->typelem);
- perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func));
+ fmgr_info_cxt(typeStruct->typinput,
+ &(prodesc->result_in_func),
+ proc_cxt);
prodesc->result_typioparam = getTypeIOParam(typeTup);
ReleaseSysCache(typeTup);
@@ -2812,29 +2808,24 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
************************************************************/
if (!is_trigger && !is_event_trigger)
{
- prodesc->nargs = procStruct->pronargs;
+ int i;
+
for (i = 0; i < prodesc->nargs; i++)
{
typeTup = SearchSysCache1(TYPEOID,
ObjectIdGetDatum(procStruct->proargtypes.values[i]));
if (!HeapTupleIsValid(typeTup))
- {
- free_plperl_function(prodesc);
elog(ERROR, "cache lookup failed for type %u",
procStruct->proargtypes.values[i]);
- }
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
/* Disallow pseudotype argument */
if (typeStruct->typtype == TYPTYPE_PSEUDO &&
procStruct->proargtypes.values[i] != RECORDOID)
- {
- free_plperl_function(prodesc);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("PL/Perl functions cannot accept type %s",
format_type_be(procStruct->proargtypes.values[i]))));
- }
if (typeStruct->typtype == TYPTYPE_COMPOSITE ||
procStruct->proargtypes.values[i] == RECORDOID)
@@ -2842,8 +2833,9 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
else
{
prodesc->arg_is_rowtype[i] = false;
- perm_fmgr_info(typeStruct->typoutput,
- &(prodesc->arg_out_func[i]));
+ fmgr_info_cxt(typeStruct->typoutput,
+ &(prodesc->arg_out_func[i]),
+ proc_cxt);
}
/* Identify array attributes */
@@ -2880,22 +2872,42 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
activate_interpreter(oldinterp);
pfree(proc_source);
+
if (!prodesc->reference) /* can this happen? */
- {
- free_plperl_function(prodesc);
elog(ERROR, "could not create PL/Perl internal procedure");
- }
/************************************************************
- * OK, link the procedure into the correct hashtable entry
+ * OK, link the procedure into the correct hashtable entry.
+ * Note we assume that the hashtable entry either doesn't exist yet,
+ * or we already cleared its proc_ptr during the validation attempts
+ * above. So no need to decrement an old refcount here.
************************************************************/
proc_key.user_id = prodesc->lanpltrusted ? GetUserId() : InvalidOid;
proc_ptr = hash_search(plperl_proc_hash, &proc_key,
HASH_ENTER, NULL);
+ /* We assume these two steps can't throw an error: */
proc_ptr->proc_ptr = prodesc;
increment_prodesc_refcount(prodesc);
}
+ PG_CATCH();
+ {
+ /*
+ * If we got as far as creating a reference, we should be able to use
+ * free_plperl_function() to clean up. If not, then at most we have
+ * some PG memory resources in proc_cxt, which we can just delete.
+ */
+ if (prodesc && prodesc->reference)
+ free_plperl_function(prodesc);
+ else if (proc_cxt)
+ MemoryContextDelete(proc_cxt);
+
+ /* Be sure to restore the previous interpreter, too, for luck */
+ activate_interpreter(oldinterp);
+
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
/* restore previous error callback */
error_context_stack = plperl_error_context.previous;
@@ -3051,12 +3063,6 @@ plperl_spi_exec(char *query, int limit)
ReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
-
- /*
- * AtEOSubXact_SPI() should not have popped any SPI context, but just
- * in case it did, make sure we remain connected.
- */
- SPI_restore_connection();
}
PG_CATCH();
{
@@ -3072,13 +3078,6 @@ plperl_spi_exec(char *query, int limit)
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
- /*
- * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
- * have left us in a disconnected state. We need this hack to return
- * to connected state.
- */
- SPI_restore_connection();
-
/* Punt the error to Perl */
croak_cstr(edata->message);
@@ -3205,9 +3204,7 @@ plperl_return_next(SV *sv)
current_call_data->tmp_cxt =
AllocSetContextCreate(CurrentMemoryContext,
"PL/Perl return_next temporary cxt",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
+ ALLOCSET_DEFAULT_SIZES);
}
old_cxt = MemoryContextSwitchTo(current_call_data->tmp_cxt);
@@ -3292,12 +3289,6 @@ plperl_spi_query(char *query)
ReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
-
- /*
- * AtEOSubXact_SPI() should not have popped any SPI context, but just
- * in case it did, make sure we remain connected.
- */
- SPI_restore_connection();
}
PG_CATCH();
{
@@ -3313,13 +3304,6 @@ plperl_spi_query(char *query)
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
- /*
- * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
- * have left us in a disconnected state. We need this hack to return
- * to connected state.
- */
- SPI_restore_connection();
-
/* Punt the error to Perl */
croak_cstr(edata->message);
@@ -3378,12 +3362,6 @@ plperl_spi_fetchrow(char *cursor)
ReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
-
- /*
- * AtEOSubXact_SPI() should not have popped any SPI context, but just
- * in case it did, make sure we remain connected.
- */
- SPI_restore_connection();
}
PG_CATCH();
{
@@ -3399,13 +3377,6 @@ plperl_spi_fetchrow(char *cursor)
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
- /*
- * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
- * have left us in a disconnected state. We need this hack to return
- * to connected state.
- */
- SPI_restore_connection();
-
/* Punt the error to Perl */
croak_cstr(edata->message);
@@ -3460,9 +3431,7 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
************************************************************/
plan_cxt = AllocSetContextCreate(TopMemoryContext,
"PL/Perl spi_prepare query",
- ALLOCSET_SMALL_MINSIZE,
- ALLOCSET_SMALL_INITSIZE,
- ALLOCSET_SMALL_MAXSIZE);
+ ALLOCSET_SMALL_SIZES);
MemoryContextSwitchTo(plan_cxt);
qdesc = (plperl_query_desc *) palloc0(sizeof(plperl_query_desc));
snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
@@ -3479,9 +3448,7 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
************************************************************/
work_cxt = AllocSetContextCreate(CurrentMemoryContext,
"PL/Perl spi_prepare workspace",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
+ ALLOCSET_DEFAULT_SIZES);
MemoryContextSwitchTo(work_cxt);
/************************************************************
@@ -3543,12 +3510,6 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
ReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
-
- /*
- * AtEOSubXact_SPI() should not have popped any SPI context, but just
- * in case it did, make sure we remain connected.
- */
- SPI_restore_connection();
}
PG_CATCH();
{
@@ -3574,13 +3535,6 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
- /*
- * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
- * have left us in a disconnected state. We need this hack to return
- * to connected state.
- */
- SPI_restore_connection();
-
/* Punt the error to Perl */
croak_cstr(edata->message);
@@ -3694,12 +3648,6 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv)
ReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
-
- /*
- * AtEOSubXact_SPI() should not have popped any SPI context, but just
- * in case it did, make sure we remain connected.
- */
- SPI_restore_connection();
}
PG_CATCH();
{
@@ -3715,13 +3663,6 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv)
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
- /*
- * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
- * have left us in a disconnected state. We need this hack to return
- * to connected state.
- */
- SPI_restore_connection();
-
/* Punt the error to Perl */
croak_cstr(edata->message);
@@ -3823,12 +3764,6 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv)
ReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
-
- /*
- * AtEOSubXact_SPI() should not have popped any SPI context, but just
- * in case it did, make sure we remain connected.
- */
- SPI_restore_connection();
}
PG_CATCH();
{
@@ -3844,13 +3779,6 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv)
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
- /*
- * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
- * have left us in a disconnected state. We need this hack to return
- * to connected state.
- */
- SPI_restore_connection();
-
/* Punt the error to Perl */
croak_cstr(edata->message);
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 0146d60a11..ae367b0fc1 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -5,7 +5,7 @@
*
* This should be included _AFTER_ postgres.h and system include files
*
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1995, Regents of the University of California
*
* src/pl/plperl/plperl.h
diff --git a/src/pl/plperl/po/de.po b/src/pl/plperl/po/de.po
index e1c4ae9e89..ff818fd6f3 100644
--- a/src/pl/plperl/po/de.po
+++ b/src/pl/plperl/po/de.po
@@ -1,16 +1,16 @@
# German message translation file for plperl
-# Copyright (C) 2010 PostgreSQL Global Development Group
+# Copyright (C) 2017 PostgreSQL Global Development Group
# This file is distributed under the same license as the PostgreSQL package.
-# Peter Eisentraut <peter_e@gmx.net>, 2009 - 2016.
+# Peter Eisentraut <peter_e@gmx.net>, 2009 - 2017.
#
# Use these quotes: »%s«
#
msgid ""
msgstr ""
-"Project-Id-Version: PostgreSQL 9.6\n"
+"Project-Id-Version: PostgreSQL 10\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2016-04-23 21:07+0000\n"
-"PO-Revision-Date: 2016-04-23 21:43-0400\n"
+"POT-Creation-Date: 2017-03-09 17:37+0000\n"
+"PO-Revision-Date: 2017-03-09 13:35-0500\n"
"Last-Translator: Peter Eisentraut <peter_e@gmx.net>\n"
"Language-Team: German <peter_e@gmx.net>\n"
"Language: de\n"
@@ -18,205 +18,205 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: plperl.c:405
+#: plperl.c:390
msgid "If true, trusted and untrusted Perl code will be compiled in strict mode."
msgstr "Wenn wahr, dann wird vertrauenswürdiger und nicht vertrauenswürdiger Perl-Code im »strict«-Modus kompiliert."
-#: plperl.c:419
+#: plperl.c:404
msgid "Perl initialization code to execute when a Perl interpreter is initialized."
msgstr "Perl-Initialisierungscode, der ausgeführt wird, wenn der Perl-Interpreter initialisiert wird."
-#: plperl.c:441
+#: plperl.c:426
msgid "Perl initialization code to execute once when plperl is first used."
msgstr "Perl-Initialisierungscode, der ausgeführt wird, wenn plperl zum ersten Mal benutzt wird."
-#: plperl.c:449
+#: plperl.c:434
msgid "Perl initialization code to execute once when plperlu is first used."
msgstr "Perl-Initialisierungscode, der ausgeführt wird, wenn plperlu zum ersten Mal benutzt wird."
-#: plperl.c:646
+#: plperl.c:631
#, c-format
msgid "cannot allocate multiple Perl interpreters on this platform"
msgstr "auf dieser Plattform können nicht mehrere Perl-Interpreter angelegt werden"
-#: plperl.c:666 plperl.c:841 plperl.c:847 plperl.c:961 plperl.c:973
-#: plperl.c:1016 plperl.c:1037 plperl.c:2080 plperl.c:2189 plperl.c:2256
-#: plperl.c:2318
+#: plperl.c:651 plperl.c:826 plperl.c:832 plperl.c:946 plperl.c:958
+#: plperl.c:1001 plperl.c:1022 plperl.c:2074 plperl.c:2183 plperl.c:2250
+#: plperl.c:2312
#, c-format
msgid "%s"
msgstr "%s"
-#: plperl.c:667
+#: plperl.c:652
#, c-format
msgid "while executing PostgreSQL::InServer::SPI::bootstrap"
msgstr "beim Ausführen von PostgreSQL::InServer::SPI::bootstrap"
-#: plperl.c:842
+#: plperl.c:827
#, c-format
msgid "while parsing Perl initialization"
msgstr "beim Parsen der Perl-Initialisierung"
-#: plperl.c:848
+#: plperl.c:833
#, c-format
msgid "while running Perl initialization"
msgstr "beim Ausführen der Perl-Initialisierung"
-#: plperl.c:962
+#: plperl.c:947
#, c-format
msgid "while executing PLC_TRUSTED"
msgstr "beim Ausführen von PLC_TRUSTED"
-#: plperl.c:974
+#: plperl.c:959
#, c-format
msgid "while executing utf8fix"
msgstr "beim Ausführen von utf8fix"
-#: plperl.c:1017
+#: plperl.c:1002
#, c-format
msgid "while executing plperl.on_plperl_init"
msgstr "beim Ausführen von plperl.on_plperl_init"
-#: plperl.c:1038
+#: plperl.c:1023
#, c-format
msgid "while executing plperl.on_plperlu_init"
msgstr "beim Ausführen von plperl.on_plperlu_init"
-#: plperl.c:1082 plperl.c:1722
+#: plperl.c:1067 plperl.c:1719
#, c-format
msgid "Perl hash contains nonexistent column \"%s\""
msgstr "Perl-Hash enthält nicht existierende Spalte »%s«"
-#: plperl.c:1167
+#: plperl.c:1072 plperl.c:1724
+#, c-format
+msgid "cannot set system attribute \"%s\""
+msgstr "Systemattribut »%s« kann nicht gesetzt werden"
+
+#: plperl.c:1157
#, c-format
msgid "number of array dimensions (%d) exceeds the maximum allowed (%d)"
msgstr "Anzahl der Arraydimensionen (%d) überschreitet erlaubtes Maximum (%d)"
-#: plperl.c:1179 plperl.c:1196
+#: plperl.c:1169 plperl.c:1186
#, c-format
msgid "multidimensional arrays must have array expressions with matching dimensions"
msgstr "mehrdimensionale Arrays müssen Arraysausdrücke mit gleicher Anzahl Dimensionen haben"
-#: plperl.c:1231
+#: plperl.c:1221
#, c-format
msgid "cannot convert Perl array to non-array type %s"
msgstr "kann Perl-Array nicht in Nicht-Array-Typ %s umwandeln"
-#: plperl.c:1333
+#: plperl.c:1323
#, c-format
msgid "cannot convert Perl hash to non-composite type %s"
msgstr "kann Perl-Hash nicht in nicht zusammengesetzten Typ %s umwandeln"
-#: plperl.c:1344
+#: plperl.c:1334
#, c-format
msgid "function returning record called in context that cannot accept type record"
msgstr "Funktion, die einen Record zurückgibt, in einem Zusammenhang aufgerufen, der Typ record nicht verarbeiten kann"
-#: plperl.c:1359
+#: plperl.c:1349
#, c-format
msgid "PL/Perl function must return reference to hash or array"
msgstr "PL/Perl-Funktion muss eine Referenz auf ein Hash oder ein Array zurückgeben"
-#: plperl.c:1396
+#: plperl.c:1386
#, c-format
msgid "lookup failed for type %s"
msgstr "Nachschlagen nach Typ %s fehlgeschlagen"
-#: plperl.c:1699
+#: plperl.c:1695
#, c-format
msgid "$_TD->{new} does not exist"
msgstr "$_TD->{new} existiert nicht"
-#: plperl.c:1703
+#: plperl.c:1699
#, c-format
msgid "$_TD->{new} is not a hash reference"
msgstr "$_TD->{new} ist keine Hash-Referenz"
-#: plperl.c:1956 plperl.c:2790
+#: plperl.c:1950 plperl.c:2778
#, c-format
msgid "PL/Perl functions cannot return type %s"
msgstr "PL/Perl-Funktionen können keinen Rückgabetyp %s haben"
-#: plperl.c:1969 plperl.c:2835
+#: plperl.c:1963 plperl.c:2820
#, c-format
msgid "PL/Perl functions cannot accept type %s"
msgstr "PL/Perl-Funktionen können Typ %s nicht annehmen"
-#: plperl.c:2085
+#: plperl.c:2079
#, c-format
msgid "didn't get a CODE reference from compiling function \"%s\""
msgstr "keine CODE-Referenz erhalten beim Kompilieren von Funktion »%s«"
-#: plperl.c:2177
+#: plperl.c:2171
#, c-format
msgid "didn't get a return item from function"
msgstr "keinen Rückgabewert aus Funktion erhalten"
-#: plperl.c:2220 plperl.c:2286
+#: plperl.c:2214 plperl.c:2280
#, c-format
msgid "couldn't fetch $_TD"
msgstr "konnte $_TD nicht auslesen"
-#: plperl.c:2244 plperl.c:2306
+#: plperl.c:2238 plperl.c:2300
#, c-format
msgid "didn't get a return item from trigger function"
msgstr "keinen Rückgabewert aus Triggerfunktion erhalten"
-#: plperl.c:2363
+#: plperl.c:2357
#, c-format
msgid "set-valued function called in context that cannot accept a set"
msgstr "Funktion mit Mengenergebnis in einem Zusammenhang aufgerufen, der keine Mengenergebnisse verarbeiten kann"
-#: plperl.c:2407
+#: plperl.c:2401
#, c-format
msgid "set-returning PL/Perl function must return reference to array or use return_next"
msgstr "PL/Perl-Funktionen mit Mengenergebnis müssen eine Referenz auf ein Array zurückgeben oder return_next verwenden"
-#: plperl.c:2521
+#: plperl.c:2515
#, c-format
msgid "ignoring modified row in DELETE trigger"
msgstr "geänderte Zeile im DELETE-Trigger wird ignoriert"
-#: plperl.c:2529
+#: plperl.c:2523
#, c-format
msgid "result of PL/Perl trigger function must be undef, \"SKIP\", or \"MODIFY\""
msgstr "Ergebnis einer PL/Perl-Triggerfunktion muss undef, »SKIP« oder »MODIFY« sein"
-#: plperl.c:2708 plperl.c:2718
-#, c-format
-msgid "out of memory"
-msgstr "Speicher aufgebraucht"
-
-#: plperl.c:2782
+#: plperl.c:2773
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "Triggerfunktionen können nur als Trigger aufgerufen werden"
-#: plperl.c:3121
+#: plperl.c:3113
#, c-format
msgid "query result has too many rows to fit in a Perl array"
msgstr "Anfrageergebnis hat zu viele Zeilen, um in ein Perl-Array zu passen"
-#: plperl.c:3166
+#: plperl.c:3158
#, c-format
msgid "cannot use return_next in a non-SETOF function"
msgstr "return_next kann nur in einer Funktion mit SETOF-Rückgabetyp verwendet werden"
-#: plperl.c:3222
+#: plperl.c:3212
#, c-format
msgid "SETOF-composite-returning PL/Perl function must call return_next with reference to hash"
msgstr "PL/Perl-Funktion, die SETOF eines zusammengesetzten Typs zurückgibt, muss return_next mit einer Referenz auf ein Hash aufrufen"
-#: plperl.c:3954
+#: plperl.c:3875
#, c-format
msgid "PL/Perl function \"%s\""
msgstr "PL/Perl-Funktion »%s«"
-#: plperl.c:3966
+#: plperl.c:3887
#, c-format
msgid "compilation of PL/Perl function \"%s\""
msgstr "Kompilierung der PL/Perl-Funktion »%s«"
-#: plperl.c:3975
+#: plperl.c:3896
#, c-format
msgid "PL/Perl anonymous code block"
msgstr "anonymer PL/Perl-Codeblock"
diff --git a/src/pl/plperl/po/fr.po b/src/pl/plperl/po/fr.po
index 714cab36fd..8aee506e6d 100644
--- a/src/pl/plperl/po/fr.po
+++ b/src/pl/plperl/po/fr.po
@@ -1,7 +1,7 @@
# translation of plperl.po to fr_fr
# french message translation file for plperl
#
-# Use these quotes: %s
+# Use these quotes: « %s »
# Guillaume Lelarge <guillaume@lelarge.info>, 2009.
#
msgid ""
@@ -14,38 +14,38 @@ msgstr ""
"Language-Team: French <guillaume@lelarge.info>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.8.7.1\n"
#: plperl.c:405
msgid "If true, trusted and untrusted Perl code will be compiled in strict mode."
msgstr ""
-"Si true, le code Perl de confiance et sans confiance sera compil en mode\n"
+"Si true, le code Perl de confiance et sans confiance sera compilé en mode\n"
"strict."
#: plperl.c:419
msgid "Perl initialization code to execute when a Perl interpreter is initialized."
msgstr ""
-"Code d'initialisation Perl excuter lorsque un interprteur Perl est\n"
-"initialis."
+"Code d'initialisation Perl à exécuter lorsque un interpréteur Perl est\n"
+"initialisé."
#: plperl.c:441
msgid "Perl initialization code to execute once when plperl is first used."
msgstr ""
-"Code d'initialisation Perl excuter lorsque plperl est utilis pour la\n"
-"premire fois"
+"Code d'initialisation Perl à exécuter lorsque plperl est utilisé pour la\n"
+"première fois"
#: plperl.c:449
msgid "Perl initialization code to execute once when plperlu is first used."
msgstr ""
-"Code d'initialisation Perl excuter lorsque plperlu est utilis pour la\n"
-"premire fois"
+"Code d'initialisation Perl à exécuter lorsque plperlu est utilisé pour la\n"
+"première fois"
#: plperl.c:646
#, c-format
msgid "cannot allocate multiple Perl interpreters on this platform"
-msgstr "ne peut pas allouer plusieurs interprteurs Perl sur cette plateforme"
+msgstr "ne peut pas allouer plusieurs interpréteurs Perl sur cette plateforme"
#: plperl.c:666 plperl.c:841 plperl.c:847 plperl.c:961 plperl.c:973
#: plperl.c:1016 plperl.c:1037 plperl.c:2080 plperl.c:2189 plperl.c:2256
@@ -57,7 +57,7 @@ msgstr "%s"
#: plperl.c:667
#, c-format
msgid "while executing PostgreSQL::InServer::SPI::bootstrap"
-msgstr "lors de l'excution de PostgreSQL::InServer::SPI::bootstrap"
+msgstr "lors de l'exécution de PostgreSQL::InServer::SPI::bootstrap"
#: plperl.c:842
#, c-format
@@ -67,37 +67,37 @@ msgstr "lors de l'analyse de l'initialisation de perl"
#: plperl.c:848
#, c-format
msgid "while running Perl initialization"
-msgstr "lors de l'excution de l'initialisation de perl"
+msgstr "lors de l'exécution de l'initialisation de perl"
#: plperl.c:962
#, c-format
msgid "while executing PLC_TRUSTED"
-msgstr "lors de l'excution de PLC_TRUSTED"
+msgstr "lors de l'exécution de PLC_TRUSTED"
#: plperl.c:974
#, c-format
msgid "while executing utf8fix"
-msgstr "lors de l'excution de utf8fix"
+msgstr "lors de l'exécution de utf8fix"
#: plperl.c:1017
#, c-format
msgid "while executing plperl.on_plperl_init"
-msgstr "lors de l'excution de plperl.on_plperl_init"
+msgstr "lors de l'exécution de plperl.on_plperl_init"
#: plperl.c:1038
#, c-format
msgid "while executing plperl.on_plperlu_init"
-msgstr "lors de l'excution de plperl.on_plperlu_init"
+msgstr "lors de l'exécution de plperl.on_plperlu_init"
#: plperl.c:1082 plperl.c:1722
#, c-format
msgid "Perl hash contains nonexistent column \"%s\""
-msgstr "Le hachage Perl contient la colonne %s inexistante"
+msgstr "Le hachage Perl contient la colonne « %s » inexistante"
#: plperl.c:1167
#, c-format
msgid "number of array dimensions (%d) exceeds the maximum allowed (%d)"
-msgstr "le nombre de dimensions du tableau (%d) dpasse la maximum autoris (%d)"
+msgstr "le nombre de dimensions du tableau (%d) dépasse la maximum autorisé (%d)"
#: plperl.c:1179 plperl.c:1196
#, c-format
@@ -120,18 +120,18 @@ msgstr "ne peut pas convertir le hachage Perl en un type %s non composite"
#, c-format
msgid "function returning record called in context that cannot accept type record"
msgstr ""
-"fonction renvoyant le type record appele dans un contexte qui ne peut pas\n"
+"fonction renvoyant le type record appelée dans un contexte qui ne peut pas\n"
"accepter le type record"
#: plperl.c:1359
#, c-format
msgid "PL/Perl function must return reference to hash or array"
-msgstr "la fonction PL/perl doit renvoyer la rfrence un hachage ou un tableau"
+msgstr "la fonction PL/perl doit renvoyer la référence à un hachage ou à un tableau"
#: plperl.c:1396
#, c-format
msgid "lookup failed for type %s"
-msgstr "recherche choue pour le type %s"
+msgstr "recherche échouée pour le type %s"
#: plperl.c:1699
#, c-format
@@ -141,7 +141,7 @@ msgstr "$_TD->{new} n'existe pas"
#: plperl.c:1703
#, c-format
msgid "$_TD->{new} is not a hash reference"
-msgstr "$_TD->{new} n'est pas une rfrence de hachage"
+msgstr "$_TD->{new} n'est pas une référence de hachage"
#: plperl.c:1956 plperl.c:2790
#, c-format
@@ -156,63 +156,63 @@ msgstr "Les fonctions PL/perl ne peuvent pas accepter le type %s"
#: plperl.c:2085
#, c-format
msgid "didn't get a CODE reference from compiling function \"%s\""
-msgstr "n'a pas obtenu une rfrence CODE lors de la compilation de la fonction %s "
+msgstr "n'a pas obtenu une référence CODE lors de la compilation de la fonction « %s »"
#: plperl.c:2177
#, c-format
msgid "didn't get a return item from function"
-msgstr "n'a pas obtenu un lment en retour de la fonction"
+msgstr "n'a pas obtenu un élément en retour de la fonction"
#: plperl.c:2220 plperl.c:2286
#, c-format
msgid "couldn't fetch $_TD"
-msgstr "n'a pas pu rcuprer $_TD"
+msgstr "n'a pas pu récupérer $_TD"
#: plperl.c:2244 plperl.c:2306
#, c-format
msgid "didn't get a return item from trigger function"
-msgstr "n'a pas obtenu un lment en retour de la fonction trigger"
+msgstr "n'a pas obtenu un élément en retour de la fonction trigger"
#: plperl.c:2363
#, c-format
msgid "set-valued function called in context that cannot accept a set"
msgstr ""
-"fonction renvoyant un ensemble appele dans un contexte qui ne peut pas\n"
+"fonction renvoyant un ensemble appelée dans un contexte qui ne peut pas\n"
"accepter un ensemble"
#: plperl.c:2407
#, c-format
msgid "set-returning PL/Perl function must return reference to array or use return_next"
msgstr ""
-"la fonction PL/perl renvoyant des ensembles doit renvoyer la rfrence \n"
+"la fonction PL/perl renvoyant des ensembles doit renvoyer la référence à\n"
"un tableau ou utiliser return_next"
#: plperl.c:2521
#, c-format
msgid "ignoring modified row in DELETE trigger"
-msgstr "ignore la ligne modifie dans le trigger DELETE"
+msgstr "ignore la ligne modifiée dans le trigger DELETE"
#: plperl.c:2529
#, c-format
msgid "result of PL/Perl trigger function must be undef, \"SKIP\", or \"MODIFY\""
msgstr ""
-"le rsultat de la fonction trigger PL/perl doit tre undef, SKIP ou\n"
-" MODIFY "
+"le résultat de la fonction trigger PL/perl doit être undef, « SKIP » ou\n"
+"« MODIFY »"
#: plperl.c:2708 plperl.c:2718
#, c-format
msgid "out of memory"
-msgstr "mmoire puise"
+msgstr "mémoire épuisée"
#: plperl.c:2782
#, c-format
msgid "trigger functions can only be called as triggers"
-msgstr "les fonctions trigger peuvent seulement tre appeles par des triggers"
+msgstr "les fonctions trigger peuvent seulement être appelées par des triggers"
#: plperl.c:3121
#, c-format
msgid "query result has too many rows to fit in a Perl array"
-msgstr "le rsultat de la requte contient trop de lignes pour tre intgr dans un tableau Perl"
+msgstr "le résultat de la requête contient trop de lignes pour être intégré dans un tableau Perl"
#: plperl.c:3166
#, c-format
@@ -224,17 +224,17 @@ msgstr "ne peut pas utiliser return_next dans une fonction non SETOF"
msgid "SETOF-composite-returning PL/Perl function must call return_next with reference to hash"
msgstr ""
"une fonction PL/perl renvoyant des lignes composites doit appeler\n"
-"return_next avec la rfrence un hachage"
+"return_next avec la référence à un hachage"
#: plperl.c:3954
#, c-format
msgid "PL/Perl function \"%s\""
-msgstr "fonction PL/Perl %s "
+msgstr "fonction PL/Perl « %s »"
#: plperl.c:3966
#, c-format
msgid "compilation of PL/Perl function \"%s\""
-msgstr "compilation de la fonction PL/Perl %s "
+msgstr "compilation de la fonction PL/Perl « %s »"
#: plperl.c:3975
#, c-format
@@ -244,13 +244,13 @@ msgstr "bloc de code PL/Perl anonyme"
#~ msgid "composite-returning PL/Perl function must return reference to hash"
#~ msgstr ""
#~ "la fonction PL/perl renvoyant des valeurs composites doit renvoyer la\n"
-#~ "rfrence un hachage"
+#~ "référence à un hachage"
#~ msgid "while executing PLC_SAFE_OK"
-#~ msgstr "lors de l'excution de PLC_SAFE_OK"
+#~ msgstr "lors de l'exécution de PLC_SAFE_OK"
#~ msgid "creation of Perl function \"%s\" failed: %s"
-#~ msgstr "chec de la cration de la fonction Perl %s : %s"
+#~ msgstr "échec de la création de la fonction Perl « %s » : %s"
#~ msgid "error from Perl function \"%s\": %s"
-#~ msgstr "chec dans la fonction Perl %s : %s"
+#~ msgstr "échec dans la fonction Perl « %s » : %s"
diff --git a/src/pl/plperl/po/ko.po b/src/pl/plperl/po/ko.po
index dc5a8b92d9..2f32d9fcbf 100644
--- a/src/pl/plperl/po/ko.po
+++ b/src/pl/plperl/po/ko.po
@@ -5,225 +5,230 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: plperl (PostgreSQL) 9.5\n"
+"Project-Id-Version: plperl (PostgreSQL) 9.6\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2016-02-01 10:02+0900\n"
-"PO-Revision-Date: 2016-02-01 10:58+0900\n"
+"POT-Creation-Date: 2016-09-26 14:02+0900\n"
+"PO-Revision-Date: 2016-09-26 18:54+0900\n"
"Last-Translator: Ioseph Kim <ioseph@uri.sarang.net>\n"
"Language-Team: Korean Team <pgsql-kr@postgresql.kr>\n"
+"Language: ko\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: ko\n"
-#: plperl.c:404
+#: plperl.c:405
msgid ""
"If true, trusted and untrusted Perl code will be compiled in strict mode."
msgstr "true로 지정하면, Perl 코드가 엄격한 구문 검사로 컴파일 됨"
-#: plperl.c:418
+#: plperl.c:419
msgid ""
"Perl initialization code to execute when a Perl interpreter is initialized."
msgstr "Perl 인터프리터가 초기화 될 때 실행할 Perl 초기화 코드"
-#: plperl.c:440
+#: plperl.c:441
msgid "Perl initialization code to execute once when plperl is first used."
msgstr "plperl 모듈이 처음 사용될 때 실행할 Perl 초기화 코드"
-#: plperl.c:448
+#: plperl.c:449
msgid "Perl initialization code to execute once when plperlu is first used."
msgstr "plperlu 모듈이 처음 사용될 때 실행할 Perl 초기화 코드"
-#: plperl.c:645
+#: plperl.c:646
#, c-format
msgid "cannot allocate multiple Perl interpreters on this platform"
msgstr "이 플랫폼에 여러 Perl 인터프리터를 사용할 수 없음"
-#: plperl.c:665 plperl.c:840 plperl.c:846 plperl.c:960 plperl.c:972
-#: plperl.c:1015 plperl.c:1036 plperl.c:2068 plperl.c:2177 plperl.c:2244
-#: plperl.c:2306
+#: plperl.c:666 plperl.c:841 plperl.c:847 plperl.c:961 plperl.c:973
+#: plperl.c:1016 plperl.c:1037 plperl.c:2080 plperl.c:2189 plperl.c:2256
+#: plperl.c:2318
#, c-format
msgid "%s"
msgstr "%s"
-#: plperl.c:666
+#: plperl.c:667
#, c-format
msgid "while executing PostgreSQL::InServer::SPI::bootstrap"
msgstr "PostgreSQL::InServer::SPI::bootstrap 실행 중"
-#: plperl.c:841
+#: plperl.c:842
#, c-format
msgid "while parsing Perl initialization"
msgstr "Perl 초기화 구문 분석 중"
-#: plperl.c:847
+#: plperl.c:848
#, c-format
msgid "while running Perl initialization"
msgstr "Perl 초기화 실행 중"
-#: plperl.c:961
+#: plperl.c:962
#, c-format
msgid "while executing PLC_TRUSTED"
msgstr "PLC_TRUSTED 실행 중"
-#: plperl.c:973
+#: plperl.c:974
#, c-format
msgid "while executing utf8fix"
msgstr "utf8fix 실행 중"
-#: plperl.c:1016
+#: plperl.c:1017
#, c-format
msgid "while executing plperl.on_plperl_init"
msgstr "plperl.on_plperl_init 실행 중"
-#: plperl.c:1037
+#: plperl.c:1038
#, c-format
msgid "while executing plperl.on_plperlu_init"
msgstr "plperl.on_plperlu_init 실행 중"
-#: plperl.c:1081 plperl.c:1710
+#: plperl.c:1082 plperl.c:1722
#, c-format
msgid "Perl hash contains nonexistent column \"%s\""
msgstr "Perl 해시에 존재하지 않는 \"%s\" 칼럼이 포함되었습니다"
-#: plperl.c:1166
+#: plperl.c:1167
#, c-format
msgid "number of array dimensions (%d) exceeds the maximum allowed (%d)"
msgstr "지정한 배열 크기(%d)가 최대치(%d)를 초과했습니다"
-#: plperl.c:1178 plperl.c:1195
+#: plperl.c:1179 plperl.c:1196
#, c-format
msgid ""
"multidimensional arrays must have array expressions with matching dimensions"
msgstr "다차원 배열에는 일치하는 차원이 포함된 배열 식이 있어야 함"
-#: plperl.c:1230
+#: plperl.c:1231
#, c-format
msgid "cannot convert Perl array to non-array type %s"
msgstr "Perl 배열형을 비배열형 %s 자료형으로 변환할 수 없음"
-#: plperl.c:1332
+#: plperl.c:1333
#, c-format
msgid "cannot convert Perl hash to non-composite type %s"
msgstr "Perl 해시 자료형을 비복합 %s 자료형으로 변환할 수 없음"
-#: plperl.c:1343
+#: plperl.c:1344
#, c-format
msgid ""
"function returning record called in context that cannot accept type record"
msgstr "반환 자료형이 record인데 함수가 그 자료형으로 반환하지 않음"
-#: plperl.c:1358
+#: plperl.c:1359
#, c-format
msgid "PL/Perl function must return reference to hash or array"
msgstr "PL/Perl 함수는 해시나 배열 자료형을 참조하게 반환해야 함"
-#: plperl.c:1395
+#: plperl.c:1396
#, c-format
msgid "lookup failed for type %s"
msgstr "%s 자료형 찾기 실패"
-#: plperl.c:1687
+#: plperl.c:1699
#, c-format
msgid "$_TD->{new} does not exist"
msgstr "$_TD->{new} 없음"
-#: plperl.c:1691
+#: plperl.c:1703
#, c-format
msgid "$_TD->{new} is not a hash reference"
msgstr "$_TD->{new} 자료형이 해시 참조가 아님"
-#: plperl.c:1944 plperl.c:2778
+#: plperl.c:1956 plperl.c:2790
#, c-format
msgid "PL/Perl functions cannot return type %s"
msgstr "PL/Perl 함수는 %s 자료형을 반환할 수 없음"
-#: plperl.c:1957 plperl.c:2823
+#: plperl.c:1969 plperl.c:2835
#, c-format
msgid "PL/Perl functions cannot accept type %s"
msgstr "PL/Perl 함수는 %s 자료형을 사용할 수 없음"
-#: plperl.c:2073
+#: plperl.c:2085
#, c-format
msgid "didn't get a CODE reference from compiling function \"%s\""
msgstr "\"%s\" 함수를 컴파일 하면서 코드 참조를 구할 수 없음"
-#: plperl.c:2165
+#: plperl.c:2177
#, c-format
msgid "didn't get a return item from function"
msgstr "함수에서 반환할 항목을 못 찾음"
-#: plperl.c:2208 plperl.c:2274
+#: plperl.c:2220 plperl.c:2286
#, c-format
msgid "couldn't fetch $_TD"
msgstr "$_TD 못 구함"
-#: plperl.c:2232 plperl.c:2294
+#: plperl.c:2244 plperl.c:2306
#, c-format
msgid "didn't get a return item from trigger function"
msgstr "트리거 함수에서 반환할 항목을 못 찾음"
-#: plperl.c:2351
+#: plperl.c:2363
#, c-format
msgid "set-valued function called in context that cannot accept a set"
msgstr ""
"set-values 함수(테이블 리턴 함수)가 set 정의 없이 사용되었습니다 (테이블과 해"
"당 열 alias 지정하세요)"
-#: plperl.c:2395
+#: plperl.c:2407
#, c-format
msgid ""
"set-returning PL/Perl function must return reference to array or use "
"return_next"
-msgstr ""
-"집합 반환 PL/Perl 함수는 배열 또는 return_next 를 사용해서 반환해야 함"
+msgstr "집합 반환 PL/Perl 함수는 배열 또는 return_next 를 사용해서 반환해야 함"
-#: plperl.c:2509
+#: plperl.c:2521
#, c-format
msgid "ignoring modified row in DELETE trigger"
msgstr "DELETE 트리거에서는 변경된 로우는 무시 함"
-#: plperl.c:2517
+#: plperl.c:2529
#, c-format
msgid ""
"result of PL/Perl trigger function must be undef, \"SKIP\", or \"MODIFY\""
-msgstr "PL/Perl 트리거 함수의 결과는 undef, \"SKIP\", \"MODIFY\" 중 하나여야 함"
+msgstr ""
+"PL/Perl 트리거 함수의 결과는 undef, \"SKIP\", \"MODIFY\" 중 하나여야 함"
-#: plperl.c:2696 plperl.c:2706
+#: plperl.c:2708 plperl.c:2718
#, c-format
msgid "out of memory"
msgstr "메모리 부족"
-#: plperl.c:2770
+#: plperl.c:2782
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "트리거 함수는 트리거로만 호출될 수 있음"
-#: plperl.c:3146
+#: plperl.c:3121
+#, c-format
+msgid "query result has too many rows to fit in a Perl array"
+msgstr "쿼리 결과가 Perl 배열에 담기에는 너무 많습니다"
+
+#: plperl.c:3166
#, c-format
msgid "cannot use return_next in a non-SETOF function"
msgstr "SETOF 함수가 아닌 경우에는 return_next 구문을 쓸 수 없음"
-#: plperl.c:3202
+#: plperl.c:3220
#, c-format
msgid ""
"SETOF-composite-returning PL/Perl function must call return_next with "
"reference to hash"
msgstr ""
-"SETOF-composite-returning PL/Perl 함수는 return_next 에서"
-" 해시 자료형을 참조해야 함"
+"SETOF-composite-returning PL/Perl 함수는 return_next 에서 해시 자료형을 참조"
+"해야 함"
-#: plperl.c:3936
+#: plperl.c:3948
#, c-format
msgid "PL/Perl function \"%s\""
msgstr "\"%s\" PL/Perl 함수"
-#: plperl.c:3948
+#: plperl.c:3960
#, c-format
msgid "compilation of PL/Perl function \"%s\""
msgstr "\"%s\" PL/Perl 함수 컴필레이션"
-#: plperl.c:3957
+#: plperl.c:3969
#, c-format
msgid "PL/Perl anonymous code block"
msgstr "PL/Perl 익명 코드 블럭"
diff --git a/src/pl/plperl/po/pl.po b/src/pl/plperl/po/pl.po
index 6c5948f647..d0dd146eb5 100644
--- a/src/pl/plperl/po/pl.po
+++ b/src/pl/plperl/po/pl.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: plperl (PostgreSQL 9.1)\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2016-07-03 03:07+0000\n"
+"POT-Creation-Date: 2017-04-09 21:07+0000\n"
"PO-Revision-Date: 2016-07-03 18:04+0200\n"
"Last-Translator: grzegorz <begina.felicysym@wp.eu>\n"
"Language-Team: begina.felicysym@wp.eu\n"
@@ -15,211 +15,211 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
-"|| n%100>=20) ? 1 : 2);\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Virtaal 0.7.1\n"
-#: plperl.c:405
+#: plperl.c:390
msgid "If true, trusted and untrusted Perl code will be compiled in strict mode."
-msgstr ""
-"Jeśli prawda, zaufanych i niezaufanych kod Perl zostanie skompilowany w "
-"trybie ścisłym."
+msgstr "Jeśli prawda, zaufanych i niezaufanych kod Perl zostanie skompilowany w trybie ścisłym."
-#: plperl.c:419
+#: plperl.c:404
msgid "Perl initialization code to execute when a Perl interpreter is initialized."
msgstr "Kod inicjujący Perl do wykonania gdy inicjowany jest interpreter Perl."
-#: plperl.c:441
+#: plperl.c:426
msgid "Perl initialization code to execute once when plperl is first used."
msgstr "Kod inicjujący Perl do jednokrotnego wykonania gdy plperl jest użyty po raz pierwszy."
-#: plperl.c:449
+#: plperl.c:434
msgid "Perl initialization code to execute once when plperlu is first used."
msgstr "Kod inicjujący Perl do jednokrotnego wykonania gdy plperlu jest użyty po raz pierwszy."
-#: plperl.c:646
+#: plperl.c:631
#, c-format
msgid "cannot allocate multiple Perl interpreters on this platform"
msgstr "nie można przydzielić wielu interpreterów Perl na tej platformie"
-#: plperl.c:666 plperl.c:841 plperl.c:847 plperl.c:961 plperl.c:973
-#: plperl.c:1016 plperl.c:1037 plperl.c:2080 plperl.c:2189 plperl.c:2256
-#: plperl.c:2318
+#: plperl.c:651 plperl.c:826 plperl.c:832 plperl.c:946 plperl.c:958
+#: plperl.c:1001 plperl.c:1022 plperl.c:2074 plperl.c:2183 plperl.c:2250
+#: plperl.c:2312
#, c-format
msgid "%s"
msgstr "%s"
-#: plperl.c:667
+#: plperl.c:652
#, c-format
msgid "while executing PostgreSQL::InServer::SPI::bootstrap"
msgstr "podczas wykonania PostgreSQL::InServer::SPI::bootstrap"
-#: plperl.c:842
+#: plperl.c:827
#, c-format
msgid "while parsing Perl initialization"
msgstr "podczas przetwarzania inicjacji Perl"
-#: plperl.c:848
+#: plperl.c:833
#, c-format
msgid "while running Perl initialization"
msgstr "podczas wykonywania inicjacji Perl"
-#: plperl.c:962
+#: plperl.c:947
#, c-format
msgid "while executing PLC_TRUSTED"
msgstr "podczas wykonywania PLC_TRUSTED"
-#: plperl.c:974
+#: plperl.c:959
#, c-format
msgid "while executing utf8fix"
msgstr "podczas wykonywania utf8fix"
-#: plperl.c:1017
+#: plperl.c:1002
#, c-format
msgid "while executing plperl.on_plperl_init"
msgstr "podczas wykonania plperl.on_plperl_init"
-#: plperl.c:1038
+#: plperl.c:1023
#, c-format
msgid "while executing plperl.on_plperlu_init"
msgstr "podczas wykonania plperl.on_plperlu_init"
-#: plperl.c:1082 plperl.c:1722
+#: plperl.c:1067 plperl.c:1719
#, c-format
msgid "Perl hash contains nonexistent column \"%s\""
msgstr "hasz Perl zawiera nieistniejącą kolumnę \"%s\""
-#: plperl.c:1167
+#: plperl.c:1072 plperl.c:1724
+#, c-format
+msgid "cannot set system attribute \"%s\""
+msgstr "nie można ustawić atrybutu systemowego \"%s\""
+
+#: plperl.c:1157
#, c-format
msgid "number of array dimensions (%d) exceeds the maximum allowed (%d)"
msgstr "liczba wymiarów tablicy (%d) przekracza maksimum (%d)"
-#: plperl.c:1179 plperl.c:1196
+#: plperl.c:1169 plperl.c:1186
#, c-format
msgid "multidimensional arrays must have array expressions with matching dimensions"
msgstr "wielowymiarowe tablice muszą mieć wyrażenia tablicowe z pasującymi wymiarami"
-#: plperl.c:1231
+#: plperl.c:1221
#, c-format
msgid "cannot convert Perl array to non-array type %s"
msgstr "nie można zmienić typu tablicowego Perl na typ nietablicowy %s"
-#: plperl.c:1333
+#: plperl.c:1323
#, c-format
msgid "cannot convert Perl hash to non-composite type %s"
msgstr "nie można przekształcić Perlowego hasza na typ niezłożony %s"
-#: plperl.c:1344
+#: plperl.c:1334
#, c-format
msgid "function returning record called in context that cannot accept type record"
msgstr "funkcja zwracająca rekord w wywołaniu, które nie akceptuje typów złożonych"
-#: plperl.c:1359
+#: plperl.c:1349
#, c-format
msgid "PL/Perl function must return reference to hash or array"
msgstr "funkcja PL/Perl musi zwracać referencję do hasza lub tablicy"
-#: plperl.c:1396
+#: plperl.c:1386
#, c-format
msgid "lookup failed for type %s"
msgstr "nie dało się wyszukać typu %s"
-#: plperl.c:1699
+#: plperl.c:1695
#, c-format
msgid "$_TD->{new} does not exist"
msgstr "$_TD->{new} nie istnieje"
-#: plperl.c:1703
+#: plperl.c:1699
#, c-format
msgid "$_TD->{new} is not a hash reference"
msgstr "$_TD->{new} nie jest referencją haszu"
-#: plperl.c:1956 plperl.c:2790
+#: plperl.c:1950 plperl.c:2785
#, c-format
msgid "PL/Perl functions cannot return type %s"
msgstr "funkcje PL/Perl nie mogą zwracać wartości typu %s"
-#: plperl.c:1969 plperl.c:2835
+#: plperl.c:1963 plperl.c:2827
#, c-format
msgid "PL/Perl functions cannot accept type %s"
msgstr "funkcje PL/Perl nie obsługują typu %s"
-#: plperl.c:2085
+#: plperl.c:2079
#, c-format
msgid "didn't get a CODE reference from compiling function \"%s\""
msgstr "nie udało się pobrać wskazania CODE z kompilowanej funkcji \"%s\""
-#: plperl.c:2177
+#: plperl.c:2171
#, c-format
msgid "didn't get a return item from function"
msgstr "nie odebrano zwracanego elementu z funkcji"
-#: plperl.c:2220 plperl.c:2286
+#: plperl.c:2214 plperl.c:2280
#, c-format
msgid "couldn't fetch $_TD"
msgstr "nie dało się pobrać $_TD"
-#: plperl.c:2244 plperl.c:2306
+#: plperl.c:2238 plperl.c:2300
#, c-format
msgid "didn't get a return item from trigger function"
msgstr "nie odebrano zwracanego elementu z funkcji wyzwalacza"
-#: plperl.c:2363
+#: plperl.c:2357
#, c-format
msgid "set-valued function called in context that cannot accept a set"
msgstr "funkcja zwracająca zbiór rekordów wywołana w kontekście, w którym nie jest to dopuszczalne"
-#: plperl.c:2407
+#: plperl.c:2401
#, c-format
msgid "set-returning PL/Perl function must return reference to array or use return_next"
msgstr "funkcja PL/Perl zwracająca zbiór rekordów musi zwracać tablicę lub użyć return_next"
-#: plperl.c:2521
+#: plperl.c:2522
#, c-format
msgid "ignoring modified row in DELETE trigger"
msgstr "ignorowanie modyfikacji wiersza w wyzwalaczy DELETE"
-#: plperl.c:2529
+#: plperl.c:2530
#, c-format
msgid "result of PL/Perl trigger function must be undef, \"SKIP\", or \"MODIFY\""
msgstr "funkcja wyzwalacza PL/Perl musi zwracać undef, \"SKIP\", lub \"MODIFY\""
-#: plperl.c:2708 plperl.c:2718
-#, c-format
-msgid "out of memory"
-msgstr "brak pamięci"
-
-#: plperl.c:2782
+#: plperl.c:2780
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "procedury wyzwalaczy mogą być wywoływane jedynie przez wyzwalacze"
-#: plperl.c:3121
+#: plperl.c:3120
#, c-format
msgid "query result has too many rows to fit in a Perl array"
msgstr "wynik zapytania ma za dużo wierszy by pomieścić w tabeli Perl"
-#: plperl.c:3166
+#: plperl.c:3165
#, c-format
msgid "cannot use return_next in a non-SETOF function"
msgstr "nie można używać return_next w funkcji nie SETOF"
-#: plperl.c:3222
+#: plperl.c:3219
#, c-format
msgid "SETOF-composite-returning PL/Perl function must call return_next with reference to hash"
msgstr "funkcja PL/Perl zwracająca grupę wartości złożonych musi wywołać return_next z referencją haszu"
-#: plperl.c:3954
+#: plperl.c:3882
#, c-format
msgid "PL/Perl function \"%s\""
msgstr "funkcja PL/Perl \"%s\""
-#: plperl.c:3966
+#: plperl.c:3894
#, c-format
msgid "compilation of PL/Perl function \"%s\""
msgstr "kompilacja funkcji PL/Perl \"%s\""
-#: plperl.c:3975
+#: plperl.c:3903
#, c-format
msgid "PL/Perl anonymous code block"
msgstr "anonimowy blok kodu PL/Perl"
+
+#~ msgid "out of memory"
+#~ msgstr "brak pamięci"
diff --git a/src/pl/plperl/po/pt_BR.po b/src/pl/plperl/po/pt_BR.po
index a4e63fa03a..ff6a1f8686 100644
--- a/src/pl/plperl/po/pt_BR.po
+++ b/src/pl/plperl/po/pt_BR.po
@@ -1,13 +1,13 @@
# Brazilian Portuguese message translation file for plperl
# Copyright (C) 2009 PostgreSQL Global Development Group
# This file is distributed under the same license as the PostgreSQL package.
-# Euler Taveira de Oliveira <euler@timbira.com>, 2009-2015.
+# Euler Taveira de Oliveira <euler@timbira.com>, 2009-2016.
#
msgid ""
msgstr ""
-"Project-Id-Version: PostgreSQL 9.5\n"
+"Project-Id-Version: PostgreSQL 9.6\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2015-09-17 22:32-0300\n"
+"POT-Creation-Date: 2016-08-09 22:53-0300\n"
"PO-Revision-Date: 2009-05-10 01:12-0300\n"
"Last-Translator: Euler Taveira de Oliveira <euler@timbira.com>\n"
"Language-Team: Brazilian Portuguese <pgbr-dev@listas.postgresql.org.br>\n"
@@ -16,200 +16,205 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: plperl.c:404
+#: plperl.c:405
msgid "If true, trusted and untrusted Perl code will be compiled in strict mode."
msgstr "Se verdadeiro, código Perl confiável e não-confiável será compilado em modo estrito."
-#: plperl.c:418
+#: plperl.c:419
msgid "Perl initialization code to execute when a Perl interpreter is initialized."
msgstr "Código de inicialização Perl executado quando um interpretador Perl for inicializado."
-#: plperl.c:440
+#: plperl.c:441
msgid "Perl initialization code to execute once when plperl is first used."
msgstr "Código de inicialização Perl executado quando plperl for utilizado pela primeira vez."
-#: plperl.c:448
+#: plperl.c:449
msgid "Perl initialization code to execute once when plperlu is first used."
msgstr "Código de inicialização Perl executado quando plperlu for utilizado pela primeira vez."
-#: plperl.c:645
+#: plperl.c:646
#, c-format
msgid "cannot allocate multiple Perl interpreters on this platform"
msgstr "não pode alocar múltiplos interpretadores Perl nessa plataforma"
-#: plperl.c:665 plperl.c:840 plperl.c:846 plperl.c:960 plperl.c:972
-#: plperl.c:1015 plperl.c:1036 plperl.c:2068 plperl.c:2175 plperl.c:2242
-#: plperl.c:2304
+#: plperl.c:666 plperl.c:841 plperl.c:847 plperl.c:961 plperl.c:973
+#: plperl.c:1016 plperl.c:1037 plperl.c:2080 plperl.c:2189 plperl.c:2256
+#: plperl.c:2318
#, c-format
msgid "%s"
msgstr "%s"
-#: plperl.c:666
+#: plperl.c:667
#, c-format
msgid "while executing PostgreSQL::InServer::SPI::bootstrap"
msgstr "ao executar PostgreSQL::InServer::SPI::bootstrap"
-#: plperl.c:841
+#: plperl.c:842
#, c-format
msgid "while parsing Perl initialization"
msgstr "ao analisar código de inicialização Perl"
-#: plperl.c:847
+#: plperl.c:848
#, c-format
msgid "while running Perl initialization"
msgstr "ao executar código de inicialização Perl"
-#: plperl.c:961
+#: plperl.c:962
#, c-format
msgid "while executing PLC_TRUSTED"
msgstr "ao executar PLC_TRUSTED"
-#: plperl.c:973
+#: plperl.c:974
#, c-format
msgid "while executing utf8fix"
msgstr "ao executar utf8fix"
-#: plperl.c:1016
+#: plperl.c:1017
#, c-format
msgid "while executing plperl.on_plperl_init"
msgstr "ao executar plperl.on_plperl_init"
-#: plperl.c:1037
+#: plperl.c:1038
#, c-format
msgid "while executing plperl.on_plperlu_init"
msgstr "ao executar plperl.on_plperlu_init"
-#: plperl.c:1081 plperl.c:1710
+#: plperl.c:1082 plperl.c:1722
#, c-format
msgid "Perl hash contains nonexistent column \"%s\""
msgstr "hash Perl contém coluna inexistente \"%s\""
-#: plperl.c:1166
+#: plperl.c:1167
#, c-format
msgid "number of array dimensions (%d) exceeds the maximum allowed (%d)"
msgstr "número de dimensões da matriz (%d) excede o máximo permitido (%d)"
-#: plperl.c:1178 plperl.c:1195
+#: plperl.c:1179 plperl.c:1196
#, c-format
msgid "multidimensional arrays must have array expressions with matching dimensions"
msgstr "matrizes multidimensionais devem ter expressões de matriz com dimensões correspondentes"
-#: plperl.c:1230
+#: plperl.c:1231
#, c-format
msgid "cannot convert Perl array to non-array type %s"
msgstr "não pode converter array Perl para tipo que não é array %s"
-#: plperl.c:1332
+#: plperl.c:1333
#, c-format
msgid "cannot convert Perl hash to non-composite type %s"
msgstr "não pode converter hash Perl para tipo não-composto %s"
-#: plperl.c:1343
+#: plperl.c:1344
#, c-format
msgid "function returning record called in context that cannot accept type record"
msgstr "função que retorna record foi chamada em um contexto que não pode aceitar tipo record"
-#: plperl.c:1358
+#: plperl.c:1359
#, c-format
msgid "PL/Perl function must return reference to hash or array"
msgstr "função PL/Perl deve retornar referência a um hash ou uma matriz"
-#: plperl.c:1395
+#: plperl.c:1396
#, c-format
msgid "lookup failed for type %s"
msgstr "falhou ao pesquisar por tipo %s"
-#: plperl.c:1687
+#: plperl.c:1699
#, c-format
msgid "$_TD->{new} does not exist"
msgstr "$_TD->{new} não existe"
-#: plperl.c:1691
+#: plperl.c:1703
#, c-format
msgid "$_TD->{new} is not a hash reference"
msgstr "$_TD->{new} não é uma referência hash"
-#: plperl.c:1944 plperl.c:2776
+#: plperl.c:1956 plperl.c:2790
#, c-format
msgid "PL/Perl functions cannot return type %s"
msgstr "funções PL/Perl não podem retornar tipo %s"
-#: plperl.c:1957 plperl.c:2821
+#: plperl.c:1969 plperl.c:2835
#, c-format
msgid "PL/Perl functions cannot accept type %s"
msgstr "funções PL/Perl não podem aceitar tipo %s"
-#: plperl.c:2073
+#: plperl.c:2085
#, c-format
msgid "didn't get a CODE reference from compiling function \"%s\""
msgstr "não obteve uma referência CODE da compilação da função \"%s\""
-#: plperl.c:2163
+#: plperl.c:2177
#, c-format
msgid "didn't get a return item from function"
msgstr "não obteve um item de retorno da função"
-#: plperl.c:2206 plperl.c:2272
+#: plperl.c:2220 plperl.c:2286
#, c-format
msgid "couldn't fetch $_TD"
msgstr "não pôde obter $_TD"
-#: plperl.c:2230 plperl.c:2292
+#: plperl.c:2244 plperl.c:2306
#, c-format
msgid "didn't get a return item from trigger function"
msgstr "não obteve um item de retorno da função de gatilho"
-#: plperl.c:2349
+#: plperl.c:2363
#, c-format
msgid "set-valued function called in context that cannot accept a set"
msgstr "função que tem argumento do tipo conjunto foi chamada em um contexto que não pode aceitar um conjunto"
-#: plperl.c:2393
+#: plperl.c:2407
#, c-format
msgid "set-returning PL/Perl function must return reference to array or use return_next"
msgstr "funçao PL/Perl que retorna conjunto deve retornar referência para matriz ou usar return_next"
-#: plperl.c:2507
+#: plperl.c:2521
#, c-format
msgid "ignoring modified row in DELETE trigger"
msgstr "ignorando registro modificado em gatilho DELETE"
-#: plperl.c:2515
+#: plperl.c:2529
#, c-format
msgid "result of PL/Perl trigger function must be undef, \"SKIP\", or \"MODIFY\""
msgstr "resultado da função de gatilho PL/Perl deve ser undef, \"SKIP\" ou \"MODIFY\""
-#: plperl.c:2694 plperl.c:2704
+#: plperl.c:2708 plperl.c:2718
#, c-format
msgid "out of memory"
msgstr "sem memória"
-#: plperl.c:2768
+#: plperl.c:2782
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "funções de gatilho só podem ser chamadas como gatilhos"
-#: plperl.c:3144
+#: plperl.c:3121
+#, c-format
+msgid "query result has too many rows to fit in a Perl array"
+msgstr "resultado da consulta tem muitos registros para caber em um array Perl"
+
+#: plperl.c:3166
#, c-format
msgid "cannot use return_next in a non-SETOF function"
msgstr "não pode utilizar return_next em uma função que não retorna conjunto"
-#: plperl.c:3200
+#: plperl.c:3222
#, c-format
msgid "SETOF-composite-returning PL/Perl function must call return_next with reference to hash"
msgstr "função PL/Perl que retorna um conjunto de tipo composto deve chamar return_next com referência a um hash"
-#: plperl.c:3934
+#: plperl.c:3954
#, c-format
msgid "PL/Perl function \"%s\""
msgstr "função PL/Perl \"%s\""
-#: plperl.c:3946
+#: plperl.c:3966
#, c-format
msgid "compilation of PL/Perl function \"%s\""
msgstr "compilação da função PL/Perl \"%s\""
-#: plperl.c:3955
+#: plperl.c:3975
#, c-format
msgid "PL/Perl anonymous code block"
msgstr "bloco de código PL/Perl anônimo"
diff --git a/src/pl/plperl/po/ru.po b/src/pl/plperl/po/ru.po
index 8120e9fe7f..ba7e6b7a7d 100644
--- a/src/pl/plperl/po/ru.po
+++ b/src/pl/plperl/po/ru.po
@@ -1,111 +1,112 @@
# Russian message translation file for plperl
-# Copyright (C) 2012 PostgreSQL Global Development Group
+# Copyright (C) 2012-2016 PostgreSQL Global Development Group
# This file is distributed under the same license as the PostgreSQL package.
-# Alexander Lakhin <exclusion@gmail.com>, 2012.
+# Alexander Lakhin <exclusion@gmail.com>, 2012-2017.
#
-# ChangeLog:
-# - April 2, 2012: Bug fixes. Alexander Lakhin <exclusion@gmail.com>.
-# - February 18, 2012: Complete translation for 9.1. Alexander Lakhin <exclusion@gmail.com>.
msgid ""
msgstr ""
-"Project-Id-Version: PostgreSQL 9.2\n"
+"Project-Id-Version: plperl (PostgreSQL current)\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2016-05-27 16:07+0000\n"
-"PO-Revision-Date: 2015-10-16 21:10+0400\n"
-"Last-Translator: Alexander Lakhin <exclusion@gmail.com>\n"
-"Language-Team: Russian <pgsql-translators@postgresql.org>\n"
+"POT-Creation-Date: 2017-03-27 12:37+0000\n"
+"PO-Revision-Date: 2017-03-29 13:41+0300\n"
+"Language-Team: Russian <pgsql-ru-general@postgresql.org>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-"X-Generator: Lokalize 2.0\n"
+"Last-Translator: Alexander Lakhin <exclusion@gmail.com>\n"
-#: plperl.c:405
+#: plperl.c:390
msgid ""
"If true, trusted and untrusted Perl code will be compiled in strict mode."
msgstr ""
"Если этот параметр равен true, доверенный и недоверенный код Perl будет "
"компилироваться в строгом режиме."
-#: plperl.c:419
+#: plperl.c:404
msgid ""
"Perl initialization code to execute when a Perl interpreter is initialized."
msgstr ""
"Код инициализации Perl, который выполняется при инициализации интерпретатора "
"Perl."
-#: plperl.c:441
+#: plperl.c:426
msgid "Perl initialization code to execute once when plperl is first used."
msgstr ""
"Код инициализации Perl, который выполняется один раз, при первом "
"использовании plperl."
-#: plperl.c:449
+#: plperl.c:434
msgid "Perl initialization code to execute once when plperlu is first used."
msgstr ""
"Код инициализации Perl, который выполняется один раз, при первом "
"использовании plperlu."
-#: plperl.c:646
+#: plperl.c:631
#, c-format
msgid "cannot allocate multiple Perl interpreters on this platform"
msgstr "на этой платформе нельзя запустить множество интерпретаторов Perl"
-#: plperl.c:666 plperl.c:841 plperl.c:847 plperl.c:961 plperl.c:973
-#: plperl.c:1016 plperl.c:1037 plperl.c:2080 plperl.c:2189 plperl.c:2256
-#: plperl.c:2318
+#: plperl.c:651 plperl.c:826 plperl.c:832 plperl.c:946 plperl.c:958
+#: plperl.c:1001 plperl.c:1022 plperl.c:2074 plperl.c:2183 plperl.c:2250
+#: plperl.c:2312
#, c-format
msgid "%s"
msgstr "%s"
-#: plperl.c:667
+#: plperl.c:652
#, c-format
msgid "while executing PostgreSQL::InServer::SPI::bootstrap"
msgstr "при выполнении PostgreSQL::InServer::SPI::bootstrap"
-#: plperl.c:842
+#: plperl.c:827
#, c-format
msgid "while parsing Perl initialization"
msgstr "при разборе параметров инициализации Perl"
-#: plperl.c:848
+#: plperl.c:833
#, c-format
msgid "while running Perl initialization"
msgstr "при выполнении инициализации Perl"
-#: plperl.c:962
+#: plperl.c:947
#, c-format
msgid "while executing PLC_TRUSTED"
msgstr "при выполнении PLC_TRUSTED"
-#: plperl.c:974
+#: plperl.c:959
#, c-format
msgid "while executing utf8fix"
msgstr "при выполнении utf8fix"
-#: plperl.c:1017
+#: plperl.c:1002
#, c-format
msgid "while executing plperl.on_plperl_init"
msgstr "при выполнении plperl.on_plperl_init"
-#: plperl.c:1038
+#: plperl.c:1023
#, c-format
msgid "while executing plperl.on_plperlu_init"
msgstr "при выполнении plperl.on_plperlu_init"
-#: plperl.c:1082 plperl.c:1722
+#: plperl.c:1067 plperl.c:1719
#, c-format
msgid "Perl hash contains nonexistent column \"%s\""
msgstr "Perl-хеш содержит несуществующий столбец \"%s\""
-#: plperl.c:1167
+#: plperl.c:1072 plperl.c:1724
+#, c-format
+msgid "cannot set system attribute \"%s\""
+msgstr "установить системный атрибут \"%s\" нельзя"
+
+#: plperl.c:1157
#, c-format
msgid "number of array dimensions (%d) exceeds the maximum allowed (%d)"
msgstr "число размерностей массива (%d) превышает предел (%d)"
-#: plperl.c:1179 plperl.c:1196
+#: plperl.c:1169 plperl.c:1186
#, c-format
msgid ""
"multidimensional arrays must have array expressions with matching dimensions"
@@ -113,80 +114,80 @@ msgstr ""
"для многомерных массивов должны задаваться выражения с соответствующими "
"размерностями"
-#: plperl.c:1231
+#: plperl.c:1221
#, c-format
msgid "cannot convert Perl array to non-array type %s"
msgstr "Perl-массив нельзя преобразовать в тип не массива %s"
-#: plperl.c:1333
+#: plperl.c:1323
#, c-format
msgid "cannot convert Perl hash to non-composite type %s"
msgstr "Perl-хеш нельзя преобразовать в не составной тип %s"
-#: plperl.c:1344
+#: plperl.c:1334
#, c-format
msgid ""
"function returning record called in context that cannot accept type record"
msgstr ""
"функция, возвращающая запись, вызвана в контексте, не допускающем этот тип"
-#: plperl.c:1359
+#: plperl.c:1349
#, c-format
msgid "PL/Perl function must return reference to hash or array"
msgstr "функция PL/Perl должна возвращать ссылку на хеш или массив"
-#: plperl.c:1396
+#: plperl.c:1386
#, c-format
msgid "lookup failed for type %s"
msgstr "найти тип %s не удалось"
-#: plperl.c:1699
+#: plperl.c:1695
#, c-format
msgid "$_TD->{new} does not exist"
msgstr "$_TD->{new} не существует"
-#: plperl.c:1703
+#: plperl.c:1699
#, c-format
msgid "$_TD->{new} is not a hash reference"
msgstr "$_TD->{new} - не ссылка на хеш"
-#: plperl.c:1956 plperl.c:2790
+#: plperl.c:1950 plperl.c:2778
#, c-format
msgid "PL/Perl functions cannot return type %s"
msgstr "функции PL/Perl не могут возвращать тип %s"
-#: plperl.c:1969 plperl.c:2835
+#: plperl.c:1963 plperl.c:2820
#, c-format
msgid "PL/Perl functions cannot accept type %s"
msgstr "функции PL/Perl не могут принимать тип %s"
-#: plperl.c:2085
+#: plperl.c:2079
#, c-format
msgid "didn't get a CODE reference from compiling function \"%s\""
msgstr "не удалось получить ссылку на код после компиляции функции \"%s\""
-#: plperl.c:2177
+#: plperl.c:2171
#, c-format
msgid "didn't get a return item from function"
msgstr "не удалось получить возвращаемый элемент от функции"
-#: plperl.c:2220 plperl.c:2286
+#: plperl.c:2214 plperl.c:2280
#, c-format
msgid "couldn't fetch $_TD"
msgstr "не удалось получить $_TD"
-#: plperl.c:2244 plperl.c:2306
+#: plperl.c:2238 plperl.c:2300
#, c-format
msgid "didn't get a return item from trigger function"
msgstr "не удалось получить возвращаемый элемент от триггерной функции"
-#: plperl.c:2363
+#: plperl.c:2357
#, c-format
msgid "set-valued function called in context that cannot accept a set"
msgstr ""
"функция, возвращающая множество, вызвана в контексте, где ему нет места"
-#: plperl.c:2407
+#: plperl.c:2401
#, c-format
msgid ""
"set-returning PL/Perl function must return reference to array or use "
@@ -195,12 +196,12 @@ msgstr ""
"функция PL/Perl, возвращающая множество, должна возвращать ссылку на массив "
"или вызывать return_next"
-#: plperl.c:2521
+#: plperl.c:2515
#, c-format
msgid "ignoring modified row in DELETE trigger"
msgstr "в триггере DELETE изменённая строка игнорируется"
-#: plperl.c:2529
+#: plperl.c:2523
#, c-format
msgid ""
"result of PL/Perl trigger function must be undef, \"SKIP\", or \"MODIFY\""
@@ -208,29 +209,24 @@ msgstr ""
"результатом триггерной функции PL/Perl должен быть undef, \"SKIP\" или "
"\"MODIFY\""
-#: plperl.c:2708 plperl.c:2718
-#, c-format
-msgid "out of memory"
-msgstr "нехватка памяти"
-
-#: plperl.c:2782
+#: plperl.c:2773
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "триггерные функции могут вызываться только в триггерах"
-#: plperl.c:3121
+#: plperl.c:3113
#, c-format
msgid "query result has too many rows to fit in a Perl array"
msgstr ""
"результат запроса содержит слишком много строк для передачи в массиве Perl"
-#: plperl.c:3166
+#: plperl.c:3158
#, c-format
msgid "cannot use return_next in a non-SETOF function"
msgstr ""
"return_next можно использовать только в функциях, возвращающих множества"
-#: plperl.c:3222
+#: plperl.c:3212
#, c-format
msgid ""
"SETOF-composite-returning PL/Perl function must call return_next with "
@@ -239,17 +235,20 @@ msgstr ""
"функция PL/Perl, возвращающая составное множество, должна вызывать "
"return_next со ссылкой на хеш"
-#: plperl.c:3954
+#: plperl.c:3875
#, c-format
msgid "PL/Perl function \"%s\""
msgstr "функция PL/Perl \"%s\""
-#: plperl.c:3966
+#: plperl.c:3887
#, c-format
msgid "compilation of PL/Perl function \"%s\""
msgstr "компиляция функции PL/Perl \"%s\""
-#: plperl.c:3975
+#: plperl.c:3896
#, c-format
msgid "PL/Perl anonymous code block"
msgstr "анонимный блок кода PL/Perl"
+
+#~ msgid "out of memory"
+#~ msgstr "нехватка памяти"
diff --git a/src/pl/plperl/ppport.h b/src/pl/plperl/ppport.h
index 5ea0c66e98..8c2365674f 100644
--- a/src/pl/plperl/ppport.h
+++ b/src/pl/plperl/ppport.h
@@ -79,7 +79,7 @@ to be installed on your system.
If this option is given, a copy of each file will be saved with
the given suffix that contains the suggested changes. This does
not require any external programs. Note that this does not
-automagially add a dot between the original filename and the
+automagically add a dot between the original filename and the
suffix. If you want the dot, you have to include it in the option
argument.
@@ -4364,9 +4364,9 @@ DPPP_(my_vload_module)(U32 flags, SV *name, SV *ver, va_list *args)
OP * const modname = newSVOP(OP_CONST, 0, name);
/* 5.005 has a somewhat hacky force_normal that doesn't croak on
- SvREADONLY() if PL_compling is true. Current perls take care in
+ SvREADONLY() if PL_compiling is true. Current perls take care in
ck_require() to correctly turn off SvREADONLY before calling
- force_normal_flags(). This seems a better fix than fudging PL_compling
+ force_normal_flags(). This seems a better fix than fudging PL_compiling
*/
SvREADONLY_off(((SVOP*)modname)->op_sv);
modname->op_private |= OPpCONST_BARE;
@@ -6205,10 +6205,10 @@ DPPP_(my_grok_number)(pTHX_ const char *pv, STRLEN len, UV *valuep)
/* UVs are at least 32 bits, so the first 9 decimal digits cannot
overflow. */
UV value = *s - '0';
- /* This construction seems to be more optimiser friendly.
+ /* This construction seems to be more optimizer friendly.
(without it gcc does the isDIGIT test and the *s - '0' separately)
With it gcc on arm is managing 6 instructions (6 cycles) per digit.
- In theory the optimiser could deduce how far to unroll the loop
+ In theory the optimizer could deduce how far to unroll the loop
before checking for overflow. */
if (++s < send) {
int digit = *s - '0';
@@ -6606,7 +6606,7 @@ DPPP_(my_grok_oct)(pTHX_ const char *start, STRLEN *len_p, I32 *flags, NV *resul
bool overflowed = FALSE;
for (; len-- && *s; s++) {
- /* gcc 2.95 optimiser not smart enough to figure that this subtraction
+ /* gcc 2.95 optimizer not smart enough to figure that this subtraction
out front allows slicker code. */
int digit = *s - '0';
if (digit >= 0 && digit <= 7) {
diff --git a/src/pl/plperl/sql/plperl_trigger.sql b/src/pl/plperl/sql/plperl_trigger.sql
index a375b401ea..624193b9d0 100644
--- a/src/pl/plperl/sql/plperl_trigger.sql
+++ b/src/pl/plperl/sql/plperl_trigger.sql
@@ -170,6 +170,38 @@ $$ LANGUAGE plperl;
SELECT direct_trigger();
+-- check that SQL run in trigger code can see transition tables
+
+CREATE TABLE transition_table_test (id int, name text);
+INSERT INTO transition_table_test VALUES (1, 'a');
+
+CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plperl AS
+$$
+ my $cursor = spi_query("SELECT * FROM old_table");
+ my $row = spi_fetchrow($cursor);
+ defined($row) || die "expected a row";
+ elog(INFO, "old: " . $row->{id} . " -> " . $row->{name});
+ my $row = spi_fetchrow($cursor);
+ !defined($row) || die "expected no more rows";
+
+ my $cursor = spi_query("SELECT * FROM new_table");
+ my $row = spi_fetchrow($cursor);
+ defined($row) || die "expected a row";
+ elog(INFO, "new: " . $row->{id} . " -> " . $row->{name});
+ my $row = spi_fetchrow($cursor);
+ !defined($row) || die "expected no more rows";
+
+ return undef;
+$$;
+
+CREATE TRIGGER a_t AFTER UPDATE ON transition_table_test
+ REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
+ FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_test_f();
+UPDATE transition_table_test SET name = 'b';
+
+DROP TABLE transition_table_test;
+DROP FUNCTION transition_table_test_f();
+
-- test plperl command triggers
create or replace function perlsnitch() returns event_trigger language plperl as $$
elog(NOTICE, "perlsnitch: " . $_TD->{event} . " " . $_TD->{tag} . " ");
diff --git a/src/pl/plperl/text2macro.pl b/src/pl/plperl/text2macro.pl
index c88e5ec4be..e681fca21a 100644
--- a/src/pl/plperl/text2macro.pl
+++ b/src/pl/plperl/text2macro.pl
@@ -49,7 +49,7 @@ for my $src_file (@ARGV)
(my $macro = $src_file) =~ s/ .*? (\w+) (?:\.\w+) $/$1/x;
- open my $src_fh, $src_file # not 3-arg form
+ open my $src_fh, '<', $src_file
or die "Can't open $src_file: $!";
printf qq{#define %s%s \\\n},
@@ -80,19 +80,19 @@ sub selftest
my $tmp = "text2macro_tmp";
my $string = q{a '' '\\'' "" "\\"" "\\\\" "\\\\n" b};
- open my $fh, ">$tmp.pl" or die;
+ open my $fh, '>', "$tmp.pl" or die;
print $fh $string;
close $fh;
system("perl $0 --name=X $tmp.pl > $tmp.c") == 0 or die;
- open $fh, ">>$tmp.c";
+ open $fh, '>>', "$tmp.c";
print $fh "#include <stdio.h>\n";
print $fh "int main() { puts(X); return 0; }\n";
close $fh;
system("cat -n $tmp.c");
system("make $tmp") == 0 or die;
- open $fh, "./$tmp |" or die;
+ open $fh, '<', "./$tmp |" or die;
my $result = <$fh>;
unlink <$tmp.*>;
diff --git a/src/pl/plpgsql/src/Makefile b/src/pl/plpgsql/src/Makefile
index e073b2abd0..95348179ac 100644
--- a/src/pl/plpgsql/src/Makefile
+++ b/src/pl/plpgsql/src/Makefile
@@ -1,6 +1,6 @@
#-------------------------------------------------------------------------
#
-# Makefile for the pl/pgsql procedural language
+# Makefile for the PL/pgSQL procedural language
#
# src/pl/plpgsql/src/Makefile
#
diff --git a/src/pl/plpgsql/src/generate-plerrcodes.pl b/src/pl/plpgsql/src/generate-plerrcodes.pl
index 6a60a13d81..eb135bc25e 100644
--- a/src/pl/plpgsql/src/generate-plerrcodes.pl
+++ b/src/pl/plpgsql/src/generate-plerrcodes.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
#
# Generate the plerrcodes.h header from errcodes.txt
-# Copyright (c) 2000-2016, PostgreSQL Global Development Group
+# Copyright (c) 2000-2017, PostgreSQL Global Development Group
use warnings;
use strict;
@@ -10,7 +10,7 @@ print
"/* autogenerated from src/backend/utils/errcodes.txt, do not edit */\n";
print "/* there is deliberately not an #ifndef PLERRCODES_H here */\n";
-open my $errcodes, $ARGV[0] or die;
+open my $errcodes, '<', $ARGV[0] or die;
while (<$errcodes>)
{
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index b628c2811b..a6375511f6 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -3,7 +3,7 @@
* pl_comp.c - Compiler part of the PL/pgSQL
* procedural language
*
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
@@ -13,7 +13,7 @@
*-------------------------------------------------------------------------
*/
-#include "plpgsql.h"
+#include "postgres.h"
#include <ctype.h>
@@ -29,9 +29,12 @@
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
+#include "utils/regproc.h"
#include "utils/rel.h"
#include "utils/syscache.h"
+#include "plpgsql.h"
+
/* ----------
* Our own local and global variables
@@ -93,7 +96,7 @@ static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,
PLpgSQL_func_hashkey *hashkey,
bool forValidator);
static void plpgsql_compile_error_callback(void *arg);
-static void add_parameter_name(int itemtype, int itemno, const char *name);
+static void add_parameter_name(PLpgSQL_nsitem_type itemtype, int itemno, const char *name);
static void add_dummy_return(PLpgSQL_function *function);
static Node *plpgsql_pre_column_ref(ParseState *pstate, ColumnRef *cref);
static Node *plpgsql_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var);
@@ -340,9 +343,7 @@ do_compile(FunctionCallInfo fcinfo,
*/
func_cxt = AllocSetContextCreate(TopMemoryContext,
"PL/pgSQL function context",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
+ ALLOCSET_DEFAULT_SIZES);
plpgsql_compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
function->fn_signature = format_procedure(fcinfo->flinfo->fn_oid);
@@ -412,7 +413,7 @@ do_compile(FunctionCallInfo fcinfo,
char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
PLpgSQL_type *argdtype;
PLpgSQL_variable *argvariable;
- int argitemtype;
+ PLpgSQL_nsitem_type argitemtype;
/* Create $n name for variable */
snprintf(buf, sizeof(buf), "$%d", i + 1);
@@ -588,11 +589,11 @@ do_compile(FunctionCallInfo fcinfo,
errmsg("trigger functions cannot have declared arguments"),
errhint("The arguments of the trigger can be accessed through TG_NARGS and TG_ARGV instead.")));
- /* Add the record for referencing NEW */
+ /* Add the record for referencing NEW ROW */
rec = plpgsql_build_record("new", 0, true);
function->new_varno = rec->dno;
- /* Add the record for referencing OLD */
+ /* Add the record for referencing OLD ROW */
rec = plpgsql_build_record("old", 0, true);
function->old_varno = rec->dno;
@@ -829,10 +830,8 @@ plpgsql_compile_inline(char *proc_source)
* its own memory context, so it can be reclaimed easily.
*/
func_cxt = AllocSetContextCreate(CurrentMemoryContext,
- "PL/pgSQL function context",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
+ "PL/pgSQL inline code context",
+ ALLOCSET_DEFAULT_SIZES);
plpgsql_compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
function->fn_signature = pstrdup(func_name);
@@ -950,7 +949,7 @@ plpgsql_compile_error_callback(void *arg)
* Add a name for a function parameter to the function's namespace
*/
static void
-add_parameter_name(int itemtype, int itemno, const char *name)
+add_parameter_name(PLpgSQL_nsitem_type itemtype, int itemno, const char *name)
{
/*
* Before adding the name, check for duplicates. We need this even though
@@ -2192,14 +2191,19 @@ build_datatype(HeapTuple typeTup, int32 typmod, Oid collation)
/* NB: this is only used to decide whether to apply expand_array */
if (typeStruct->typtype == TYPTYPE_BASE)
{
- /* this test should match what get_element_type() checks */
+ /*
+ * This test should include what get_element_type() checks. We also
+ * disallow non-toastable array types (i.e. oidvector and int2vector).
+ */
typ->typisarray = (typeStruct->typlen == -1 &&
- OidIsValid(typeStruct->typelem));
+ OidIsValid(typeStruct->typelem) &&
+ typeStruct->typstorage != 'p');
}
else if (typeStruct->typtype == TYPTYPE_DOMAIN)
{
/* we can short-circuit looking up base types if it's not varlena */
typ->typisarray = (typeStruct->typlen == -1 &&
+ typeStruct->typstorage != 'p' &&
OidIsValid(get_base_element_type(typeStruct->typbasetype)));
}
else
@@ -2449,15 +2453,16 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
hashkey->isTrigger = CALLED_AS_TRIGGER(fcinfo);
/*
- * if trigger, get relation OID. In validation mode we do not know what
- * relation is intended to be used, so we leave trigrelOid zero; the hash
- * entry built in this case will never really be used.
+ * if trigger, get its OID. In validation mode we do not know what
+ * relation or transition table names are intended to be used, so we leave
+ * trigOid zero; the hash entry built in this case will never really be
+ * used.
*/
if (hashkey->isTrigger && !forValidator)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
- hashkey->trigrelOid = RelationGetRelid(trigdata->tg_relation);
+ hashkey->trigOid = trigdata->tg_trigger->tgoid;
}
/* get input collation, if known */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 31ae38e395..0d75a99361 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
* pl_exec.c - Executor for the PL/pgSQL
* procedural language
*
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
@@ -13,7 +13,7 @@
*-------------------------------------------------------------------------
*/
-#include "plpgsql.h"
+#include "postgres.h"
#include <ctype.h>
@@ -44,6 +44,8 @@
#include "pgxc/pgxc.h"
#endif
+#include "plpgsql.h"
+
typedef struct
{
@@ -51,7 +53,6 @@ typedef struct
Oid *types; /* types of arguments */
Datum *values; /* evaluated argument values */
char *nulls; /* null markers (' '/'n' style) */
- bool *freevals; /* which arguments are pfree-able */
} PreparedParamsData;
/*
@@ -91,6 +92,36 @@ static EState *shared_simple_eval_estate = NULL;
static SimpleEcontextStackEntry *simple_econtext_stack = NULL;
/*
+ * Memory management within a plpgsql function generally works with three
+ * contexts:
+ *
+ * 1. Function-call-lifespan data, such as variable values, is kept in the
+ * "main" context, a/k/a the "SPI Proc" context established by SPI_connect().
+ * This is usually the CurrentMemoryContext while running code in this module
+ * (which is not good, because careless coding can easily cause
+ * function-lifespan memory leaks, but we live with it for now).
+ *
+ * 2. Some statement-execution routines need statement-lifespan workspace.
+ * A suitable context is created on-demand by get_stmt_mcontext(), and must
+ * be reset at the end of the requesting routine. Error recovery will clean
+ * it up automatically. Nested statements requiring statement-lifespan
+ * workspace will result in a stack of such contexts, see push_stmt_mcontext().
+ *
+ * 3. We use the eval_econtext's per-tuple memory context for expression
+ * evaluation, and as a general-purpose workspace for short-lived allocations.
+ * Such allocations usually aren't explicitly freed, but are left to be
+ * cleaned up by a context reset, typically done by exec_eval_cleanup().
+ *
+ * These macros are for use in making short-lived allocations:
+ */
+#define get_eval_mcontext(estate) \
+ ((estate)->eval_econtext->ecxt_per_tuple_memory)
+#define eval_mcontext_alloc(estate, sz) \
+ MemoryContextAlloc(get_eval_mcontext(estate), sz)
+#define eval_mcontext_alloc0(estate, sz) \
+ MemoryContextAllocZero(get_eval_mcontext(estate), sz)
+
+/*
* We use a session-wide hash table for caching cast information.
*
* Once built, the compiled expression trees (cast_expr fields) survive for
@@ -117,7 +148,7 @@ typedef struct /* cast_hash table entry */
{
plpgsql_CastHashKey key; /* hash key --- MUST BE FIRST */
Expr *cast_expr; /* cast expression, or NULL if no-op cast */
- /* The ExprState tree is valid only when cast_lxid matches current LXID */
+ /* ExprState is valid only when cast_lxid matches current LXID */
ExprState *cast_exprstate; /* expression's eval tree */
bool cast_in_use; /* true while we're executing eval tree */
LocalTransactionId cast_lxid;
@@ -131,6 +162,9 @@ static HTAB *shared_cast_hash = NULL;
************************************************************/
static void plpgsql_exec_error_callback(void *arg);
static PLpgSQL_datum *copy_plpgsql_datum(PLpgSQL_datum *datum);
+static MemoryContext get_stmt_mcontext(PLpgSQL_execstate *estate);
+static void push_stmt_mcontext(PLpgSQL_execstate *estate);
+static void pop_stmt_mcontext(PLpgSQL_execstate *estate);
static int exec_stmt_block(PLpgSQL_execstate *estate,
PLpgSQL_stmt_block *block);
@@ -194,7 +228,7 @@ static void exec_eval_cleanup(PLpgSQL_execstate *estate);
static void exec_prepare_plan(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr, int cursorOptions);
static bool exec_simple_check_node(Node *node);
-static void exec_simple_check_plan(PLpgSQL_expr *expr);
+static void exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr);
static void exec_simple_recheck_plan(PLpgSQL_expr *expr, CachedPlan *cplan);
static void exec_check_rw_parameter(PLpgSQL_expr *expr, int target_dno);
static bool contains_target_param(Node *node, int *target_dno);
@@ -233,8 +267,7 @@ static Datum exec_eval_expr(PLpgSQL_execstate *estate,
Oid *rettype,
int32 *rettypmod);
static int exec_run_select(PLpgSQL_execstate *estate,
- PLpgSQL_expr *expr, long maxtuples, Portal *portalP,
- bool parallelOK);
+ PLpgSQL_expr *expr, long maxtuples, Portal *portalP);
static int exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
Portal portal, bool prefetch_ok);
static ParamListInfo setup_param_list(PLpgSQL_execstate *estate,
@@ -274,11 +307,9 @@ static void assign_text_var(PLpgSQL_execstate *estate, PLpgSQL_var *var,
const char *str);
static PreparedParamsData *exec_eval_using_params(PLpgSQL_execstate *estate,
List *params);
-static void free_params_data(PreparedParamsData *ppd);
static Portal exec_dynquery_with_params(PLpgSQL_execstate *estate,
PLpgSQL_expr *dynquery, List *params,
const char *portalname, int cursorOptions);
-
static char *format_expr_params(PLpgSQL_execstate *estate,
const PLpgSQL_expr *expr);
static char *format_preparedparamsdata(PLpgSQL_execstate *estate,
@@ -565,6 +596,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
/* Clean up any leftover temporary memory */
plpgsql_destroy_econtext(&estate);
exec_eval_cleanup(&estate);
+ /* stmt_mcontext will be destroyed when function's main context is */
/*
* Pop the error context stack
@@ -660,6 +692,10 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
else
elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, or UPDATE");
+ /* Make transition tables visible to this SPI connection */
+ rc = SPI_register_trigger_data(trigdata);
+ Assert(rc >= 0);
+
/*
* Assign the special tg_ variables
*/
@@ -835,6 +871,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
/* Clean up any leftover temporary memory */
plpgsql_destroy_econtext(&estate);
exec_eval_cleanup(&estate);
+ /* stmt_mcontext will be destroyed when function's main context is */
/*
* Pop the error context stack
@@ -847,6 +884,11 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
return rettup;
}
+/* ----------
+ * plpgsql_exec_event_trigger Called by the call handler for
+ * event trigger execution.
+ * ----------
+ */
void
plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata)
{
@@ -918,6 +960,7 @@ plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata)
/* Clean up any leftover temporary memory */
plpgsql_destroy_econtext(&estate);
exec_eval_cleanup(&estate);
+ /* stmt_mcontext will be destroyed when function's main context is */
/*
* Pop the error context stack
@@ -1044,7 +1087,62 @@ copy_plpgsql_datum(PLpgSQL_datum *datum)
return result;
}
+/*
+ * Create a memory context for statement-lifespan variables, if we don't
+ * have one already. It will be a child of stmt_mcontext_parent, which is
+ * either the function's main context or a pushed-down outer stmt_mcontext.
+ */
+static MemoryContext
+get_stmt_mcontext(PLpgSQL_execstate *estate)
+{
+ if (estate->stmt_mcontext == NULL)
+ {
+ estate->stmt_mcontext =
+ AllocSetContextCreate(estate->stmt_mcontext_parent,
+ "PLpgSQL per-statement data",
+ ALLOCSET_DEFAULT_SIZES);
+ }
+ return estate->stmt_mcontext;
+}
+
+/*
+ * Push down the current stmt_mcontext so that called statements won't use it.
+ * This is needed by statements that have statement-lifespan data and need to
+ * preserve it across some inner statements. The caller should eventually do
+ * pop_stmt_mcontext().
+ */
+static void
+push_stmt_mcontext(PLpgSQL_execstate *estate)
+{
+ /* Should have done get_stmt_mcontext() first */
+ Assert(estate->stmt_mcontext != NULL);
+ /* Assert we've not messed up the stack linkage */
+ Assert(MemoryContextGetParent(estate->stmt_mcontext) == estate->stmt_mcontext_parent);
+ /* Push it down to become the parent of any nested stmt mcontext */
+ estate->stmt_mcontext_parent = estate->stmt_mcontext;
+ /* And make it not available for use directly */
+ estate->stmt_mcontext = NULL;
+}
+
+/*
+ * Undo push_stmt_mcontext(). We assume this is done just before or after
+ * resetting the caller's stmt_mcontext; since that action will also delete
+ * any child contexts, there's no need to explicitly delete whatever context
+ * might currently be estate->stmt_mcontext.
+ */
+static void
+pop_stmt_mcontext(PLpgSQL_execstate *estate)
+{
+ /* We need only pop the stack */
+ estate->stmt_mcontext = estate->stmt_mcontext_parent;
+ estate->stmt_mcontext_parent = MemoryContextGetParent(estate->stmt_mcontext);
+}
+
+/*
+ * Subroutine for exec_stmt_block: does any condition in the condition list
+ * match the current exception?
+ */
static bool
exception_matches_conditions(ErrorData *edata, PLpgSQL_condition *cond)
{
@@ -1177,9 +1275,21 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
ResourceOwner oldowner = CurrentResourceOwner;
ExprContext *old_eval_econtext = estate->eval_econtext;
ErrorData *save_cur_error = estate->cur_error;
+ MemoryContext stmt_mcontext;
estate->err_text = gettext_noop("during statement block entry");
+ /*
+ * We will need a stmt_mcontext to hold the error data if an error
+ * occurs. It seems best to force it to exist before entering the
+ * subtransaction, so that we reduce the risk of out-of-memory during
+ * error recovery, and because this greatly simplifies restoring the
+ * stmt_mcontext stack to the correct state after an error. We can
+ * ameliorate the cost of this by allowing the called statements to
+ * use this mcontext too; so we don't push it down here.
+ */
+ stmt_mcontext = get_stmt_mcontext(estate);
+
BeginInternalSubTransaction(NULL);
/* Want to run statements inside function's memory context */
MemoryContextSwitchTo(oldcontext);
@@ -1205,7 +1315,9 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
* If the block ended with RETURN, we may need to copy the return
* value out of the subtransaction eval_context. This is
* currently only needed for scalar result types --- rowtype
- * values will always exist in the function's own memory context.
+ * values will always exist in the function's main memory context,
+ * cf. exec_stmt_return(). We can avoid a physical copy if the
+ * value happens to be a R/W expanded object.
*/
if (rc == PLPGSQL_RC_RETURN &&
!estate->retisset &&
@@ -1216,8 +1328,8 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
bool resTypByVal;
get_typlenbyval(estate->rettype, &resTypLen, &resTypByVal);
- estate->retval = datumCopy(estate->retval,
- resTypByVal, resTypLen);
+ estate->retval = datumTransfer(estate->retval,
+ resTypByVal, resTypLen);
}
/* Commit the inner transaction, return to outer xact context */
@@ -1225,17 +1337,14 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
+ /* Assert that the stmt_mcontext stack is unchanged */
+ Assert(stmt_mcontext == estate->stmt_mcontext);
+
/*
* Revert to outer eval_econtext. (The inner one was
* automatically cleaned up during subxact exit.)
*/
estate->eval_econtext = old_eval_econtext;
-
- /*
- * AtEOSubXact_SPI() should not have popped any SPI context, but
- * just in case it did, make sure we remain connected.
- */
- SPI_restore_connection();
}
PG_CATCH();
{
@@ -1244,8 +1353,8 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
estate->err_text = gettext_noop("during exception cleanup");
- /* Save error info */
- MemoryContextSwitchTo(oldcontext);
+ /* Save error info in our stmt_mcontext */
+ MemoryContextSwitchTo(stmt_mcontext);
edata = CopyErrorData();
FlushErrorState();
@@ -1254,15 +1363,28 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
- /* Revert to outer eval_econtext */
- estate->eval_econtext = old_eval_econtext;
+ /*
+ * Set up the stmt_mcontext stack as though we had restored our
+ * previous state and then done push_stmt_mcontext(). The push is
+ * needed so that statements in the exception handler won't
+ * clobber the error data that's in our stmt_mcontext.
+ */
+ estate->stmt_mcontext_parent = stmt_mcontext;
+ estate->stmt_mcontext = NULL;
/*
- * If AtEOSubXact_SPI() popped any SPI context of the subxact, it
- * will have left us in a disconnected state. We need this hack
- * to return to connected state.
+ * Now we can delete any nested stmt_mcontexts that might have
+ * been created as children of ours. (Note: we do not immediately
+ * release any statement-lifespan data that might have been left
+ * behind in stmt_mcontext itself. We could attempt that by doing
+ * a MemoryContextReset on it before collecting the error data
+ * above, but it seems too risky to do any significant amount of
+ * work before collecting the error.)
*/
- SPI_restore_connection();
+ MemoryContextDeleteChildren(stmt_mcontext);
+
+ /* Revert to outer eval_econtext */
+ estate->eval_econtext = old_eval_econtext;
/*
* Must clean up the econtext too. However, any tuple table made
@@ -1322,8 +1444,10 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
/* If no match found, re-throw the error */
if (e == NULL)
ReThrowError(edata);
- else
- FreeErrorData(edata);
+
+ /* Restore stmt_mcontext stack and release the error data */
+ pop_stmt_mcontext(estate);
+ MemoryContextReset(stmt_mcontext);
}
PG_END_TRY();
@@ -1428,7 +1552,7 @@ exec_stmt(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt)
CHECK_FOR_INTERRUPTS();
- switch ((enum PLpgSQL_stmt_types) stmt->cmd_type)
+ switch (stmt->cmd_type)
{
case PLPGSQL_STMT_BLOCK:
rc = exec_stmt_block(estate, (PLpgSQL_stmt_block *) stmt);
@@ -1567,7 +1691,7 @@ exec_stmt_perform(PLpgSQL_execstate *estate, PLpgSQL_stmt_perform *stmt)
{
PLpgSQL_expr *expr = stmt->expr;
- (void) exec_run_select(estate, expr, 0, NULL, true);
+ (void) exec_run_select(estate, expr, 0, NULL);
exec_set_found(estate, (estate->eval_processed != 0));
exec_eval_cleanup(estate);
@@ -1666,11 +1790,15 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
case PLPGSQL_GETDIAG_CONTEXT:
{
- char *contextstackstr = GetErrorContextStack();
+ char *contextstackstr;
+ MemoryContext oldcontext;
- exec_assign_c_string(estate, var, contextstackstr);
+ /* Use eval_mcontext for short-lived string */
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
+ contextstackstr = GetErrorContextStack();
+ MemoryContextSwitchTo(oldcontext);
- pfree(contextstackstr);
+ exec_assign_c_string(estate, var, contextstackstr);
}
break;
@@ -1680,6 +1808,8 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
}
}
+ exec_eval_cleanup(estate);
+
return PLPGSQL_RC_OK;
}
@@ -1741,7 +1871,10 @@ exec_stmt_case(PLpgSQL_execstate *estate, PLpgSQL_stmt_case *stmt)
/*
* When expected datatype is different from real, change it. Note that
* what we're modifying here is an execution copy of the datum, so
- * this doesn't affect the originally stored function parse tree.
+ * this doesn't affect the originally stored function parse tree. (In
+ * theory, if the expression datatype keeps changing during execution,
+ * this could cause a function-lifespan memory leak. Doesn't seem
+ * worth worrying about though.)
*/
if (t_var->datatype->typoid != t_typoid ||
t_var->datatype->atttypmod != t_typmod)
@@ -2111,7 +2244,7 @@ exec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt)
/*
* Open the implicit cursor for the statement using exec_run_select
*/
- exec_run_select(estate, stmt->query, 0, &portal, false);
+ exec_run_select(estate, stmt->query, 0, &portal);
/*
* Execute the loop
@@ -2135,6 +2268,7 @@ static int
exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
{
PLpgSQL_var *curvar;
+ MemoryContext stmt_mcontext = NULL;
char *curname = NULL;
PLpgSQL_expr *query;
ParamListInfo paramLI;
@@ -2149,7 +2283,14 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
curvar = (PLpgSQL_var *) (estate->datums[stmt->curvar]);
if (!curvar->isnull)
{
+ MemoryContext oldcontext;
+
+ /* We only need stmt_mcontext to hold the cursor name string */
+ stmt_mcontext = get_stmt_mcontext(estate);
+ oldcontext = MemoryContextSwitchTo(stmt_mcontext);
curname = TextDatumGetCString(curvar->value);
+ MemoryContextSwitchTo(oldcontext);
+
if (SPI_cursor_find(curname) != NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_CURSOR),
@@ -2219,10 +2360,6 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
elog(ERROR, "could not open cursor: %s",
SPI_result_code_string(SPI_result));
- /* don't need paramlist any more */
- if (paramLI)
- pfree(paramLI);
-
/*
* If cursor variable was NULL, store the generated portal name in it
*/
@@ -2230,6 +2367,13 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
assign_text_var(estate, curvar, portal->name);
/*
+ * Clean up before entering exec_for_query
+ */
+ exec_eval_cleanup(estate);
+ if (stmt_mcontext)
+ MemoryContextReset(stmt_mcontext);
+
+ /*
* Execute the loop. We can't prefetch because the cursor is accessible
* to the user, for instance via UPDATE WHERE CURRENT OF within the loop.
*/
@@ -2244,9 +2388,6 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
if (curname == NULL)
assign_simple_var(estate, curvar, (Datum) 0, true, false);
- if (curname)
- pfree(curname);
-
return rc;
}
@@ -2269,6 +2410,8 @@ exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
Oid loop_var_elem_type;
bool found = false;
int rc = PLPGSQL_RC_OK;
+ MemoryContext stmt_mcontext;
+ MemoryContext oldcontext;
ArrayIterator array_iterator;
Oid iterator_result_type;
int32 iterator_result_typmod;
@@ -2282,6 +2425,15 @@ exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("FOREACH expression must not be null")));
+ /*
+ * Do as much as possible of the code below in stmt_mcontext, to avoid any
+ * leaks from called subroutines. We need a private stmt_mcontext since
+ * we'll be calling arbitrary statement code.
+ */
+ stmt_mcontext = get_stmt_mcontext(estate);
+ push_stmt_mcontext(estate);
+ oldcontext = MemoryContextSwitchTo(stmt_mcontext);
+
/* check the type of the expression - must be an array */
if (!OidIsValid(get_element_type(arrtype)))
ereport(ERROR,
@@ -2290,9 +2442,9 @@ exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
format_type_be(arrtype))));
/*
- * We must copy the array, else it will disappear in exec_eval_cleanup.
- * This is annoying, but cleanup will certainly happen while running the
- * loop body, so we have little choice.
+ * We must copy the array into stmt_mcontext, else it will disappear in
+ * exec_eval_cleanup. This is annoying, but cleanup will certainly happen
+ * while running the loop body, so we have little choice.
*/
arr = DatumGetArrayTypePCopy(value);
@@ -2358,6 +2510,9 @@ exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
{
found = true; /* looped at least once */
+ /* exec_assign_value and exec_stmts must run in the main context */
+ MemoryContextSwitchTo(oldcontext);
+
/* Assign current element/slice to the loop variable */
exec_assign_value(estate, loop_var, value, isnull,
iterator_result_type, iterator_result_typmod);
@@ -2416,11 +2571,16 @@ exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
break;
}
}
+
+ MemoryContextSwitchTo(stmt_mcontext);
}
+ /* Restore memory context state */
+ MemoryContextSwitchTo(oldcontext);
+ pop_stmt_mcontext(estate);
+
/* Release temporary memory, including the array value */
- array_free_iterator(array_iterator);
- pfree(arr);
+ MemoryContextReset(stmt_mcontext);
/*
* Set the FOUND variable to indicate the result of executing the loop
@@ -2468,6 +2628,13 @@ exec_stmt_exit(PLpgSQL_execstate *estate, PLpgSQL_stmt_exit *stmt)
/* ----------
* exec_stmt_return Evaluate an expression and start
* returning from the function.
+ *
+ * Note: in the retistuple code paths, the returned tuple is always in the
+ * function's main context, whereas for non-tuple data types the result may
+ * be in the eval_mcontext. The former case is not a memory leak since we're
+ * about to exit the function anyway. (If you want to change it, note that
+ * exec_stmt_block() knows about this behavior.) The latter case means that
+ * we must not do exec_eval_cleanup while unwinding the control stack.
* ----------
*/
static int
@@ -2642,8 +2809,8 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
{
TupleDesc tupdesc;
int natts;
- HeapTuple tuple = NULL;
- bool free_tuple = false;
+ HeapTuple tuple;
+ MemoryContext oldcontext;
if (!estate->retisset)
ereport(ERROR,
@@ -2715,17 +2882,17 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
rec->refname),
errdetail("The tuple structure of a not-yet-assigned"
" record is indeterminate.")));
+
+ /* Use eval_mcontext for tuple conversion work */
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
tupmap = convert_tuples_by_position(rec->tupdesc,
tupdesc,
gettext_noop("wrong record type supplied in RETURN NEXT"));
tuple = rec->tup;
- /* it might need conversion */
if (tupmap)
- {
tuple = do_convert_tuple(tuple, tupmap);
- free_conversion_map(tupmap);
- free_tuple = true;
- }
+ tuplestore_puttuple(estate->tuple_store, tuple);
+ MemoryContextSwitchTo(oldcontext);
}
break;
@@ -2733,12 +2900,15 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
{
PLpgSQL_row *row = (PLpgSQL_row *) retvar;
+ /* Use eval_mcontext for tuple conversion work */
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
tuple = make_tuple_from_row(estate, row, tupdesc);
if (tuple == NULL)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("wrong record type supplied in RETURN NEXT")));
- free_tuple = true;
+ tuplestore_puttuple(estate->tuple_store, tuple);
+ MemoryContextSwitchTo(oldcontext);
}
break;
@@ -2773,24 +2943,17 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot return non-composite value from function returning composite type")));
+ /* Use eval_mcontext for tuple conversion work */
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
tuple = get_tuple_from_datum(retval);
- free_tuple = true; /* tuple is always freshly palloc'd */
-
- /* it might need conversion */
retvaldesc = get_tupdesc_from_datum(retval);
tupmap = convert_tuples_by_position(retvaldesc, tupdesc,
gettext_noop("returned record type does not match expected record type"));
if (tupmap)
- {
- HeapTuple newtuple;
-
- newtuple = do_convert_tuple(tuple, tupmap);
- free_conversion_map(tupmap);
- heap_freetuple(tuple);
- tuple = newtuple;
- }
+ tuple = do_convert_tuple(tuple, tupmap);
+ tuplestore_puttuple(estate->tuple_store, tuple);
ReleaseTupleDesc(retvaldesc);
- /* tuple will be stored into tuplestore below */
+ MemoryContextSwitchTo(oldcontext);
}
else
{
@@ -2798,13 +2961,13 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
Datum *nulldatums;
bool *nullflags;
- nulldatums = (Datum *) palloc0(natts * sizeof(Datum));
- nullflags = (bool *) palloc(natts * sizeof(bool));
+ nulldatums = (Datum *)
+ eval_mcontext_alloc0(estate, natts * sizeof(Datum));
+ nullflags = (bool *)
+ eval_mcontext_alloc(estate, natts * sizeof(bool));
memset(nullflags, true, natts * sizeof(bool));
tuplestore_putvalues(estate->tuple_store, tupdesc,
nulldatums, nullflags);
- pfree(nulldatums);
- pfree(nullflags);
}
}
else
@@ -2835,14 +2998,6 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
errmsg("RETURN NEXT must have a parameter")));
}
- if (HeapTupleIsValid(tuple))
- {
- tuplestore_puttuple(estate->tuple_store, tuple);
-
- if (free_tuple)
- heap_freetuple(tuple);
- }
-
exec_eval_cleanup(estate);
return PLPGSQL_RC_OK;
@@ -2861,6 +3016,7 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
Portal portal;
uint64 processed = 0;
TupleConversionMap *tupmap;
+ MemoryContext oldcontext;
if (!estate->retisset)
ereport(ERROR,
@@ -2873,7 +3029,7 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
if (stmt->query != NULL)
{
/* static query */
- exec_run_select(estate, stmt->query, 0, &portal, true);
+ exec_run_select(estate, stmt->query, 0, &portal);
}
else
{
@@ -2881,9 +3037,12 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
Assert(stmt->dynquery != NULL);
portal = exec_dynquery_with_params(estate, stmt->dynquery,
stmt->params, NULL,
- CURSOR_OPT_PARALLEL_OK);
+ 0);
}
+ /* Use eval_mcontext for tuple conversion work */
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
+
tupmap = convert_tuples_by_position(portal->tupDesc,
estate->rettupdesc,
gettext_noop("structure of query does not match function result type"));
@@ -2893,6 +3052,10 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
uint64 i;
SPI_cursor_fetch(portal, true, 50);
+
+ /* SPI will have changed CurrentMemoryContext */
+ MemoryContextSwitchTo(get_eval_mcontext(estate));
+
if (SPI_processed == 0)
break;
@@ -2911,12 +3074,12 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
SPI_freetuptable(SPI_tuptable);
}
- if (tupmap)
- free_conversion_map(tupmap);
-
SPI_freetuptable(SPI_tuptable);
SPI_cursor_close(portal);
+ MemoryContextSwitchTo(oldcontext);
+ exec_eval_cleanup(estate);
+
estate->eval_processed = processed;
exec_set_found(estate, processed != 0);
@@ -2968,7 +3131,7 @@ do { \
(errcode(ERRCODE_SYNTAX_ERROR), \
errmsg("RAISE option already specified: %s", \
name))); \
- opt = pstrdup(extval); \
+ opt = MemoryContextStrdup(stmt_mcontext, extval); \
} while (0)
/* ----------
@@ -2988,6 +3151,7 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
char *err_datatype = NULL;
char *err_table = NULL;
char *err_schema = NULL;
+ MemoryContext stmt_mcontext;
ListCell *lc;
/* RAISE with no parameters: re-throw current exception */
@@ -3002,10 +3166,13 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
errmsg("RAISE without parameters cannot be used outside an exception handler")));
}
+ /* We'll need to accumulate the various strings in stmt_mcontext */
+ stmt_mcontext = get_stmt_mcontext(estate);
+
if (stmt->condname)
{
err_code = plpgsql_recognize_err_condition(stmt->condname, true);
- condname = pstrdup(stmt->condname);
+ condname = MemoryContextStrdup(stmt_mcontext, stmt->condname);
}
if (stmt->message)
@@ -3013,8 +3180,13 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
StringInfoData ds;
ListCell *current_param;
char *cp;
+ MemoryContext oldcontext;
+ /* build string in stmt_mcontext */
+ oldcontext = MemoryContextSwitchTo(stmt_mcontext);
initStringInfo(&ds);
+ MemoryContextSwitchTo(oldcontext);
+
current_param = list_head(stmt->params);
for (cp = stmt->message; *cp; cp++)
@@ -3067,7 +3239,6 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
elog(ERROR, "unexpected RAISE parameter list length");
err_message = ds.data;
- /* No pfree(ds.data), the pfree(err_message) does it */
}
foreach(lc, stmt->options)
@@ -3099,7 +3270,7 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
errmsg("RAISE option already specified: %s",
"ERRCODE")));
err_code = plpgsql_recognize_err_condition(extval, true);
- condname = pstrdup(extval);
+ condname = MemoryContextStrdup(stmt_mcontext, extval);
break;
case PLPGSQL_RAISEOPTION_MESSAGE:
SET_RAISE_OPTION_TEXT(err_message, "MESSAGE");
@@ -3145,7 +3316,8 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
condname = NULL;
}
else
- err_message = pstrdup(unpack_sql_state(err_code));
+ err_message = MemoryContextStrdup(stmt_mcontext,
+ unpack_sql_state(err_code));
}
/*
@@ -3167,24 +3339,8 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
(err_schema != NULL) ?
err_generic_string(PG_DIAG_SCHEMA_NAME, err_schema) : 0));
- if (condname != NULL)
- pfree(condname);
- if (err_message != NULL)
- pfree(err_message);
- if (err_detail != NULL)
- pfree(err_detail);
- if (err_hint != NULL)
- pfree(err_hint);
- if (err_column != NULL)
- pfree(err_column);
- if (err_constraint != NULL)
- pfree(err_constraint);
- if (err_datatype != NULL)
- pfree(err_datatype);
- if (err_table != NULL)
- pfree(err_table);
- if (err_schema != NULL)
- pfree(err_schema);
+ /* Clean up transient strings */
+ MemoryContextReset(stmt_mcontext);
return PLPGSQL_RC_OK;
}
@@ -3316,9 +3472,7 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
{
shared_cast_context = AllocSetContextCreate(TopMemoryContext,
"PLpgSQL cast info",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
+ ALLOCSET_DEFAULT_SIZES);
memset(&ctl, 0, sizeof(ctl));
ctl.keysize = sizeof(plpgsql_CastHashKey);
ctl.entrysize = sizeof(plpgsql_CastHashEntry);
@@ -3332,6 +3486,14 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
estate->cast_hash_context = shared_cast_context;
}
+ /*
+ * We start with no stmt_mcontext; one will be created only if needed.
+ * That context will be a direct child of the function's main execution
+ * context. Additional stmt_mcontexts might be created as children of it.
+ */
+ estate->stmt_mcontext = NULL;
+ estate->stmt_mcontext_parent = CurrentMemoryContext;
+
estate->eval_tuptable = NULL;
estate->eval_processed = 0;
estate->eval_lastoid = InvalidOid;
@@ -3381,7 +3543,10 @@ exec_eval_cleanup(PLpgSQL_execstate *estate)
SPI_freetuptable(estate->eval_tuptable);
estate->eval_tuptable = NULL;
- /* Clear result of exec_eval_simple_expr (but keep the econtext) */
+ /*
+ * Clear result of exec_eval_simple_expr (but keep the econtext). This
+ * also clears any short-lived allocations done via get_eval_mcontext.
+ */
if (estate->eval_econtext != NULL)
ResetExprContext(estate->eval_econtext);
}
@@ -3433,7 +3598,7 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
expr->plan = plan;
/* Check to see if it's a simple expression */
- exec_simple_check_plan(expr);
+ exec_simple_check_plan(estate, expr);
/*
* Mark expression as not using a read-write param. exec_assign_value has
@@ -3446,6 +3611,9 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
/* ----------
* exec_stmt_execsql Execute an SQL statement (possibly with INTO).
+ *
+ * Note: some callers rely on this not touching stmt_mcontext. If it ever
+ * needs to use that, fix those callers to push/pop stmt_mcontext.
* ----------
*/
static int
@@ -3465,7 +3633,7 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
{
ListCell *l;
- exec_prepare_plan(estate, expr, 0);
+ exec_prepare_plan(estate, expr, CURSOR_OPT_PARALLEL_OK);
stmt->mod_stmt = false;
foreach(l, SPI_plan_get_plan_sources(expr->plan))
{
@@ -3474,9 +3642,8 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
foreach(l2, plansource->query_list)
{
- Query *q = (Query *) lfirst(l2);
+ Query *q = lfirst_node(Query, l2);
- Assert(IsA(q, Query));
if (q->canSetTag)
{
if (q->commandType == CMD_INSERT ||
@@ -3692,6 +3859,7 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
char *querystr;
int exec_res;
PreparedParamsData *ppd = NULL;
+ MemoryContext stmt_mcontext = get_stmt_mcontext(estate);
/*
* First we evaluate the string expression after the EXECUTE keyword. Its
@@ -3706,8 +3874,8 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
/* Get the C-String representation */
querystr = convert_value_to_string(estate, query, restype);
- /* copy it out of the temporary context before we clean up */
- querystr = pstrdup(querystr);
+ /* copy it into the stmt_mcontext before we clean up */
+ querystr = MemoryContextStrdup(stmt_mcontext, querystr);
exec_eval_cleanup(estate);
@@ -3860,12 +4028,9 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
*/
}
- if (ppd)
- free_params_data(ppd);
-
- /* Release any result from SPI_execute, as well as the querystring */
+ /* Release any result from SPI_execute, as well as transient data */
SPI_freetuptable(SPI_tuptable);
- pfree(querystr);
+ MemoryContextReset(stmt_mcontext);
return PLPGSQL_RC_OK;
}
@@ -3909,6 +4074,7 @@ static int
exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
{
PLpgSQL_var *curvar;
+ MemoryContext stmt_mcontext = NULL;
char *curname = NULL;
PLpgSQL_expr *query;
Portal portal;
@@ -3922,7 +4088,14 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
curvar = (PLpgSQL_var *) (estate->datums[stmt->curvar]);
if (!curvar->isnull)
{
+ MemoryContext oldcontext;
+
+ /* We only need stmt_mcontext to hold the cursor name string */
+ stmt_mcontext = get_stmt_mcontext(estate);
+ oldcontext = MemoryContextSwitchTo(stmt_mcontext);
curname = TextDatumGetCString(curvar->value);
+ MemoryContextSwitchTo(oldcontext);
+
if (SPI_cursor_find(curname) != NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_CURSOR),
@@ -3959,7 +4132,10 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
stmt->cursor_options);
/*
- * If cursor variable was NULL, store the generated portal name in it
+ * If cursor variable was NULL, store the generated portal name in it.
+ * Note: exec_dynquery_with_params already reset the stmt_mcontext, so
+ * curname is a dangling pointer here; but testing it for nullness is
+ * OK.
*/
if (curname == NULL)
assign_text_var(estate, curvar, portal->name);
@@ -4036,10 +4212,10 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
if (curname == NULL)
assign_text_var(estate, curvar, portal->name);
- if (curname)
- pfree(curname);
- if (paramLI)
- pfree(paramLI);
+ /* If we had any transient data, clean it up */
+ exec_eval_cleanup(estate);
+ if (stmt_mcontext)
+ MemoryContextReset(stmt_mcontext);
return PLPGSQL_RC_OK;
}
@@ -4053,7 +4229,7 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
static int
exec_stmt_fetch(PLpgSQL_execstate *estate, PLpgSQL_stmt_fetch *stmt)
{
- PLpgSQL_var *curvar = NULL;
+ PLpgSQL_var *curvar;
PLpgSQL_rec *rec = NULL;
PLpgSQL_row *row = NULL;
long how_many = stmt->how_many;
@@ -4061,6 +4237,7 @@ exec_stmt_fetch(PLpgSQL_execstate *estate, PLpgSQL_stmt_fetch *stmt)
Portal portal;
char *curname;
uint64 n;
+ MemoryContext oldcontext;
/* ----------
* Get the portal of the cursor by name
@@ -4071,14 +4248,17 @@ exec_stmt_fetch(PLpgSQL_execstate *estate, PLpgSQL_stmt_fetch *stmt)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("cursor variable \"%s\" is null", curvar->refname)));
+
+ /* Use eval_mcontext for short-lived string */
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
curname = TextDatumGetCString(curvar->value);
+ MemoryContextSwitchTo(oldcontext);
portal = SPI_cursor_find(curname);
if (portal == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_CURSOR),
errmsg("cursor \"%s\" does not exist", curname)));
- pfree(curname);
/* Calculate position for FETCH_RELATIVE or FETCH_ABSOLUTE */
if (stmt->expr)
@@ -4150,9 +4330,10 @@ exec_stmt_fetch(PLpgSQL_execstate *estate, PLpgSQL_stmt_fetch *stmt)
static int
exec_stmt_close(PLpgSQL_execstate *estate, PLpgSQL_stmt_close *stmt)
{
- PLpgSQL_var *curvar = NULL;
+ PLpgSQL_var *curvar;
Portal portal;
char *curname;
+ MemoryContext oldcontext;
/* ----------
* Get the portal of the cursor by name
@@ -4163,14 +4344,17 @@ exec_stmt_close(PLpgSQL_execstate *estate, PLpgSQL_stmt_close *stmt)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("cursor variable \"%s\" is null", curvar->refname)));
+
+ /* Use eval_mcontext for short-lived string */
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
curname = TextDatumGetCString(curvar->value);
+ MemoryContextSwitchTo(oldcontext);
portal = SPI_cursor_find(curname);
if (portal == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_CURSOR),
errmsg("cursor \"%s\" does not exist", curname)));
- pfree(curname);
/* ----------
* And close it.
@@ -4218,6 +4402,9 @@ exec_assign_expr(PLpgSQL_execstate *estate, PLpgSQL_datum *target,
* exec_assign_c_string Put a C string into a text variable.
*
* We take a NULL pointer as signifying empty string, not SQL null.
+ *
+ * As with the underlying exec_assign_value, caller is expected to do
+ * exec_eval_cleanup later.
* ----------
*/
static void
@@ -4225,21 +4412,25 @@ exec_assign_c_string(PLpgSQL_execstate *estate, PLpgSQL_datum *target,
const char *str)
{
text *value;
+ MemoryContext oldcontext;
+ /* Use eval_mcontext for short-lived text value */
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
if (str != NULL)
value = cstring_to_text(str);
else
value = cstring_to_text("");
+ MemoryContextSwitchTo(oldcontext);
+
exec_assign_value(estate, target, PointerGetDatum(value), false,
TEXTOID, -1);
- pfree(value);
}
/* ----------
* exec_assign_value Put a value into a target datum
*
- * Note: in some code paths, this will leak memory in the eval_econtext;
+ * Note: in some code paths, this will leak memory in the eval_mcontext;
* we assume that will be cleaned up later by exec_eval_cleanup. We cannot
* call exec_eval_cleanup here for fear of destroying the input Datum value.
* ----------
@@ -4276,10 +4467,10 @@ exec_assign_value(PLpgSQL_execstate *estate,
/*
* If type is by-reference, copy the new value (which is
- * probably in the eval_econtext) into the procedure's memory
- * context. But if it's a read/write reference to an expanded
- * object, no physical copy needs to happen; at most we need
- * to reparent the object's memory context.
+ * probably in the eval_mcontext) into the procedure's main
+ * memory context. But if it's a read/write reference to an
+ * expanded object, no physical copy needs to happen; at most
+ * we need to reparent the object's memory context.
*
* If it's an array, we force the value to be stored in R/W
* expanded form. This wins if the function later does, say,
@@ -4379,10 +4570,9 @@ exec_assign_value(PLpgSQL_execstate *estate,
PLpgSQL_rec *rec;
int fno;
HeapTuple newtup;
- int natts;
- Datum *values;
- bool *nulls;
- bool *replaces;
+ int colnums[1];
+ Datum values[1];
+ bool nulls[1];
Oid atttype;
int32 atttypmod;
@@ -4401,9 +4591,8 @@ exec_assign_value(PLpgSQL_execstate *estate,
errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
/*
- * Get the number of the records field to change and the
- * number of attributes in the tuple. Note: disallow system
- * column names because the code below won't cope.
+ * Get the number of the record field to change. Disallow
+ * system columns because the code below won't cope.
*/
fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
if (fno <= 0)
@@ -4411,42 +4600,25 @@ exec_assign_value(PLpgSQL_execstate *estate,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("record \"%s\" has no field \"%s\"",
rec->refname, recfield->fieldname)));
- fno--;
- natts = rec->tupdesc->natts;
-
- /*
- * Set up values/control arrays for heap_modify_tuple. For all
- * the attributes except the one we want to replace, use the
- * value that's in the old tuple.
- */
- values = palloc(sizeof(Datum) * natts);
- nulls = palloc(sizeof(bool) * natts);
- replaces = palloc(sizeof(bool) * natts);
-
- memset(replaces, false, sizeof(bool) * natts);
- replaces[fno] = true;
+ colnums[0] = fno;
/*
* Now insert the new value, being careful to cast it to the
* right type.
*/
- atttype = rec->tupdesc->attrs[fno]->atttypid;
- atttypmod = rec->tupdesc->attrs[fno]->atttypmod;
- values[fno] = exec_cast_value(estate,
- value,
- &isNull,
- valtype,
- valtypmod,
- atttype,
- atttypmod);
- nulls[fno] = isNull;
-
- /*
- * Now call heap_modify_tuple() to create a new tuple that
- * replaces the old one in the record.
- */
- newtup = heap_modify_tuple(rec->tup, rec->tupdesc,
- values, nulls, replaces);
+ atttype = rec->tupdesc->attrs[fno - 1]->atttypid;
+ atttypmod = rec->tupdesc->attrs[fno - 1]->atttypmod;
+ values[0] = exec_cast_value(estate,
+ value,
+ &isNull,
+ valtype,
+ valtypmod,
+ atttype,
+ atttypmod);
+ nulls[0] = isNull;
+
+ newtup = heap_modify_tuple_by_cols(rec->tup, rec->tupdesc,
+ 1, colnums, values, nulls);
if (rec->freetup)
heap_freetuple(rec->tup);
@@ -4454,10 +4626,6 @@ exec_assign_value(PLpgSQL_execstate *estate,
rec->tup = newtup;
rec->freetup = true;
- pfree(values);
- pfree(nulls);
- pfree(replaces);
-
break;
}
@@ -4563,7 +4731,8 @@ exec_assign_value(PLpgSQL_execstate *estate,
/*
* Evaluate the subscripts, switch into left-to-right order.
- * Like ExecEvalArrayRef(), complain if any subscript is null.
+ * Like the expression built by ExecInitArrayRef(), complain
+ * if any subscript is null.
*/
for (i = 0; i < nsubscripts; i++)
{
@@ -4611,14 +4780,14 @@ exec_assign_value(PLpgSQL_execstate *estate,
* fixed-length array types we skip the assignment. We can't
* support assignment of a null entry into a fixed-length
* array, either, so that's a no-op too. This is all ugly but
- * corresponds to the current behavior of ExecEvalArrayRef().
+ * corresponds to the current behavior of execExpr*.c.
*/
if (arrayelem->arraytyplen > 0 && /* fixed-length array? */
(oldarrayisnull || isNull))
return;
/* empty array, if any, and newarraydatum are short-lived */
- oldcontext = MemoryContextSwitchTo(estate->eval_econtext->ecxt_per_tuple_memory);
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
if (oldarrayisnull)
oldarraydatum = PointerGetDatum(construct_empty_array(arrayelem->elemtypoid));
@@ -4672,7 +4841,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
* responsibility that the results are semantically OK.
*
* In some cases we have to palloc a return value, and in such cases we put
- * it into the estate's short-term memory context.
+ * it into the estate's eval_mcontext.
*/
static void
exec_eval_datum(PLpgSQL_execstate *estate,
@@ -4706,7 +4875,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
elog(ERROR, "row variable has no tupdesc");
/* Make sure we have a valid type/typmod setting */
BlessTupleDesc(row->rowtupdesc);
- oldcontext = MemoryContextSwitchTo(estate->eval_econtext->ecxt_per_tuple_memory);
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
tup = make_tuple_from_row(estate, row, row->rowtupdesc);
if (tup == NULL) /* should not happen */
elog(ERROR, "row not compatible with its own tupdesc");
@@ -4732,7 +4901,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
/* Make sure we have a valid type/typmod setting */
BlessTupleDesc(rec->tupdesc);
- oldcontext = MemoryContextSwitchTo(estate->eval_econtext->ecxt_per_tuple_memory);
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
*typeid = rec->tupdesc->tdtypeid;
*typetypmod = rec->tupdesc->tdtypmod;
*value = heap_copy_tuple_as_datum(rec->tup, rec->tupdesc);
@@ -5026,7 +5195,7 @@ exec_eval_expr(PLpgSQL_execstate *estate,
* If first time through, create a plan for this expression.
*/
if (expr->plan == NULL)
- exec_prepare_plan(estate, expr, 0);
+ exec_prepare_plan(estate, expr, CURSOR_OPT_PARALLEL_OK);
/*
* If this is a simple expression, bypass SPI and use the executor
@@ -5039,7 +5208,7 @@ exec_eval_expr(PLpgSQL_execstate *estate,
/*
* Else do it the hard way via exec_run_select
*/
- rc = exec_run_select(estate, expr, 2, NULL, false);
+ rc = exec_run_select(estate, expr, 2, NULL);
if (rc != SPI_OK_SELECT)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -5095,18 +5264,23 @@ exec_eval_expr(PLpgSQL_execstate *estate,
*/
static int
exec_run_select(PLpgSQL_execstate *estate,
- PLpgSQL_expr *expr, long maxtuples, Portal *portalP,
- bool parallelOK)
+ PLpgSQL_expr *expr, long maxtuples, Portal *portalP)
{
ParamListInfo paramLI;
int rc;
/*
- * On the first call for this expression generate the plan
+ * On the first call for this expression generate the plan.
+ *
+ * If we don't need to return a portal, then we're just going to execute
+ * the query once, which means it's OK to use a parallel plan, even if the
+ * number of rows being fetched is limited. If we do need to return a
+ * portal, the caller might do cursor operations, which parallel query
+ * can't support.
*/
if (expr->plan == NULL)
- exec_prepare_plan(estate, expr, parallelOK ?
- CURSOR_OPT_PARALLEL_OK : 0);
+ exec_prepare_plan(estate, expr,
+ portalP == NULL ? CURSOR_OPT_PARALLEL_OK : 0);
/*
* If a portal was requested, put the query into the portal
@@ -5124,8 +5298,7 @@ exec_run_select(PLpgSQL_execstate *estate,
if (*portalP == NULL)
elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
expr->query, SPI_result_code_string(SPI_result));
- if (paramLI)
- pfree(paramLI);
+ exec_eval_cleanup(estate);
return SPI_OK_CURSOR;
}
@@ -5352,9 +5525,8 @@ loop_exit:
* give correct results if that happens, and it's unlikely to be worth the
* cycles to check.
*
- * Note: if pass-by-reference, the result is in the eval_econtext's
- * temporary memory context. It will be freed when exec_eval_cleanup
- * is done.
+ * Note: if pass-by-reference, the result is in the eval_mcontext.
+ * It will be freed when exec_eval_cleanup is done.
* ----------
*/
static bool
@@ -5386,9 +5558,12 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
/*
* Revalidate cached plan, so that we will notice if it became stale. (We
- * need to hold a refcount while using the plan, anyway.)
+ * need to hold a refcount while using the plan, anyway.) If replanning
+ * is needed, do that work in the eval_mcontext.
*/
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
cplan = SPI_plan_get_cached_plan(expr->plan);
+ MemoryContextSwitchTo(oldcontext);
/*
* We can't get a failure here, because the number of CachedPlanSources in
@@ -5406,7 +5581,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
exec_check_rw_parameter(expr, expr->rwparam);
if (expr->expr_simple_expr == NULL)
{
- /* Ooops, release refcount and fail */
+ /* Oops, release refcount and fail */
ReleaseCachedPlan(cplan, true);
return false;
}
@@ -5438,9 +5613,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
* Without this, stable functions within the expression would fail to see
* updates made so far by our own function.
*/
- SPI_push();
-
- oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
if (!estate->readonly_func)
{
CommandCounterIncrement();
@@ -5472,14 +5645,12 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
*/
*result = ExecEvalExpr(expr->expr_simple_state,
econtext,
- isNull,
- NULL);
+ isNull);
/* Assorted cleanup */
expr->expr_simple_in_use = false;
- if (paramLI && paramLI != estate->paramLI)
- pfree(paramLI);
+ econtext->ecxt_param_list_info = NULL;
estate->paramLI->parserSetupArg = save_setup_arg;
@@ -5488,8 +5659,6 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
MemoryContextSwitchTo(oldcontext);
- SPI_pop();
-
/*
* Now we can release our refcount on the cached plan.
*/
@@ -5527,8 +5696,8 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
* throw errors (for example "no such record field") and we do not want that
* to happen in a part of the expression that might never be evaluated at
* runtime. For another thing, exec_eval_datum() may return short-lived
- * values stored in the estate's short-term memory context, which will not
- * necessarily survive to the next SPI operation. And for a third thing, ROW
+ * values stored in the estate's eval_mcontext, which will not necessarily
+ * survive to the next SPI operation. And for a third thing, ROW
* and RECFIELD datums' values depend on other datums, and we don't have a
* cheap way to track that. Therefore, param slots for non-VAR datum types
* are always reset here and then filled on-demand by plpgsql_param_fetch().
@@ -5627,7 +5796,7 @@ setup_param_list(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
* to some trusted function. We don't want the R/W pointer to get into the
* shared param list, where it could get passed to some less-trusted function.
*
- * Caller should pfree the result after use, if it's not NULL.
+ * The result, if not NULL, is in the estate's eval_mcontext.
*
* XXX. Could we use ParamListInfo's new paramMask to avoid creating unshared
* parameter lists?
@@ -5655,8 +5824,9 @@ setup_unshared_param_list(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
/* initialize ParamListInfo with one entry per datum, all invalid */
paramLI = (ParamListInfo)
- palloc0(offsetof(ParamListInfoData, params) +
- estate->ndatums * sizeof(ParamExternData));
+ eval_mcontext_alloc0(estate,
+ offsetof(ParamListInfoData, params) +
+ estate->ndatums * sizeof(ParamExternData));
paramLI->paramFetch = plpgsql_param_fetch;
paramLI->paramFetchArg = (void *) estate;
paramLI->parserSetup = (ParserSetupHook) plpgsql_parser_setup;
@@ -5813,12 +5983,11 @@ exec_move_row(PLpgSQL_execstate *estate,
/* If we have a tupdesc but no data, form an all-nulls tuple */
bool *nulls;
- nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
+ nulls = (bool *)
+ eval_mcontext_alloc(estate, tupdesc->natts * sizeof(bool));
memset(nulls, true, tupdesc->natts * sizeof(bool));
tup = heap_form_tuple(tupdesc, NULL, nulls);
-
- pfree(nulls);
}
if (tupdesc)
@@ -5936,6 +6105,9 @@ exec_move_row(PLpgSQL_execstate *estate,
* make_tuple_from_row Make a tuple from the values of a row object
*
* A NULL return indicates rowtype mismatch; caller must raise suitable error
+ *
+ * The result tuple is freshly palloc'd in caller's context. Some junk
+ * may be left behind in eval_mcontext, too.
* ----------
*/
static HeapTuple
@@ -5952,8 +6124,8 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
if (natts != row->nfields)
return NULL;
- dvalues = (Datum *) palloc0(natts * sizeof(Datum));
- nulls = (bool *) palloc(natts * sizeof(bool));
+ dvalues = (Datum *) eval_mcontext_alloc0(estate, natts * sizeof(Datum));
+ nulls = (bool *) eval_mcontext_alloc(estate, natts * sizeof(bool));
for (i = 0; i < natts; i++)
{
@@ -5978,16 +6150,13 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
tuple = heap_form_tuple(tupdesc, dvalues, nulls);
- pfree(dvalues);
- pfree(nulls);
-
return tuple;
}
/* ----------
* get_tuple_from_datum extract a tuple from a composite Datum
*
- * Returns a freshly palloc'd HeapTuple.
+ * Returns a HeapTuple, freshly palloc'd in caller's context.
*
* Note: it's caller's responsibility to be sure value is of composite type.
* ----------
@@ -6074,7 +6243,7 @@ exec_move_row_from_datum(PLpgSQL_execstate *estate,
/* ----------
* convert_value_to_string Convert a non-null Datum to C string
*
- * Note: the result is in the estate's eval_econtext, and will be cleared
+ * Note: the result is in the estate's eval_mcontext, and will be cleared
* by the next exec_eval_cleanup() call. The invoked output function might
* leave additional cruft there as well, so just pfree'ing the result string
* would not be enough to avoid memory leaks if we did not do it like this.
@@ -6094,7 +6263,7 @@ convert_value_to_string(PLpgSQL_execstate *estate, Datum value, Oid valtype)
Oid typoutput;
bool typIsVarlena;
- oldcontext = MemoryContextSwitchTo(estate->eval_econtext->ecxt_per_tuple_memory);
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
getTypeOutputInfo(valtype, &typoutput, &typIsVarlena);
result = OidOutputFunctionCall(typoutput, value);
MemoryContextSwitchTo(oldcontext);
@@ -6109,7 +6278,7 @@ convert_value_to_string(PLpgSQL_execstate *estate, Datum value, Oid valtype)
* unlikely that a cast operation would produce null from non-null or vice
* versa, that could happen in principle.
*
- * Note: the estate's eval_econtext is used for temporary storage, and may
+ * Note: the estate's eval_mcontext is used for temporary storage, and may
* also contain the result Datum if we have to do a conversion to a pass-
* by-reference data type. Be sure to do an exec_eval_cleanup() call when
* done with the result.
@@ -6137,7 +6306,7 @@ exec_cast_value(PLpgSQL_execstate *estate,
ExprContext *econtext = estate->eval_econtext;
MemoryContext oldcontext;
- oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
econtext->caseValue_datum = value;
econtext->caseValue_isNull = *isnull;
@@ -6145,7 +6314,7 @@ exec_cast_value(PLpgSQL_execstate *estate,
cast_entry->cast_in_use = true;
value = ExecEvalExpr(cast_entry->cast_exprstate, econtext,
- isnull, NULL);
+ isnull);
cast_entry->cast_in_use = false;
@@ -6194,10 +6363,10 @@ get_cast_hashentry(PLpgSQL_execstate *estate,
/*
* Since we could easily fail (no such coercion), construct a
- * temporary coercion expression tree in a short-lived context, then
- * if successful copy it to cast_hash_context.
+ * temporary coercion expression tree in the short-lived
+ * eval_mcontext, then if successful copy it to cast_hash_context.
*/
- oldcontext = MemoryContextSwitchTo(estate->eval_econtext->ecxt_per_tuple_memory);
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
/*
* We use a CaseTestExpr as the base of the coercion tree, since it's
@@ -6527,6 +6696,9 @@ exec_simple_check_node(Node *node)
return TRUE;
}
+ case T_SQLValueFunction:
+ return TRUE;
+
case T_XmlExpr:
{
XmlExpr *expr = (XmlExpr *) node;
@@ -6578,12 +6750,13 @@ exec_simple_check_node(Node *node)
* ----------
*/
static void
-exec_simple_check_plan(PLpgSQL_expr *expr)
+exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
{
List *plansources;
CachedPlanSource *plansource;
Query *query;
CachedPlan *cplan;
+ MemoryContext oldcontext;
/*
* Initialize to "not simple", and remember the plan generation number we
@@ -6632,6 +6805,7 @@ exec_simple_check_plan(PLpgSQL_expr *expr)
*/
if (query->hasAggs ||
query->hasWindowFuncs ||
+ query->hasTargetSRFs ||
query->hasSubLinks ||
query->hasForUpdate ||
query->cteList ||
@@ -6654,10 +6828,13 @@ exec_simple_check_plan(PLpgSQL_expr *expr)
/*
* OK, it seems worth constructing a plan for more careful checking.
+ *
+ * Get the generic plan for the query. If replanning is needed, do that
+ * work in the eval_mcontext.
*/
-
- /* Get the generic plan for the query */
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
cplan = SPI_plan_get_cached_plan(expr->plan);
+ MemoryContextSwitchTo(oldcontext);
/* Can't fail, because we checked for a single CachedPlanSource above */
Assert(cplan != NULL);
@@ -6691,13 +6868,11 @@ exec_simple_recheck_plan(PLpgSQL_expr *expr, CachedPlan *cplan)
*/
if (list_length(cplan->stmt_list) != 1)
return;
- stmt = (PlannedStmt *) linitial(cplan->stmt_list);
+ stmt = linitial_node(PlannedStmt, cplan->stmt_list);
/*
* 2. It must be a RESULT plan --> no scan's required
*/
- if (!IsA(stmt, PlannedStmt))
- return;
if (stmt->commandType != CMD_SELECT)
return;
plan = stmt->planTree;
@@ -7041,23 +7216,30 @@ assign_text_var(PLpgSQL_execstate *estate, PLpgSQL_var *var, const char *str)
/*
* exec_eval_using_params --- evaluate params of USING clause
+ *
+ * The result data structure is created in the stmt_mcontext, and should
+ * be freed by resetting that context.
*/
static PreparedParamsData *
exec_eval_using_params(PLpgSQL_execstate *estate, List *params)
{
PreparedParamsData *ppd;
+ MemoryContext stmt_mcontext = get_stmt_mcontext(estate);
int nargs;
int i;
ListCell *lc;
- ppd = (PreparedParamsData *) palloc(sizeof(PreparedParamsData));
+ ppd = (PreparedParamsData *)
+ MemoryContextAlloc(stmt_mcontext, sizeof(PreparedParamsData));
nargs = list_length(params);
ppd->nargs = nargs;
- ppd->types = (Oid *) palloc(nargs * sizeof(Oid));
- ppd->values = (Datum *) palloc(nargs * sizeof(Datum));
- ppd->nulls = (char *) palloc(nargs * sizeof(char));
- ppd->freevals = (bool *) palloc(nargs * sizeof(bool));
+ ppd->types = (Oid *)
+ MemoryContextAlloc(stmt_mcontext, nargs * sizeof(Oid));
+ ppd->values = (Datum *)
+ MemoryContextAlloc(stmt_mcontext, nargs * sizeof(Datum));
+ ppd->nulls = (char *)
+ MemoryContextAlloc(stmt_mcontext, nargs * sizeof(char));
i = 0;
foreach(lc, params)
@@ -7065,13 +7247,15 @@ exec_eval_using_params(PLpgSQL_execstate *estate, List *params)
PLpgSQL_expr *param = (PLpgSQL_expr *) lfirst(lc);
bool isnull;
int32 ppdtypmod;
+ MemoryContext oldcontext;
ppd->values[i] = exec_eval_expr(estate, param,
&isnull,
&ppd->types[i],
&ppdtypmod);
ppd->nulls[i] = isnull ? 'n' : ' ';
- ppd->freevals[i] = false;
+
+ oldcontext = MemoryContextSwitchTo(stmt_mcontext);
if (ppd->types[i] == UNKNOWNOID)
{
@@ -7084,12 +7268,9 @@ exec_eval_using_params(PLpgSQL_execstate *estate, List *params)
*/
ppd->types[i] = TEXTOID;
if (!isnull)
- {
ppd->values[i] = CStringGetTextDatum(DatumGetCString(ppd->values[i]));
- ppd->freevals[i] = true;
- }
}
- /* pass-by-ref non null values must be copied into plpgsql context */
+ /* pass-by-ref non null values must be copied into stmt_mcontext */
else if (!isnull)
{
int16 typLen;
@@ -7097,12 +7278,11 @@ exec_eval_using_params(PLpgSQL_execstate *estate, List *params)
get_typlenbyval(ppd->types[i], &typLen, &typByVal);
if (!typByVal)
- {
ppd->values[i] = datumCopy(ppd->values[i], typByVal, typLen);
- ppd->freevals[i] = true;
- }
}
+ MemoryContextSwitchTo(oldcontext);
+
exec_eval_cleanup(estate);
i++;
@@ -7112,29 +7292,12 @@ exec_eval_using_params(PLpgSQL_execstate *estate, List *params)
}
/*
- * free_params_data --- pfree all pass-by-reference values used in USING clause
- */
-static void
-free_params_data(PreparedParamsData *ppd)
-{
- int i;
-
- for (i = 0; i < ppd->nargs; i++)
- {
- if (ppd->freevals[i])
- pfree(DatumGetPointer(ppd->values[i]));
- }
-
- pfree(ppd->types);
- pfree(ppd->values);
- pfree(ppd->nulls);
- pfree(ppd->freevals);
-
- pfree(ppd);
-}
-
-/*
* Open portal for dynamic query
+ *
+ * Caution: this resets the stmt_mcontext at exit. We might eventually need
+ * to move that responsibility to the callers, but currently no caller needs
+ * to have statement-lifetime temp data that survives past this, so it's
+ * simpler to do it here.
*/
static Portal
exec_dynquery_with_params(PLpgSQL_execstate *estate,
@@ -7149,6 +7312,7 @@ exec_dynquery_with_params(PLpgSQL_execstate *estate,
Oid restype;
int32 restypmod;
char *querystr;
+ MemoryContext stmt_mcontext = get_stmt_mcontext(estate);
/*
* Evaluate the string expression after the EXECUTE keyword. Its result is
@@ -7163,8 +7327,8 @@ exec_dynquery_with_params(PLpgSQL_execstate *estate,
/* Get the C-String representation */
querystr = convert_value_to_string(estate, query, restype);
- /* copy it out of the temporary context before we clean up */
- querystr = pstrdup(querystr);
+ /* copy it into the stmt_mcontext before we clean up */
+ querystr = MemoryContextStrdup(stmt_mcontext, querystr);
exec_eval_cleanup(estate);
@@ -7184,7 +7348,6 @@ exec_dynquery_with_params(PLpgSQL_execstate *estate,
ppd->values, ppd->nulls,
estate->readonly_func,
cursorOptions);
- free_params_data(ppd);
}
else
{
@@ -7199,7 +7362,9 @@ exec_dynquery_with_params(PLpgSQL_execstate *estate,
if (portal == NULL)
elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
querystr, SPI_result_code_string(SPI_result));
- pfree(querystr);
+
+ /* Release transient data */
+ MemoryContextReset(stmt_mcontext);
return portal;
}
@@ -7207,6 +7372,7 @@ exec_dynquery_with_params(PLpgSQL_execstate *estate,
/*
* Return a formatted string with information about an expression's parameters,
* or NULL if the expression does not take any parameters.
+ * The result is in the eval_mcontext.
*/
static char *
format_expr_params(PLpgSQL_execstate *estate,
@@ -7215,10 +7381,13 @@ format_expr_params(PLpgSQL_execstate *estate,
int paramno;
int dno;
StringInfoData paramstr;
+ MemoryContext oldcontext;
if (!expr->paramnos)
return NULL;
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
+
initStringInfo(&paramstr);
paramno = 0;
dno = -1;
@@ -7260,12 +7429,15 @@ format_expr_params(PLpgSQL_execstate *estate,
paramno++;
}
+ MemoryContextSwitchTo(oldcontext);
+
return paramstr.data;
}
/*
* Return a formatted string with information about PreparedParamsData, or NULL
* if there are no parameters.
+ * The result is in the eval_mcontext.
*/
static char *
format_preparedparamsdata(PLpgSQL_execstate *estate,
@@ -7273,10 +7445,13 @@ format_preparedparamsdata(PLpgSQL_execstate *estate,
{
int paramno;
StringInfoData paramstr;
+ MemoryContext oldcontext;
if (!ppd)
return NULL;
+ oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
+
initStringInfo(&paramstr);
for (paramno = 0; paramno < ppd->nargs; paramno++)
{
@@ -7302,5 +7477,7 @@ format_preparedparamsdata(PLpgSQL_execstate *estate,
}
}
+ MemoryContextSwitchTo(oldcontext);
+
return paramstr.data;
}
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index 27ebebce1e..93f89814b3 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -3,7 +3,7 @@
* pl_funcs.c - Misc functions for the PL/pgSQL
* procedural language
*
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
@@ -13,10 +13,12 @@
*-------------------------------------------------------------------------
*/
-#include "plpgsql.h"
+#include "postgres.h"
#include "utils/memutils.h"
+#include "plpgsql.h"
+
/* ----------
* Local variables for namespace handling
@@ -51,7 +53,7 @@ plpgsql_ns_init(void)
* ----------
*/
void
-plpgsql_ns_push(const char *label, enum PLpgSQL_label_types label_type)
+plpgsql_ns_push(const char *label, PLpgSQL_label_type label_type)
{
if (label == NULL)
label = "";
@@ -89,7 +91,7 @@ plpgsql_ns_top(void)
* ----------
*/
void
-plpgsql_ns_additem(int itemtype, int itemno, const char *name)
+plpgsql_ns_additem(PLpgSQL_nsitem_type itemtype, int itemno, const char *name)
{
PLpgSQL_nsitem *nse;
@@ -231,7 +233,7 @@ plpgsql_ns_find_nearest_loop(PLpgSQL_nsitem *ns_cur)
const char *
plpgsql_stmt_typename(PLpgSQL_stmt *stmt)
{
- switch ((enum PLpgSQL_stmt_types) stmt->cmd_type)
+ switch (stmt->cmd_type)
{
case PLPGSQL_STMT_BLOCK:
return _("statement block");
@@ -291,7 +293,7 @@ plpgsql_stmt_typename(PLpgSQL_stmt *stmt)
* GET DIAGNOSTICS item name as a string, for use in error messages etc.
*/
const char *
-plpgsql_getdiag_kindname(int kind)
+plpgsql_getdiag_kindname(PLpgSQL_getdiag_kind kind)
{
switch (kind)
{
@@ -367,7 +369,7 @@ static void free_expr(PLpgSQL_expr *expr);
static void
free_stmt(PLpgSQL_stmt *stmt)
{
- switch ((enum PLpgSQL_stmt_types) stmt->cmd_type)
+ switch (stmt->cmd_type)
{
case PLPGSQL_STMT_BLOCK:
free_block((PLpgSQL_stmt_block *) stmt);
@@ -791,7 +793,7 @@ static void
dump_stmt(PLpgSQL_stmt *stmt)
{
printf("%3d:", stmt->lineno);
- switch ((enum PLpgSQL_stmt_types) stmt->cmd_type)
+ switch (stmt->cmd_type)
{
case PLPGSQL_STMT_BLOCK:
dump_block((PLpgSQL_stmt_block *) stmt);
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index bf029860eb..c2e64957a5 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -3,7 +3,7 @@
*
* pl_gram.y - Parser for the PL/pgSQL procedural language
*
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
@@ -13,7 +13,7 @@
*-------------------------------------------------------------------------
*/
-#include "plpgsql.h"
+#include "postgres.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
@@ -23,6 +23,8 @@
#include "parser/scansup.h"
#include "utils/builtins.h"
+#include "plpgsql.h"
+
/* Location tracking support --- simpler than bison's default */
#define YYLLOC_DEFAULT(Current, Rhs, N) \
@@ -180,10 +182,11 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%type <expr> expr_until_then expr_until_loop opt_expr_until_when
%type <expr> opt_exitcond
-%type <ival> assign_var foreach_slice
+%type <datum> assign_var
%type <var> cursor_variable
%type <datum> decl_cursor_arg
%type <forvariable> for_variable
+%type <ival> foreach_slice
%type <stmt> for_control
%type <str> any_identifier opt_block_label opt_loop_label opt_label
@@ -209,7 +212,8 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%type <boolean> getdiag_area_opt
%type <list> getdiag_list
%type <diagitem> getdiag_list_item
-%type <ival> getdiag_item getdiag_target
+%type <datum> getdiag_target
+%type <ival> getdiag_item
%type <ival> opt_scrollable
%type <fetch> opt_fetch_direction
@@ -916,7 +920,7 @@ stmt_assign : assign_var assign_operator expr_until_semi
new = palloc0(sizeof(PLpgSQL_stmt_assign));
new->cmd_type = PLPGSQL_STMT_ASSIGN;
new->lineno = plpgsql_location_to_lineno(@1);
- new->varno = $1;
+ new->varno = $1->dno;
new->expr = $3;
$$ = (PLpgSQL_stmt *)new;
@@ -1014,7 +1018,7 @@ getdiag_list_item : getdiag_target assign_operator getdiag_item
PLpgSQL_diag_item *new;
new = palloc(sizeof(PLpgSQL_diag_item));
- new->target = $1;
+ new->target = $1->dno;
new->kind = $3;
$$ = new;
@@ -1069,17 +1073,16 @@ getdiag_item :
}
;
-getdiag_target : T_DATUM
+getdiag_target : assign_var
{
- check_assignable($1.datum, @1);
- if ($1.datum->dtype == PLPGSQL_DTYPE_ROW ||
- $1.datum->dtype == PLPGSQL_DTYPE_REC)
+ if ($1->dtype == PLPGSQL_DTYPE_ROW ||
+ $1->dtype == PLPGSQL_DTYPE_REC)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable",
- NameOfDatum(&($1))),
+ ((PLpgSQL_variable *) $1)->refname),
parser_errposition(@1)));
- $$ = $1.datum->dno;
+ $$ = $1;
}
| T_WORD
{
@@ -1097,7 +1100,7 @@ getdiag_target : T_DATUM
assign_var : T_DATUM
{
check_assignable($1.datum, @1);
- $$ = $1.datum->dno;
+ $$ = $1.datum;
}
| assign_var '[' expr_until_rightbracket
{
@@ -1106,13 +1109,13 @@ assign_var : T_DATUM
new = palloc0(sizeof(PLpgSQL_arrayelem));
new->dtype = PLPGSQL_DTYPE_ARRAYELEM;
new->subscript = $3;
- new->arrayparentno = $1;
+ new->arrayparentno = $1->dno;
/* initialize cached type data to "not valid" */
new->parenttypoid = InvalidOid;
plpgsql_adddatum((PLpgSQL_datum *) new);
- $$ = new->dno;
+ $$ = (PLpgSQL_datum *) new;
}
;
@@ -2173,7 +2176,13 @@ stmt_null : K_NULL ';'
cursor_variable : T_DATUM
{
- if ($1.datum->dtype != PLPGSQL_DTYPE_VAR)
+ /*
+ * In principle we should support a cursor_variable
+ * that is an array element, but for now we don't, so
+ * just throw an error if next token is '['.
+ */
+ if ($1.datum->dtype != PLPGSQL_DTYPE_VAR ||
+ plpgsql_peek() == '[')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cursor variable must be a simple variable"),
@@ -2853,11 +2862,11 @@ make_execsql_stmt(int firsttoken, int location)
* clause lurking within it, and parse that via read_into_target().
*
* Because INTO is sometimes used in the main SQL grammar, we have to be
- * careful not to take any such usage of INTO as a pl/pgsql INTO clause.
+ * careful not to take any such usage of INTO as a PL/pgSQL INTO clause.
* There are currently three such cases:
*
* 1. SELECT ... INTO. We don't care, we just override that with the
- * pl/pgsql definition.
+ * PL/pgSQL definition.
*
* 2. INSERT INTO. This is relatively easy to recognize since the words
* must appear adjacently; but we can't assume INSERT starts the command,
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index 36868fb273..83ec4530db 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -3,7 +3,7 @@
* pl_handler.c - Handler for the PL/pgSQL
* procedural language
*
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
@@ -13,7 +13,7 @@
*-------------------------------------------------------------------------
*/
-#include "plpgsql.h"
+#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/pg_proc.h"
@@ -24,6 +24,9 @@
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+#include "utils/varlena.h"
+
+#include "plpgsql.h"
static bool plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source);
@@ -287,7 +290,7 @@ PG_FUNCTION_INFO_V1(plpgsql_inline_handler);
Datum
plpgsql_inline_handler(PG_FUNCTION_ARGS)
{
- InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
+ InlineCodeBlock *codeblock = castNode(InlineCodeBlock, DatumGetPointer(PG_GETARG_DATUM(0)));
PLpgSQL_function *func;
FunctionCallInfoData fake_fcinfo;
FmgrInfo flinfo;
@@ -295,8 +298,6 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS)
Datum retval;
int rc;
- Assert(IsA(codeblock, InlineCodeBlock));
-
/*
* Connect to SPI manager
*/
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index 2737fff3fb..553be8c93c 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -4,7 +4,7 @@
* lexical scanning for PL/pgSQL
*
*
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
@@ -13,13 +13,15 @@
*
*-------------------------------------------------------------------------
*/
-#include "plpgsql.h"
+#include "postgres.h"
#include "mb/pg_wchar.h"
#include "parser/scanner.h"
+#include "plpgsql.h"
#include "pl_gram.h" /* must be after parser/scanner.h */
+
#define PG_KEYWORD(a,b,c) {a,b,c},
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 140bf4badd..43e7eb317b 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -3,7 +3,7 @@
* plpgsql.h - Definitions for the PL/pgSQL
* procedural language
*
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
@@ -16,8 +16,6 @@
#ifndef PLPGSQL_H
#define PLPGSQL_H
-#include "postgres.h"
-
#include "access/xact.h"
#include "commands/event_trigger.h"
#include "commands/trigger.h"
@@ -34,34 +32,31 @@
#undef _
#define _(x) dgettext(TEXTDOMAIN, x)
-/* ----------
+/*
* Compiler's namespace item types
- * ----------
*/
-enum
+typedef enum PLpgSQL_nsitem_type
{
PLPGSQL_NSTYPE_LABEL,
PLPGSQL_NSTYPE_VAR,
PLPGSQL_NSTYPE_ROW,
PLPGSQL_NSTYPE_REC
-};
+} PLpgSQL_nsitem_type;
-/* ----------
+/*
* A PLPGSQL_NSTYPE_LABEL stack entry must be one of these types
- * ----------
*/
-enum PLpgSQL_label_types
+typedef enum PLpgSQL_label_type
{
PLPGSQL_LABEL_BLOCK, /* DECLARE/BEGIN block */
PLPGSQL_LABEL_LOOP, /* looping construct */
PLPGSQL_LABEL_OTHER /* anything else */
-};
+} PLpgSQL_label_type;
-/* ----------
+/*
* Datum array node types
- * ----------
*/
-enum
+typedef enum PLpgSQL_datum_type
{
PLPGSQL_DTYPE_VAR,
PLPGSQL_DTYPE_ROW,
@@ -69,25 +64,23 @@ enum
PLPGSQL_DTYPE_RECFIELD,
PLPGSQL_DTYPE_ARRAYELEM,
PLPGSQL_DTYPE_EXPR
-};
+} PLpgSQL_datum_type;
-/* ----------
+/*
* Variants distinguished in PLpgSQL_type structs
- * ----------
*/
-enum
+typedef enum PLpgSQL_type_type
{
PLPGSQL_TTYPE_SCALAR, /* scalar types and domains */
PLPGSQL_TTYPE_ROW, /* composite types */
PLPGSQL_TTYPE_REC, /* RECORD pseudotype */
PLPGSQL_TTYPE_PSEUDO /* other pseudotypes */
-};
+} PLpgSQL_type_type;
-/* ----------
+/*
* Execution tree node types
- * ----------
*/
-enum PLpgSQL_stmt_types
+typedef enum PLpgSQL_stmt_type
{
PLPGSQL_STMT_BLOCK,
PLPGSQL_STMT_ASSIGN,
@@ -113,12 +106,10 @@ enum PLpgSQL_stmt_types
PLPGSQL_STMT_FETCH,
PLPGSQL_STMT_CLOSE,
PLPGSQL_STMT_PERFORM
-};
-
+} PLpgSQL_stmt_type;
-/* ----------
+/*
* Execution node return codes
- * ----------
*/
enum
{
@@ -128,11 +119,10 @@ enum
PLPGSQL_RC_CONTINUE
};
-/* ----------
+/*
* GET DIAGNOSTICS information items
- * ----------
*/
-enum
+typedef enum PLpgSQL_getdiag_kind
{
PLPGSQL_GETDIAG_ROW_COUNT,
PLPGSQL_GETDIAG_RESULT_OID,
@@ -147,13 +137,12 @@ enum
PLPGSQL_GETDIAG_MESSAGE_TEXT,
PLPGSQL_GETDIAG_TABLE_NAME,
PLPGSQL_GETDIAG_SCHEMA_NAME
-};
+} PLpgSQL_getdiag_kind;
-/* --------
+/*
* RAISE statement options
- * --------
*/
-enum
+typedef enum PLpgSQL_raise_option_type
{
PLPGSQL_RAISEOPTION_ERRCODE,
PLPGSQL_RAISEOPTION_MESSAGE,
@@ -164,13 +153,12 @@ enum
PLPGSQL_RAISEOPTION_DATATYPE,
PLPGSQL_RAISEOPTION_TABLE,
PLPGSQL_RAISEOPTION_SCHEMA
-};
+} PLpgSQL_raise_option_type;
-/* --------
+/*
* Behavioral modes for plpgsql variable resolution
- * --------
*/
-typedef enum
+typedef enum PLpgSQL_resolve_option
{
PLPGSQL_RESOLVE_ERROR, /* throw error if ambiguous */
PLPGSQL_RESOLVE_VARIABLE, /* prefer plpgsql var to table column */
@@ -182,12 +170,14 @@ typedef enum
* Node and structure definitions
**********************************************************************/
-
-typedef struct
-{ /* Postgres data type */
+/*
+ * Postgres data type
+ */
+typedef struct PLpgSQL_type
+{
char *typname; /* (simple) name of the type */
Oid typoid; /* OID of the data type */
- int ttype; /* PLPGSQL_TTYPE_ code */
+ PLpgSQL_type_type ttype; /* PLPGSQL_TTYPE_ code */
int16 typlen; /* stuff copied from its pg_type entry */
bool typbyval;
char typtype;
@@ -197,32 +187,38 @@ typedef struct
int32 atttypmod; /* typmod (taken from someplace else) */
} PLpgSQL_type;
-
/*
+ * Generic datum array item
+ *
* PLpgSQL_datum is the common supertype for PLpgSQL_expr, PLpgSQL_var,
* PLpgSQL_row, PLpgSQL_rec, PLpgSQL_recfield, and PLpgSQL_arrayelem
*/
-typedef struct
-{ /* Generic datum array item */
- int dtype;
+typedef struct PLpgSQL_datum
+{
+ PLpgSQL_datum_type dtype;
int dno;
} PLpgSQL_datum;
/*
+ * Scalar or composite variable
+ *
* The variants PLpgSQL_var, PLpgSQL_row, and PLpgSQL_rec share these
* fields
*/
-typedef struct
-{ /* Scalar or composite variable */
- int dtype;
+typedef struct PLpgSQL_variable
+{
+ PLpgSQL_datum_type dtype;
int dno;
char *refname;
int lineno;
} PLpgSQL_variable;
+/*
+ * SQL Query to plan and execute
+ */
typedef struct PLpgSQL_expr
-{ /* SQL Query to plan and execute */
- int dtype;
+{
+ PLpgSQL_datum_type dtype;
int dno;
char *query;
SPIPlanPtr plan;
@@ -252,10 +248,12 @@ typedef struct PLpgSQL_expr
LocalTransactionId expr_simple_lxid;
} PLpgSQL_expr;
-
-typedef struct
-{ /* Scalar variable */
- int dtype;
+/*
+ * Scalar variable
+ */
+typedef struct PLpgSQL_var
+{
+ PLpgSQL_datum_type dtype;
int dno;
char *refname;
int lineno;
@@ -273,19 +271,20 @@ typedef struct
bool freeval;
} PLpgSQL_var;
-
-typedef struct
-{ /* Row variable */
- int dtype;
+/*
+ * Row variable
+ */
+typedef struct PLpgSQL_row
+{
+ PLpgSQL_datum_type dtype;
int dno;
char *refname;
int lineno;
+ /* Note: TupleDesc is only set up for named rowtypes, else it is NULL. */
TupleDesc rowtupdesc;
/*
- * Note: TupleDesc is only set up for named rowtypes, else it is NULL.
- *
* Note: if the underlying rowtype contains a dropped column, the
* corresponding fieldnames[] entry will be NULL, and there is no
* corresponding var (varnos[] will be -1).
@@ -295,10 +294,12 @@ typedef struct
int *varnos;
} PLpgSQL_row;
-
-typedef struct
-{ /* Record variable (non-fixed structure) */
- int dtype;
+/*
+ * Record variable (non-fixed structure)
+ */
+typedef struct PLpgSQL_rec
+{
+ PLpgSQL_datum_type dtype;
int dno;
char *refname;
int lineno;
@@ -309,22 +310,27 @@ typedef struct
bool freetupdesc;
} PLpgSQL_rec;
-
-typedef struct
-{ /* Field in record */
- int dtype;
+/*
+ * Field in record
+ */
+typedef struct PLpgSQL_recfield
+{
+ PLpgSQL_datum_type dtype;
int dno;
char *fieldname;
int recparentno; /* dno of parent record */
} PLpgSQL_recfield;
-
-typedef struct
-{ /* Element of array variable */
- int dtype;
+/*
+ * Element of array variable
+ */
+typedef struct PLpgSQL_arrayelem
+{
+ PLpgSQL_datum_type dtype;
int dno;
PLpgSQL_expr *subscript;
int arrayparentno; /* dno of parent array variable */
+
/* Remaining fields are cached info about the array variable's type */
Oid parenttypoid; /* type of array variable; 0 if not yet set */
int32 parenttypmod; /* typmod of array variable */
@@ -337,50 +343,67 @@ typedef struct
char elemtypalign; /* typalign of element type */
} PLpgSQL_arrayelem;
-
+/*
+ * Item in the compilers namespace tree
+ */
typedef struct PLpgSQL_nsitem
-{ /* Item in the compilers namespace tree */
- int itemtype;
+{
+ PLpgSQL_nsitem_type itemtype;
+
+ /*
+ * For labels, itemno is a value of enum PLpgSQL_label_type. For other
+ * itemtypes, itemno is the associated PLpgSQL_datum's dno.
+ */
int itemno;
- /* For labels, itemno is a value of enum PLpgSQL_label_types. */
- /* For other itemtypes, itemno is the associated PLpgSQL_datum's dno. */
struct PLpgSQL_nsitem *prev;
char name[FLEXIBLE_ARRAY_MEMBER]; /* nul-terminated string */
} PLpgSQL_nsitem;
-
-typedef struct
-{ /* Generic execution node */
- int cmd_type;
+/*
+ * Generic execution node
+ */
+typedef struct PLpgSQL_stmt
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
} PLpgSQL_stmt;
-
+/*
+ * One EXCEPTION condition name
+ */
typedef struct PLpgSQL_condition
-{ /* One EXCEPTION condition name */
+{
int sqlerrstate; /* SQLSTATE code */
char *condname; /* condition name (for debugging) */
struct PLpgSQL_condition *next;
} PLpgSQL_condition;
-typedef struct
+/*
+ * EXCEPTION block
+ */
+typedef struct PLpgSQL_exception_block
{
int sqlstate_varno;
int sqlerrm_varno;
List *exc_list; /* List of WHEN clauses */
} PLpgSQL_exception_block;
-typedef struct
-{ /* One EXCEPTION ... WHEN clause */
+/*
+ * One EXCEPTION ... WHEN clause
+ */
+typedef struct PLpgSQL_exception
+{
int lineno;
PLpgSQL_condition *conditions;
List *action; /* List of statements */
} PLpgSQL_exception;
-
-typedef struct
-{ /* Block of statements */
- int cmd_type;
+/*
+ * Block of statements
+ */
+typedef struct PLpgSQL_stmt_block
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
char *label;
List *body; /* List of statements */
@@ -389,40 +412,53 @@ typedef struct
PLpgSQL_exception_block *exceptions;
} PLpgSQL_stmt_block;
-
-typedef struct
-{ /* Assign statement */
- int cmd_type;
+/*
+ * Assign statement
+ */
+typedef struct PLpgSQL_stmt_assign
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
int varno;
PLpgSQL_expr *expr;
} PLpgSQL_stmt_assign;
-typedef struct
-{ /* PERFORM statement */
- int cmd_type;
+/*
+ * PERFORM statement
+ */
+typedef struct PLpgSQL_stmt_perform
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
PLpgSQL_expr *expr;
} PLpgSQL_stmt_perform;
-typedef struct
-{ /* Get Diagnostics item */
- int kind; /* id for diagnostic value desired */
+/*
+ * GET DIAGNOSTICS item
+ */
+typedef struct PLpgSQL_diag_item
+{
+ PLpgSQL_getdiag_kind kind; /* id for diagnostic value desired */
int target; /* where to assign it */
} PLpgSQL_diag_item;
-typedef struct
-{ /* Get Diagnostics statement */
- int cmd_type;
+/*
+ * GET DIAGNOSTICS statement
+ */
+typedef struct PLpgSQL_stmt_getdiag
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
bool is_stacked; /* STACKED or CURRENT diagnostics area? */
List *diag_items; /* List of PLpgSQL_diag_item */
} PLpgSQL_stmt_getdiag;
-
-typedef struct
-{ /* IF statement */
- int cmd_type;
+/*
+ * IF statement
+ */
+typedef struct PLpgSQL_stmt_if
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
PLpgSQL_expr *cond; /* boolean expression for THEN */
List *then_body; /* List of statements */
@@ -430,17 +466,22 @@ typedef struct
List *else_body; /* List of statements */
} PLpgSQL_stmt_if;
-typedef struct /* one ELSIF arm of IF statement */
+/*
+ * one ELSIF arm of IF statement
+ */
+typedef struct PLpgSQL_if_elsif
{
int lineno;
PLpgSQL_expr *cond; /* boolean expression for this case */
List *stmts; /* List of statements */
} PLpgSQL_if_elsif;
-
-typedef struct /* CASE statement */
+/*
+ * CASE statement
+ */
+typedef struct PLpgSQL_stmt_case
{
- int cmd_type;
+ PLpgSQL_stmt_type cmd_type;
int lineno;
PLpgSQL_expr *t_expr; /* test expression, or NULL if none */
int t_varno; /* var to store test expression value into */
@@ -449,36 +490,45 @@ typedef struct /* CASE statement */
List *else_stmts; /* List of statements */
} PLpgSQL_stmt_case;
-typedef struct /* one arm of CASE statement */
+/*
+ * one arm of CASE statement
+ */
+typedef struct PLpgSQL_case_when
{
int lineno;
PLpgSQL_expr *expr; /* boolean expression for this case */
List *stmts; /* List of statements */
} PLpgSQL_case_when;
-
-typedef struct
-{ /* Unconditional LOOP statement */
- int cmd_type;
+/*
+ * Unconditional LOOP statement
+ */
+typedef struct PLpgSQL_stmt_loop
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
char *label;
List *body; /* List of statements */
} PLpgSQL_stmt_loop;
-
-typedef struct
-{ /* WHILE cond LOOP statement */
- int cmd_type;
+/*
+ * WHILE cond LOOP statement
+ */
+typedef struct PLpgSQL_stmt_while
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
char *label;
PLpgSQL_expr *cond;
List *body; /* List of statements */
} PLpgSQL_stmt_while;
-
-typedef struct
-{ /* FOR statement with integer loopvar */
- int cmd_type;
+/*
+ * FOR statement with integer loopvar
+ */
+typedef struct PLpgSQL_stmt_fori
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
char *label;
PLpgSQL_var *var;
@@ -489,15 +539,14 @@ typedef struct
List *body; /* List of statements */
} PLpgSQL_stmt_fori;
-
/*
* PLpgSQL_stmt_forq represents a FOR statement running over a SQL query.
* It is the common supertype of PLpgSQL_stmt_fors, PLpgSQL_stmt_forc
* and PLpgSQL_dynfors.
*/
-typedef struct
+typedef struct PLpgSQL_stmt_forq
{
- int cmd_type;
+ PLpgSQL_stmt_type cmd_type;
int lineno;
char *label;
PLpgSQL_rec *rec;
@@ -505,9 +554,12 @@ typedef struct
List *body; /* List of statements */
} PLpgSQL_stmt_forq;
-typedef struct
-{ /* FOR statement running over SELECT */
- int cmd_type;
+/*
+ * FOR statement running over SELECT
+ */
+typedef struct PLpgSQL_stmt_fors
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
char *label;
PLpgSQL_rec *rec;
@@ -517,9 +569,12 @@ typedef struct
PLpgSQL_expr *query;
} PLpgSQL_stmt_fors;
-typedef struct
-{ /* FOR statement running over cursor */
- int cmd_type;
+/*
+ * FOR statement running over cursor
+ */
+typedef struct PLpgSQL_stmt_forc
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
char *label;
PLpgSQL_rec *rec;
@@ -530,9 +585,12 @@ typedef struct
PLpgSQL_expr *argquery; /* cursor arguments if any */
} PLpgSQL_stmt_forc;
-typedef struct
-{ /* FOR statement running over EXECUTE */
- int cmd_type;
+/*
+ * FOR statement running over EXECUTE
+ */
+typedef struct PLpgSQL_stmt_dynfors
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
char *label;
PLpgSQL_rec *rec;
@@ -543,10 +601,12 @@ typedef struct
List *params; /* USING expressions */
} PLpgSQL_stmt_dynfors;
-
-typedef struct
-{ /* FOREACH item in array loop */
- int cmd_type;
+/*
+ * FOREACH item in array loop
+ */
+typedef struct PLpgSQL_stmt_foreach_a
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
char *label;
int varno; /* loop target variable */
@@ -555,10 +615,12 @@ typedef struct
List *body; /* List of statements */
} PLpgSQL_stmt_foreach_a;
-
-typedef struct
-{ /* OPEN a curvar */
- int cmd_type;
+/*
+ * OPEN a curvar
+ */
+typedef struct PLpgSQL_stmt_open
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
int curvar;
int cursor_options;
@@ -569,10 +631,12 @@ typedef struct
List *params; /* USING expressions */
} PLpgSQL_stmt_open;
-
-typedef struct
-{ /* FETCH or MOVE statement */
- int cmd_type;
+/*
+ * FETCH or MOVE statement
+ */
+typedef struct PLpgSQL_stmt_fetch
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
PLpgSQL_rec *rec; /* target, as record or row */
PLpgSQL_row *row;
@@ -584,53 +648,68 @@ typedef struct
bool returns_multiple_rows; /* can return more than one row? */
} PLpgSQL_stmt_fetch;
-
-typedef struct
-{ /* CLOSE curvar */
- int cmd_type;
+/*
+ * CLOSE curvar
+ */
+typedef struct PLpgSQL_stmt_close
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
int curvar;
} PLpgSQL_stmt_close;
-
-typedef struct
-{ /* EXIT or CONTINUE statement */
- int cmd_type;
+/*
+ * EXIT or CONTINUE statement
+ */
+typedef struct PLpgSQL_stmt_exit
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
bool is_exit; /* Is this an exit or a continue? */
char *label; /* NULL if it's an unlabelled EXIT/CONTINUE */
PLpgSQL_expr *cond;
} PLpgSQL_stmt_exit;
-
-typedef struct
-{ /* RETURN statement */
- int cmd_type;
+/*
+ * RETURN statement
+ */
+typedef struct PLpgSQL_stmt_return
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
PLpgSQL_expr *expr;
int retvarno;
} PLpgSQL_stmt_return;
-typedef struct
-{ /* RETURN NEXT statement */
- int cmd_type;
+/*
+ * RETURN NEXT statement
+ */
+typedef struct PLpgSQL_stmt_return_next
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
PLpgSQL_expr *expr;
int retvarno;
} PLpgSQL_stmt_return_next;
-typedef struct
-{ /* RETURN QUERY statement */
- int cmd_type;
+/*
+ * RETURN QUERY statement
+ */
+typedef struct PLpgSQL_stmt_return_query
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
PLpgSQL_expr *query; /* if static query */
PLpgSQL_expr *dynquery; /* if dynamic query (RETURN QUERY EXECUTE) */
List *params; /* USING arguments for dynamic query */
} PLpgSQL_stmt_return_query;
-typedef struct
-{ /* RAISE statement */
- int cmd_type;
+/*
+ * RAISE statement
+ */
+typedef struct PLpgSQL_stmt_raise
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
int elog_level;
char *condname; /* condition name, SQLSTATE, or NULL */
@@ -639,37 +718,48 @@ typedef struct
List *options; /* list of PLpgSQL_raise_option */
} PLpgSQL_stmt_raise;
-typedef struct
-{ /* RAISE statement option */
- int opt_type;
+/*
+ * RAISE statement option
+ */
+typedef struct PLpgSQL_raise_option
+{
+ PLpgSQL_raise_option_type opt_type;
PLpgSQL_expr *expr;
} PLpgSQL_raise_option;
-typedef struct
-{ /* ASSERT statement */
- int cmd_type;
+/*
+ * ASSERT statement
+ */
+typedef struct PLpgSQL_stmt_assert
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
PLpgSQL_expr *cond;
PLpgSQL_expr *message;
} PLpgSQL_stmt_assert;
-typedef struct
-{ /* Generic SQL statement to execute */
- int cmd_type;
+/*
+ * Generic SQL statement to execute
+ */
+typedef struct PLpgSQL_stmt_execsql
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
PLpgSQL_expr *sqlstmt;
- bool mod_stmt; /* is the stmt INSERT/UPDATE/DELETE? */
- /* note: mod_stmt is set when we plan the query */
+ bool mod_stmt; /* is the stmt INSERT/UPDATE/DELETE? Note:
+ * mod_stmt is set when we plan the query */
bool into; /* INTO supplied? */
bool strict; /* INTO STRICT flag */
PLpgSQL_rec *rec; /* INTO target, if record */
PLpgSQL_row *row; /* INTO target, if row */
} PLpgSQL_stmt_execsql;
-
-typedef struct
-{ /* Dynamic SQL string to execute */
- int cmd_type;
+/*
+ * Dynamic SQL string to execute
+ */
+typedef struct PLpgSQL_stmt_dynexecute
+{
+ PLpgSQL_stmt_type cmd_type;
int lineno;
PLpgSQL_expr *query; /* string expression */
bool into; /* INTO supplied? */
@@ -679,9 +769,11 @@ typedef struct
List *params; /* USING expressions */
} PLpgSQL_stmt_dynexecute;
-
+/*
+ * Hash lookup key for functions
+ */
typedef struct PLpgSQL_func_hashkey
-{ /* Hash lookup key for functions */
+{
Oid funcOid;
bool isTrigger; /* true if called as a trigger */
@@ -689,12 +781,12 @@ typedef struct PLpgSQL_func_hashkey
/* be careful that pad bytes in this struct get zeroed! */
/*
- * For a trigger function, the OID of the relation triggered on is part of
- * the hash key --- we want to compile the trigger separately for each
- * relation it is used with, in case the rowtype is different. Zero if
- * not called as a trigger.
+ * For a trigger function, the OID of the trigger is part of the hash key
+ * --- we want to compile the trigger function separately for each trigger
+ * it is used with, in case the rowtype or transition table names are
+ * different. Zero if not called as a trigger.
*/
- Oid trigrelOid;
+ Oid trigOid;
/*
* We must include the input collation as part of the hash key too,
@@ -710,6 +802,9 @@ typedef struct PLpgSQL_func_hashkey
Oid argtypes[FUNC_MAX_ARGS];
} PLpgSQL_func_hashkey;
+/*
+ * Trigger type
+ */
typedef enum PLpgSQL_trigtype
{
PLPGSQL_DML_TRIGGER,
@@ -717,8 +812,11 @@ typedef enum PLpgSQL_trigtype
PLPGSQL_NOT_TRIGGER
} PLpgSQL_trigtype;
+/*
+ * Complete compiled function
+ */
typedef struct PLpgSQL_function
-{ /* Complete compiled function */
+{
char *fn_signature;
Oid fn_oid;
TransactionId fn_xmin;
@@ -777,9 +875,11 @@ typedef struct PLpgSQL_function
unsigned long use_count;
} PLpgSQL_function;
-
+/*
+ * Runtime execution data
+ */
typedef struct PLpgSQL_execstate
-{ /* Runtime execution data */
+{
PLpgSQL_function *func; /* function being executed */
Datum retval;
@@ -814,10 +914,14 @@ typedef struct PLpgSQL_execstate
/* EState to use for "simple" expression evaluation */
EState *simple_eval_estate;
- /* Lookup table to use for executing type casts */
+ /* lookup table to use for executing type casts */
HTAB *cast_hash;
MemoryContext cast_hash_context;
+ /* memory context for statement-lifespan temporary values */
+ MemoryContext stmt_mcontext; /* current stmt context, or NULL if none */
+ MemoryContext stmt_mcontext_parent; /* parent of current context */
+
/* temporary state for results from evaluation of query or expr */
SPITupleTable *eval_tuptable;
uint64 eval_processed;
@@ -831,7 +935,6 @@ typedef struct PLpgSQL_execstate
void *plugin_info; /* reserved for use by optional plugin */
} PLpgSQL_execstate;
-
/*
* A PLpgSQL_plugin structure represents an instrumentation plugin.
* To instrument PL/pgSQL, a plugin library must access the rendezvous
@@ -846,24 +949,23 @@ typedef struct PLpgSQL_execstate
* (if the pointers are non-NULL) to give the plugin a chance to watch
* what we are doing.
*
- * func_setup is called when we start a function, before we've initialized
- * the local variables defined by the function.
+ * func_setup is called when we start a function, before we've initialized
+ * the local variables defined by the function.
*
- * func_beg is called when we start a function, after we've initialized
- * the local variables.
+ * func_beg is called when we start a function, after we've initialized
+ * the local variables.
*
- * func_end is called at the end of a function.
+ * func_end is called at the end of a function.
*
- * stmt_beg and stmt_end are called before and after (respectively) each
- * statement.
+ * stmt_beg and stmt_end are called before and after (respectively) each
+ * statement.
*
* Also, immediately before any call to func_setup, PL/pgSQL fills in the
* error_callback and assign_expr fields with pointers to its own
* plpgsql_exec_error_callback and exec_assign_expr functions. This is
* a somewhat ad-hoc expedient to simplify life for debugger plugins.
*/
-
-typedef struct
+typedef struct PLpgSQL_plugin
{
/* Function pointers set up by the plugin */
void (*func_setup) (PLpgSQL_execstate *estate, PLpgSQL_function *func);
@@ -878,21 +980,22 @@ typedef struct
PLpgSQL_expr *expr);
} PLpgSQL_plugin;
+/*
+ * Struct types used during parsing
+ */
-/* Struct types used during parsing */
-
-typedef struct
+typedef struct PLword
{
char *ident; /* palloc'd converted identifier */
bool quoted; /* Was it double-quoted? */
} PLword;
-typedef struct
+typedef struct PLcword
{
List *idents; /* composite identifiers (list of String) */
} PLcword;
-typedef struct
+typedef struct PLwdatum
{
PLpgSQL_datum *datum; /* referenced variable */
char *ident; /* valid if simple name */
@@ -946,9 +1049,8 @@ extern PLpgSQL_plugin **plpgsql_plugin_ptr;
* Function declarations
**********************************************************************/
-/* ----------
+/*
* Functions in pl_comp.c
- * ----------
*/
extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
bool forValidator);
@@ -979,15 +1081,13 @@ extern void plpgsql_adddatum(PLpgSQL_datum *new);
extern int plpgsql_add_initdatums(int **varnos);
extern void plpgsql_HashTableInit(void);
-/* ----------
+/*
* Functions in pl_handler.c
- * ----------
*/
extern void _PG_init(void);
-/* ----------
+/*
* Functions in pl_exec.c
- * ----------
*/
extern Datum plpgsql_exec_function(PLpgSQL_function *func,
FunctionCallInfo fcinfo,
@@ -1005,16 +1105,15 @@ extern void plpgsql_exec_get_datum_type_info(PLpgSQL_execstate *estate,
PLpgSQL_datum *datum,
Oid *typeid, int32 *typmod, Oid *collation);
-/* ----------
+/*
* Functions for namespace handling in pl_funcs.c
- * ----------
*/
extern void plpgsql_ns_init(void);
extern void plpgsql_ns_push(const char *label,
- enum PLpgSQL_label_types label_type);
+ PLpgSQL_label_type label_type);
extern void plpgsql_ns_pop(void);
extern PLpgSQL_nsitem *plpgsql_ns_top(void);
-extern void plpgsql_ns_additem(int itemtype, int itemno, const char *name);
+extern void plpgsql_ns_additem(PLpgSQL_nsitem_type itemtype, int itemno, const char *name);
extern PLpgSQL_nsitem *plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur, bool localmode,
const char *name1, const char *name2,
const char *name3, int *names_used);
@@ -1022,18 +1121,16 @@ extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur,
const char *name);
extern PLpgSQL_nsitem *plpgsql_ns_find_nearest_loop(PLpgSQL_nsitem *ns_cur);
-/* ----------
+/*
* Other functions in pl_funcs.c
- * ----------
*/
extern const char *plpgsql_stmt_typename(PLpgSQL_stmt *stmt);
-extern const char *plpgsql_getdiag_kindname(int kind);
+extern const char *plpgsql_getdiag_kindname(PLpgSQL_getdiag_kind kind);
extern void plpgsql_free_function_memory(PLpgSQL_function *func);
extern void plpgsql_dumptree(PLpgSQL_function *func);
-/* ----------
+/*
* Scanner functions in pl_scanner.c
- * ----------
*/
extern int plpgsql_base_yylex(void);
extern int plpgsql_yylex(void);
@@ -1051,9 +1148,8 @@ extern int plpgsql_latest_lineno(void);
extern void plpgsql_scanner_init(const char *str);
extern void plpgsql_scanner_finish(void);
-/* ----------
+/*
* Externs in gram.y
- * ----------
*/
extern int plpgsql_yyparse(void);
diff --git a/src/pl/plpgsql/src/po/fr.po b/src/pl/plpgsql/src/po/fr.po
index 9ef7d235f0..485bf36a54 100644
--- a/src/pl/plpgsql/src/po/fr.po
+++ b/src/pl/plpgsql/src/po/fr.po
@@ -1,7 +1,7 @@
# translation of plpgsql.po to fr_fr
# french message translation file for plpgsql
#
-# Use these quotes: %s
+# Use these quotes: « %s »
# Guillaume Lelarge <guillaume@lelarge.info>, 2009.
#
msgid ""
@@ -14,7 +14,7 @@ msgstr ""
"Language-Team: French <guillaume@lelarge.info>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 1.8.7.1\n"
@@ -28,13 +28,13 @@ msgstr "les fonctions PL/pgsql ne peuvent pas accepter le type %s"
#, c-format
msgid "could not determine actual return type for polymorphic function \"%s\""
msgstr ""
-"n'a pas pu dterminer le type de retour actuel pour la fonction\n"
-"polymorphique %s "
+"n'a pas pu déterminer le type de retour actuel pour la fonction\n"
+"polymorphique « %s »"
#: pl_comp.c:543
#, c-format
msgid "trigger functions can only be called as triggers"
-msgstr "les fonctions triggers peuvent seulement tre appeles par des triggers"
+msgstr "les fonctions triggers peuvent seulement être appelées par des triggers"
#: pl_comp.c:547 pl_handler.c:433
#, c-format
@@ -44,83 +44,83 @@ msgstr "les fonctions PL/pgsql ne peuvent pas renvoyer le type %s"
#: pl_comp.c:588
#, c-format
msgid "trigger functions cannot have declared arguments"
-msgstr "les fonctions triggers ne peuvent pas avoir des arguments dclars"
+msgstr "les fonctions triggers ne peuvent pas avoir des arguments déclarés"
#: pl_comp.c:589
#, c-format
msgid "The arguments of the trigger can be accessed through TG_NARGS and TG_ARGV instead."
msgstr ""
-"Les arguments du trigger peuvent tre accds via TG_NARGS et TG_ARGV \n"
+"Les arguments du trigger peuvent être accédés via TG_NARGS et TG_ARGV à\n"
"la place."
#: pl_comp.c:691
#, c-format
msgid "event trigger functions cannot have declared arguments"
-msgstr "les fonctions triggers sur vnement ne peuvent pas avoir des arguments dclars"
+msgstr "les fonctions triggers sur événement ne peuvent pas avoir des arguments déclarés"
#: pl_comp.c:944
#, c-format
msgid "compilation of PL/pgSQL function \"%s\" near line %d"
-msgstr "compilation de la fonction PL/pgsql %s prs de la ligne %d"
+msgstr "compilation de la fonction PL/pgsql « %s » près de la ligne %d"
#: pl_comp.c:967
#, c-format
msgid "parameter name \"%s\" used more than once"
-msgstr "le nom du paramtre %s est utilis plus d'une fois"
+msgstr "le nom du paramètre « %s » est utilisé plus d'une fois"
#: pl_comp.c:1077
#, c-format
msgid "column reference \"%s\" is ambiguous"
-msgstr "la rfrence la colonne %s est ambigu"
+msgstr "la référence à la colonne « %s » est ambigu"
#: pl_comp.c:1079
#, c-format
msgid "It could refer to either a PL/pgSQL variable or a table column."
msgstr ""
-"Cela pourrait faire rfrence une variable PL/pgsql ou la colonne d'une\n"
+"Cela pourrait faire référence à une variable PL/pgsql ou à la colonne d'une\n"
"table."
#: pl_comp.c:1259 pl_comp.c:1287 pl_exec.c:4395 pl_exec.c:4744 pl_exec.c:4829
#: pl_exec.c:4920
#, c-format
msgid "record \"%s\" has no field \"%s\""
-msgstr "l'enregistrement %s n'a pas de champs %s "
+msgstr "l'enregistrement « %s » n'a pas de champs « %s »"
#: pl_comp.c:1818
#, c-format
msgid "relation \"%s\" does not exist"
-msgstr "la relation %s n'existe pas"
+msgstr "la relation « %s » n'existe pas"
#: pl_comp.c:1927
#, c-format
msgid "variable \"%s\" has pseudo-type %s"
-msgstr "la variable %s a le pseudo-type %s"
+msgstr "la variable « %s » a le pseudo-type %s"
#: pl_comp.c:1994
#, c-format
msgid "relation \"%s\" is not a table"
-msgstr "la relation %s n'est pas une table"
+msgstr "la relation « %s » n'est pas une table"
#: pl_comp.c:2154
#, c-format
msgid "type \"%s\" is only a shell"
-msgstr "le type %s est seulement un shell"
+msgstr "le type « %s » est seulement un shell"
#: pl_comp.c:2243 pl_comp.c:2296
#, c-format
msgid "unrecognized exception condition \"%s\""
-msgstr "condition d'exception non reconnue %s "
+msgstr "condition d'exception non reconnue « %s »"
#: pl_comp.c:2503
#, c-format
msgid "could not determine actual argument type for polymorphic function \"%s\""
msgstr ""
-"n'a pas pu dterminer le type d'argument actuel pour la fonction\n"
-"polymorphique %s "
+"n'a pas pu déterminer le type d'argument actuel pour la fonction\n"
+"polymorphique « %s »"
#: pl_exec.c:324 pl_exec.c:612 pl_exec.c:872
msgid "during initialization of execution state"
-msgstr "durant l'initialisation de l'tat de la fonction"
+msgstr "durant l'initialisation de l'état de la fonction"
#: pl_exec.c:331
msgid "while storing call arguments into local variables"
@@ -128,12 +128,12 @@ msgstr "lors du stockage des arguments dans les variables locales"
#: pl_exec.c:416 pl_exec.c:760
msgid "during function entry"
-msgstr "durant l'entre d'une fonction"
+msgstr "durant l'entrée d'une fonction"
#: pl_exec.c:441
#, c-format
msgid "control reached end of function without RETURN"
-msgstr "le contrle a atteint la fin de la fonction sans RETURN"
+msgstr "le contrôle a atteint la fin de la fonction sans RETURN"
#: pl_exec.c:448
msgid "while casting return value to function's return type"
@@ -143,13 +143,13 @@ msgstr "lors de la conversion de la valeur de retour au type de retour de la fon
#, c-format
msgid "set-valued function called in context that cannot accept a set"
msgstr ""
-"fonction renvoyant un ensemble appele dans un contexte qui ne peut pas\n"
+"fonction renvoyant un ensemble appelée dans un contexte qui ne peut pas\n"
"accepter un ensemble"
#: pl_exec.c:499 pl_exec.c:2779
msgid "returned record type does not match expected record type"
msgstr ""
-"le type d'enregistrement renvoy ne correspond pas au type d'enregistrement\n"
+"le type d'enregistrement renvoyé ne correspond pas au type d'enregistrement\n"
"attendu"
#: pl_exec.c:554 pl_exec.c:789 pl_exec.c:907
@@ -159,17 +159,17 @@ msgstr "lors de la sortie de la fonction"
#: pl_exec.c:785 pl_exec.c:903
#, c-format
msgid "control reached end of trigger procedure without RETURN"
-msgstr "le contrle a atteint la fin de la procdure trigger sans RETURN"
+msgstr "le contrôle a atteint la fin de la procédure trigger sans RETURN"
#: pl_exec.c:794
#, c-format
msgid "trigger procedure cannot return a set"
-msgstr "la procdure trigger ne peut pas renvoyer un ensemble"
+msgstr "la procédure trigger ne peut pas renvoyer un ensemble"
#: pl_exec.c:816
msgid "returned row structure does not match the structure of the triggering table"
msgstr ""
-"la structure de ligne renvoye ne correspond pas la structure de la table\n"
+"la structure de ligne renvoyée ne correspond pas à la structure de la table\n"
"du trigger"
#. translator: last %s is a phrase such as "during statement block
@@ -192,7 +192,7 @@ msgstr "fonction PL/pgsql %s, %s"
#: pl_exec.c:973
#, c-format
msgid "PL/pgSQL function %s line %d at %s"
-msgstr "fonction PL/pgsql %s, ligne %d %s"
+msgstr "fonction PL/pgsql %s, ligne %d à %s"
#: pl_exec.c:979
#, c-format
@@ -206,11 +206,11 @@ msgstr "lors de l'initialisation de variables locales du bloc d'instructions"
#: pl_exec.c:1128
#, c-format
msgid "variable \"%s\" declared NOT NULL cannot default to NULL"
-msgstr "la variable %s dclare NOT NULL ne peut pas valoir NULL par dfaut"
+msgstr "la variable « %s » déclarée NOT NULL ne peut pas valoir NULL par défaut"
#: pl_exec.c:1178
msgid "during statement block entry"
-msgstr "lors de l'entre dans le bloc d'instructions"
+msgstr "lors de l'entrée dans le bloc d'instructions"
#: pl_exec.c:1199
msgid "during statement block exit"
@@ -224,7 +224,7 @@ msgstr "lors du nettoyage de l'exception"
#, c-format
msgid "GET STACKED DIAGNOSTICS cannot be used outside an exception handler"
msgstr ""
-"GET STACKED DIAGNOSTICS ne peut pas tre utilis l'extrieur d'un gestionnaire\n"
+"GET STACKED DIAGNOSTICS ne peut pas être utilisé à l'extérieur d'un gestionnaire\n"
"d'exception"
#: pl_exec.c:1789
@@ -240,32 +240,32 @@ msgstr "l'instruction CASE n'a pas la partie ELSE."
#: pl_exec.c:1944
#, c-format
msgid "lower bound of FOR loop cannot be null"
-msgstr "la limite infrieure de la boucle FOR ne peut pas tre NULL"
+msgstr "la limite inférieure de la boucle FOR ne peut pas être NULL"
#: pl_exec.c:1960
#, c-format
msgid "upper bound of FOR loop cannot be null"
-msgstr "la limite suprieure de la boucle FOR ne peut pas tre NULL"
+msgstr "la limite supérieure de la boucle FOR ne peut pas être NULL"
#: pl_exec.c:1978
#, c-format
msgid "BY value of FOR loop cannot be null"
-msgstr "la valeur BY d'une boucle FOR ne peut pas tre NULL"
+msgstr "la valeur BY d'une boucle FOR ne peut pas être NULL"
#: pl_exec.c:1984
#, c-format
msgid "BY value of FOR loop must be greater than zero"
-msgstr "la valeur BY d'une boucle FOR doit tre plus grande que zro"
+msgstr "la valeur BY d'une boucle FOR doit être plus grande que zéro"
#: pl_exec.c:2153 pl_exec.c:3912
#, c-format
msgid "cursor \"%s\" already in use"
-msgstr "curseur %s dj en cours d'utilisation"
+msgstr "curseur « %s » déjà en cours d'utilisation"
#: pl_exec.c:2176 pl_exec.c:3974
#, c-format
msgid "arguments given for cursor without arguments"
-msgstr "arguments donns pour le curseur sans arguments"
+msgstr "arguments donnés pour le curseur sans arguments"
#: pl_exec.c:2195 pl_exec.c:3993
#, c-format
@@ -275,7 +275,7 @@ msgstr "arguments requis pour le curseur"
#: pl_exec.c:2280
#, c-format
msgid "FOREACH expression must not be null"
-msgstr "l'expression FOREACH ne doit pas tre NULL"
+msgstr "l'expression FOREACH ne doit pas être NULL"
#: pl_exec.c:2286
#, c-format
@@ -290,17 +290,17 @@ msgstr "la dimension de la partie (%d) est en dehors des valeurs valides (0..%d)
#: pl_exec.c:2330
#, c-format
msgid "FOREACH ... SLICE loop variable must be of an array type"
-msgstr "la variable d'une boucle FOREACH ... SLICE doit tre d'un type tableau"
+msgstr "la variable d'une boucle FOREACH ... SLICE doit être d'un type tableau"
#: pl_exec.c:2334
#, c-format
msgid "FOREACH loop variable must not be of an array type"
-msgstr "la valeur d'une boucle FOREACH ne doit pas tre de type tableau"
+msgstr "la valeur d'une boucle FOREACH ne doit pas être de type tableau"
#: pl_exec.c:2522 pl_exec.c:2604 pl_exec.c:2771
#, c-format
msgid "cannot return non-composite value from function returning composite type"
-msgstr "ne peut pas renvoyer de valeurs non composites partir d'une fonction renvoyant un type composite"
+msgstr "ne peut pas renvoyer de valeurs non composites à partir d'une fonction renvoyant un type composite"
#: pl_exec.c:2648 pl_gram.y:3161
#, c-format
@@ -310,29 +310,29 @@ msgstr "ne peut pas utiliser RETURN NEXT dans une fonction non SETOF"
#: pl_exec.c:2682 pl_exec.c:2813
#, c-format
msgid "wrong result type supplied in RETURN NEXT"
-msgstr "mauvais type de rsultat fourni dans RETURN NEXT"
+msgstr "mauvais type de résultat fourni dans RETURN NEXT"
#: pl_exec.c:2711 pl_exec.c:4382 pl_exec.c:4711 pl_exec.c:4737 pl_exec.c:4803
#: pl_exec.c:4822 pl_exec.c:4890 pl_exec.c:4913
#, c-format
msgid "record \"%s\" is not assigned yet"
-msgstr "l'enregistrement %s n'est pas encore affecte"
+msgstr "l'enregistrement « %s » n'est pas encore affectée"
#: pl_exec.c:2713 pl_exec.c:4384 pl_exec.c:4713 pl_exec.c:4739 pl_exec.c:4805
#: pl_exec.c:4824 pl_exec.c:4892 pl_exec.c:4915
#, c-format
msgid "The tuple structure of a not-yet-assigned record is indeterminate."
-msgstr "La structure de ligne d'un enregistrement pas encore affect est indtermine."
+msgstr "La structure de ligne d'un enregistrement pas encore affecté est indéterminée."
#: pl_exec.c:2717 pl_exec.c:2737
#, c-format
msgid "wrong record type supplied in RETURN NEXT"
-msgstr "mauvais type d'enregistrement fourni RETURN NEXT"
+msgstr "mauvais type d'enregistrement fourni à RETURN NEXT"
#: pl_exec.c:2832
#, c-format
msgid "RETURN NEXT must have a parameter"
-msgstr "RETURN NEXT doit avoir un paramtre"
+msgstr "RETURN NEXT doit avoir un paramètre"
#: pl_exec.c:2865 pl_gram.y:3223
#, c-format
@@ -341,24 +341,24 @@ msgstr "ne peut pas utiliser RETURN QUERY dans une fonction non SETOF"
#: pl_exec.c:2886
msgid "structure of query does not match function result type"
-msgstr "la structure de la requte ne correspond pas au type de rsultat de la fonction"
+msgstr "la structure de la requête ne correspond pas au type de résultat de la fonction"
#: pl_exec.c:2966 pl_exec.c:3096
#, c-format
msgid "RAISE option already specified: %s"
-msgstr "option RAISE dj spcifie : %s"
+msgstr "option RAISE déjà spécifiée : %s"
#: pl_exec.c:2999
#, c-format
msgid "RAISE without parameters cannot be used outside an exception handler"
msgstr ""
-"RAISE sans paramtre ne peut pas tre utilis sans un gestionnaire\n"
+"RAISE sans paramètre ne peut pas être utilisé sans un gestionnaire\n"
"d'exception"
#: pl_exec.c:3086
#, c-format
msgid "RAISE statement option cannot be null"
-msgstr "l'option de l'instruction RAISE ne peut pas tre NULL"
+msgstr "l'option de l'instruction RAISE ne peut pas être NULL"
#: pl_exec.c:3155
#, c-format
@@ -368,7 +368,7 @@ msgstr "%s"
#: pl_exec.c:3226
#, c-format
msgid "assertion failed"
-msgstr "chec de l'assertion"
+msgstr "échec de l'assertion"
#: pl_exec.c:3418 pl_exec.c:3562 pl_exec.c:3751
#, c-format
@@ -383,57 +383,57 @@ msgstr "ne peut pas utiliser les instructions BEGIN/END de transactions dans PL/
#: pl_exec.c:3423 pl_exec.c:3567 pl_exec.c:3756
#, c-format
msgid "Use a BEGIN block with an EXCEPTION clause instead."
-msgstr "Utiliser un bloc BEGIN dans une clause EXCEPTION la place."
+msgstr "Utiliser un bloc BEGIN dans une clause EXCEPTION à la place."
#: pl_exec.c:3590 pl_exec.c:3780
#, c-format
msgid "INTO used with a command that cannot return data"
-msgstr "INTO utilis dans une commande qui ne peut pas envoyer de donnes"
+msgstr "INTO utilisé dans une commande qui ne peut pas envoyer de données"
#: pl_exec.c:3618 pl_exec.c:3808
#, c-format
msgid "query returned no rows"
-msgstr "la requte n'a renvoy aucune ligne"
+msgstr "la requête n'a renvoyé aucune ligne"
#: pl_exec.c:3637 pl_exec.c:3827
#, c-format
msgid "query returned more than one row"
-msgstr "la requte a renvoy plus d'une ligne"
+msgstr "la requête a renvoyé plus d'une ligne"
#: pl_exec.c:3654
#, c-format
msgid "query has no destination for result data"
-msgstr "la requte n'a pas de destination pour les donnes rsultantes"
+msgstr "la requête n'a pas de destination pour les données résultantes"
#: pl_exec.c:3655
#, c-format
msgid "If you want to discard the results of a SELECT, use PERFORM instead."
-msgstr "Si vous voulez annuler les rsultats d'un SELECT, utilisez PERFORM la place."
+msgstr "Si vous voulez annuler les résultats d'un SELECT, utilisez PERFORM à la place."
#: pl_exec.c:3687 pl_exec.c:7128
#, c-format
msgid "query string argument of EXECUTE is null"
-msgstr "l'argument de la requte de EXECUTE est NULL"
+msgstr "l'argument de la requête de EXECUTE est NULL"
#: pl_exec.c:3743
#, c-format
msgid "EXECUTE of SELECT ... INTO is not implemented"
-msgstr "EXECUTE de SELECT ... INTO n'est pas implant"
+msgstr "EXECUTE de SELECT ... INTO n'est pas implanté"
#: pl_exec.c:3744
#, c-format
msgid "You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS instead."
-msgstr "Vous pouvez aussi utiliser EXECUTE ... INTO ou EXECUTE CREATE TABLE ... AS la place."
+msgstr "Vous pouvez aussi utiliser EXECUTE ... INTO ou EXECUTE CREATE TABLE ... AS à la place."
#: pl_exec.c:4056 pl_exec.c:4148
#, c-format
msgid "cursor variable \"%s\" is null"
-msgstr "la variable du curseur %s est NULL"
+msgstr "la variable du curseur « %s » est NULL"
#: pl_exec.c:4063 pl_exec.c:4155
#, c-format
msgid "cursor \"%s\" does not exist"
-msgstr "le curseur %s n'existe pas"
+msgstr "le curseur « %s » n'existe pas"
#: pl_exec.c:4077
#, c-format
@@ -444,23 +444,23 @@ msgstr "la position relative ou absolue du curseur est NULL"
#, c-format
msgid "null value cannot be assigned to variable \"%s\" declared NOT NULL"
msgstr ""
-"une valeur NULL ne peut pas tre affecte la variable %s dclare\n"
+"une valeur NULL ne peut pas être affectée à la variable « %s » déclarée\n"
"non NULL"
#: pl_exec.c:4326
#, c-format
msgid "cannot assign non-composite value to a row variable"
-msgstr "ne peut pas affecter une valeur non composite une variable de type ROW"
+msgstr "ne peut pas affecter une valeur non composite à une variable de type ROW"
#: pl_exec.c:4350
#, c-format
msgid "cannot assign non-composite value to a record variable"
-msgstr "ne peut pas affecter une valeur non composite une variable RECORD"
+msgstr "ne peut pas affecter une valeur non composite à une variable RECORD"
#: pl_exec.c:4493
#, c-format
msgid "number of array dimensions (%d) exceeds the maximum allowed (%d)"
-msgstr "le nombre de dimensions du tableau (%d) dpasse la maximum autoris (%d)"
+msgstr "le nombre de dimensions du tableau (%d) dépasse la maximum autorisé (%d)"
#: pl_exec.c:4525
#, c-format
@@ -470,29 +470,29 @@ msgstr "l'objet souscrit n'est pas un tableau"
#: pl_exec.c:4562
#, c-format
msgid "array subscript in assignment must not be null"
-msgstr "un indice de tableau dans une affectation ne peut pas tre NULL"
+msgstr "un indice de tableau dans une affectation ne peut pas être NULL"
#: pl_exec.c:5029
#, c-format
msgid "query \"%s\" did not return data"
-msgstr "la requte %s ne renvoie pas de donnes"
+msgstr "la requête « %s » ne renvoie pas de données"
#: pl_exec.c:5037
#, c-format
msgid "query \"%s\" returned %d column"
msgid_plural "query \"%s\" returned %d columns"
-msgstr[0] "la requte %s a renvoy %d colonne"
-msgstr[1] "la requte %s a renvoy %d colonnes"
+msgstr[0] "la requête « %s » a renvoyé %d colonne"
+msgstr[1] "la requête « %s » a renvoyé %d colonnes"
#: pl_exec.c:5064
#, c-format
msgid "query \"%s\" returned more than one row"
-msgstr "la requte %s a renvoy plus d'une ligne"
+msgstr "la requête « %s » a renvoyé plus d'une ligne"
#: pl_exec.c:5128
#, c-format
msgid "query \"%s\" is not a SELECT"
-msgstr "la requte %s n'est pas un SELECT"
+msgstr "la requête « %s » n'est pas un SELECT"
#: pl_funcs.c:237
msgid "statement block"
@@ -504,7 +504,7 @@ msgstr "affectation"
#: pl_funcs.c:249
msgid "FOR with integer loop variable"
-msgstr "variable entire de boucle FOR"
+msgstr "variable entière de boucle FOR"
#: pl_funcs.c:251
msgid "FOR over SELECT rows"
@@ -529,66 +529,66 @@ msgstr "FOR sur une instruction EXECUTE"
#: pl_gram.y:473
#, c-format
msgid "block label must be placed before DECLARE, not after"
-msgstr "le label du bloc doit tre plac avant DECLARE, et non pas aprs"
+msgstr "le label du bloc doit être placé avant DECLARE, et non pas après"
#: pl_gram.y:493
#, c-format
msgid "collations are not supported by type %s"
-msgstr "les collationnements ne sont pas supports par le type %s"
+msgstr "les collationnements ne sont pas supportés par le type %s"
#: pl_gram.y:508
#, c-format
msgid "row or record variable cannot be CONSTANT"
-msgstr "la variable ROW ou RECORD ne peut pas tre CONSTANT"
+msgstr "la variable ROW ou RECORD ne peut pas être CONSTANT"
#: pl_gram.y:518
#, c-format
msgid "row or record variable cannot be NOT NULL"
-msgstr "la variable ROW ou RECORD ne peut pas tre NOT NULL"
+msgstr "la variable ROW ou RECORD ne peut pas être NOT NULL"
#: pl_gram.y:529
#, c-format
msgid "default value for row or record variable is not supported"
-msgstr "la valeur par dfaut de variable ROW ou RECORD n'est pas supporte"
+msgstr "la valeur par défaut de variable ROW ou RECORD n'est pas supportée"
#: pl_gram.y:674 pl_gram.y:689 pl_gram.y:715
#, c-format
msgid "variable \"%s\" does not exist"
-msgstr "la variable %s n'existe pas"
+msgstr "la variable « %s » n'existe pas"
#: pl_gram.y:733 pl_gram.y:761
msgid "duplicate declaration"
-msgstr "dclaration duplique"
+msgstr "déclaration dupliquée"
#: pl_gram.y:744 pl_gram.y:772
#, c-format
msgid "variable \"%s\" shadows a previously defined variable"
-msgstr "la variable %s cache une variable dfinie prcdemment"
+msgstr "la variable « %s » cache une variable définie précédemment"
#: pl_gram.y:951
#, c-format
msgid "diagnostics item %s is not allowed in GET STACKED DIAGNOSTICS"
-msgstr "l'lment %s de diagnostique l'est pas autoris dans GET STACKED DIAGNOSTICS"
+msgstr "l'élément %s de diagnostique l'est pas autorisé dans GET STACKED DIAGNOSTICS"
#: pl_gram.y:969
#, c-format
msgid "diagnostics item %s is not allowed in GET CURRENT DIAGNOSTICS"
-msgstr "l'lment %s de diagnostique l'est pas autoris dans GET CURRENT DIAGNOSTICS"
+msgstr "l'élément %s de diagnostique l'est pas autorisé dans GET CURRENT DIAGNOSTICS"
#: pl_gram.y:1067
msgid "unrecognized GET DIAGNOSTICS item"
-msgstr "lment GET DIAGNOSTICS non reconnu"
+msgstr "élément GET DIAGNOSTICS non reconnu"
#: pl_gram.y:1078 pl_gram.y:3410
#, c-format
msgid "\"%s\" is not a scalar variable"
-msgstr " %s n'est pas une variable scalaire"
+msgstr "« %s » n'est pas une variable scalaire"
#: pl_gram.y:1330 pl_gram.y:1524
#, c-format
msgid "loop variable of loop over rows must be a record or row variable or list of scalar variables"
msgstr ""
-"la variable d'une boucle sur des lignes doit tre une variable de type\n"
+"la variable d'une boucle sur des lignes doit être une variable de type\n"
"RECORD ou ROW, ou encore une liste de variables scalaires"
#: pl_gram.y:1364
@@ -599,7 +599,7 @@ msgstr "le curseur de la boucle FOR doit avoir seulement une variable cible"
#: pl_gram.y:1371
#, c-format
msgid "cursor FOR loop must use a bound cursor variable"
-msgstr "le curseur de la boucle FOR doit utiliser une variable curseur limit"
+msgstr "le curseur de la boucle FOR doit utiliser une variable curseur limité"
#: pl_gram.y:1455
#, c-format
@@ -609,37 +609,37 @@ msgstr "la boucle FOR de type entier doit avoir une seule variable cible"
#: pl_gram.y:1491
#, c-format
msgid "cannot specify REVERSE in query FOR loop"
-msgstr "ne peut pas spcifier REVERSE dans la requte de la boucle FOR"
+msgstr "ne peut pas spécifier REVERSE dans la requête de la boucle FOR"
#: pl_gram.y:1638
#, c-format
msgid "loop variable of FOREACH must be a known variable or list of variables"
-msgstr "la variable d'une boucle FOREACH doit tre une variable connue ou une liste de variables"
+msgstr "la variable d'une boucle FOREACH doit être une variable connue ou une liste de variables"
#: pl_gram.y:1679
#, c-format
msgid "there is no label \"%s\" attached to any block or loop enclosing this statement"
-msgstr "il n'existe pas de label %s attach un bloc ou une boucle englobant cette instruction"
+msgstr "il n'existe pas de label « %s » attaché à un bloc ou à une boucle englobant cette instruction"
#: pl_gram.y:1687
#, c-format
msgid "block label \"%s\" cannot be used in CONTINUE"
-msgstr "le label de bloc %s ne peut pas tre utilis avec l'instruction CONTINUE"
+msgstr "le label de bloc « %s » ne peut pas être utilisé avec l'instruction CONTINUE"
#: pl_gram.y:1702
#, c-format
msgid "EXIT cannot be used outside a loop, unless it has a label"
-msgstr "EXIT ne peut pas tre utilis l'extrieur d'une boucle, sauf s'il a un label"
+msgstr "EXIT ne peut pas être utilisé à l'extérieur d'une boucle, sauf s'il a un label"
#: pl_gram.y:1703
#, c-format
msgid "CONTINUE cannot be used outside a loop"
-msgstr "CONTINUE ne peut pas tre utilis l'extrieur d'une boucle"
+msgstr "CONTINUE ne peut pas être utilisé à l'extérieur d'une boucle"
#: pl_gram.y:1727 pl_gram.y:1764 pl_gram.y:1812 pl_gram.y:2863 pl_gram.y:2945
#: pl_gram.y:3056 pl_gram.y:3812
msgid "unexpected end of function definition"
-msgstr "dfinition inattendue de la fin de fonction"
+msgstr "définition inattendue de la fin de fonction"
#: pl_gram.y:1832 pl_gram.y:1856 pl_gram.y:1872 pl_gram.y:1878 pl_gram.y:1992
#: pl_gram.y:2000 pl_gram.y:2014 pl_gram.y:2109 pl_gram.y:2290 pl_gram.y:2384
@@ -653,7 +653,7 @@ msgstr "code SQLSTATE invalide"
#: pl_gram.y:2056
msgid "syntax error, expected \"FOR\""
-msgstr "erreur de syntaxe, FOR attendu"
+msgstr "erreur de syntaxe, « FOR » attendu"
#: pl_gram.y:2118
#, c-format
@@ -663,31 +663,31 @@ msgstr "l'instruction FETCH ne peut pas renvoyer plusieurs lignes"
#: pl_gram.y:2174
#, c-format
msgid "cursor variable must be a simple variable"
-msgstr "la variable de curseur doit tre une variable simple"
+msgstr "la variable de curseur doit être une variable simple"
#: pl_gram.y:2180
#, c-format
msgid "variable \"%s\" must be of type cursor or refcursor"
-msgstr "la variable %s doit tre de type cursor ou refcursor"
+msgstr "la variable « %s » doit être de type cursor ou refcursor"
#: pl_gram.y:2506 pl_gram.y:2517
#, c-format
msgid "\"%s\" is not a known variable"
-msgstr " %s n'est pas une variable connue"
+msgstr "« %s » n'est pas une variable connue"
#: pl_gram.y:2621 pl_gram.y:2631 pl_gram.y:2787
msgid "mismatched parentheses"
-msgstr "parenthses non correspondantes"
+msgstr "parenthèses non correspondantes"
#: pl_gram.y:2635
#, c-format
msgid "missing \"%s\" at end of SQL expression"
-msgstr " %s manquant la fin de l'expression SQL"
+msgstr "« %s » manquant à la fin de l'expression SQL"
#: pl_gram.y:2641
#, c-format
msgid "missing \"%s\" at end of SQL statement"
-msgstr " %s manquant la fin de l'instruction SQL"
+msgstr "« %s » manquant à la fin de l'instruction SQL"
#: pl_gram.y:2658
msgid "missing expression"
@@ -699,15 +699,15 @@ msgstr "instruction SQL manquante"
#: pl_gram.y:2789
msgid "incomplete data type declaration"
-msgstr "dclaration incomplte d'un type de donnes"
+msgstr "déclaration incomplète d'un type de données"
#: pl_gram.y:2812
msgid "missing data type declaration"
-msgstr "dclaration manquante d'un type de donnes"
+msgstr "déclaration manquante d'un type de données"
#: pl_gram.y:2868
msgid "INTO specified more than once"
-msgstr "INTO spcifi plus d'une fois"
+msgstr "INTO spécifié plus d'une fois"
#: pl_gram.y:3037
msgid "expected FROM or IN"
@@ -716,7 +716,7 @@ msgstr "attendait FROM ou IN"
#: pl_gram.y:3097
#, c-format
msgid "RETURN cannot have a parameter in function returning set"
-msgstr "RETURN ne peut pas avoir un paramtre dans une fonction renvoyant un ensemble"
+msgstr "RETURN ne peut pas avoir un paramètre dans une fonction renvoyant un ensemble"
#: pl_gram.y:3098
#, c-format
@@ -726,76 +726,76 @@ msgstr "Utilisez RETURN NEXT ou RETURN QUERY."
#: pl_gram.y:3106
#, c-format
msgid "RETURN cannot have a parameter in function with OUT parameters"
-msgstr "RETURN ne peut pas avoir un paramtre dans une fonction avec des paramtres OUT"
+msgstr "RETURN ne peut pas avoir un paramètre dans une fonction avec des paramètres OUT"
#: pl_gram.y:3115
#, c-format
msgid "RETURN cannot have a parameter in function returning void"
-msgstr "RETURN ne peut pas avoir un paramtre dans une fonction renvoyant void"
+msgstr "RETURN ne peut pas avoir un paramètre dans une fonction renvoyant void"
#: pl_gram.y:3175
#, c-format
msgid "RETURN NEXT cannot have a parameter in function with OUT parameters"
msgstr ""
-"RETURN NEXT ne peut pas avoir un paramtre dans une fonction avec des\n"
-"paramtres OUT"
+"RETURN NEXT ne peut pas avoir un paramètre dans une fonction avec des\n"
+"paramètres OUT"
#: pl_gram.y:3279
#, c-format
msgid "\"%s\" is declared CONSTANT"
-msgstr " %s est dclar CONSTANT"
+msgstr "« %s » est déclaré CONSTANT"
#: pl_gram.y:3341 pl_gram.y:3353
#, c-format
msgid "record or row variable cannot be part of multiple-item INTO list"
msgstr ""
-"la variable de type RECORD ou ROW ne peut pas faire partie d'une liste INTO \n"
-"plusieurs lments"
+"la variable de type RECORD ou ROW ne peut pas faire partie d'une liste INTO à\n"
+"plusieurs éléments"
#: pl_gram.y:3398
#, c-format
msgid "too many INTO variables specified"
-msgstr "trop de variables INTO indiques"
+msgstr "trop de variables INTO indiquées"
#: pl_gram.y:3606
#, c-format
msgid "end label \"%s\" specified for unlabelled block"
-msgstr "label de fin %s spcifi pour un bloc sans label"
+msgstr "label de fin « %s » spécifié pour un bloc sans label"
#: pl_gram.y:3613
#, c-format
msgid "end label \"%s\" differs from block's label \"%s\""
-msgstr "label de fin %s diffrent du label %s du bloc"
+msgstr "label de fin « %s » différent du label « %s » du bloc"
#: pl_gram.y:3648
#, c-format
msgid "cursor \"%s\" has no arguments"
-msgstr "le curseur %s n'a pas d'arguments"
+msgstr "le curseur « %s » n'a pas d'arguments"
#: pl_gram.y:3662
#, c-format
msgid "cursor \"%s\" has arguments"
-msgstr "le curseur %s a des arguments"
+msgstr "le curseur « %s » a des arguments"
#: pl_gram.y:3704
#, c-format
msgid "cursor \"%s\" has no argument named \"%s\""
-msgstr "le curseur %s n'a pas d'argument nomm %s "
+msgstr "le curseur « %s » n'a pas d'argument nommé « %s »"
#: pl_gram.y:3724
#, c-format
msgid "value for parameter \"%s\" of cursor \"%s\" specified more than once"
-msgstr "la valeur du paramtre %s pour le curseur %s est spcifie plus d'une fois"
+msgstr "la valeur du paramètre « %s » pour le curseur « %s » est spécifiée plus d'une fois"
#: pl_gram.y:3749
#, c-format
msgid "not enough arguments for cursor \"%s\""
-msgstr "pas assez d'arguments pour le curseur %s "
+msgstr "pas assez d'arguments pour le curseur « %s »"
#: pl_gram.y:3756
#, c-format
msgid "too many arguments for cursor \"%s\""
-msgstr "trop d'arguments pour le curseur %s "
+msgstr "trop d'arguments pour le curseur « %s »"
#: pl_gram.y:3844
msgid "unrecognized RAISE statement option"
@@ -803,17 +803,17 @@ msgstr "option de l'instruction RAISE inconnue"
#: pl_gram.y:3848
msgid "syntax error, expected \"=\""
-msgstr "erreur de syntaxe, = attendu"
+msgstr "erreur de syntaxe, « = » attendu"
#: pl_gram.y:3889
#, c-format
msgid "too many parameters specified for RAISE"
-msgstr "trop de paramtres pour RAISE"
+msgstr "trop de paramètres pour RAISE"
#: pl_gram.y:3893
#, c-format
msgid "too few parameters specified for RAISE"
-msgstr "trop peu de paramtres pour RAISE"
+msgstr "trop peu de paramètres pour RAISE"
#: pl_handler.c:149
msgid "Sets handling of conflicts between PL/pgSQL variable names and table column names."
@@ -821,11 +821,11 @@ msgstr "Configure la gestion des conflits entre les noms de variables PL/pgsql e
#: pl_handler.c:158
msgid "Print information about parameters in the DETAIL part of the error messages generated on INTO ... STRICT failures."
-msgstr "Affiche des informations sur les paramtres dans la partie DETAIL des messages d'erreur gnrs pour des checs INTO .. STRICT."
+msgstr "Affiche des informations sur les paramètres dans la partie DETAIL des messages d'erreur générés pour des échecs INTO .. STRICT."
#: pl_handler.c:166
msgid "Perform checks given in ASSERT statements."
-msgstr "Ralise les vrifications donnes dans les instructions ASSERT."
+msgstr "Réalise les vérifications données dans les instructions ASSERT."
#: pl_handler.c:174
msgid "List of programming constructs that should produce a warning."
@@ -839,80 +839,80 @@ msgstr "Liste des constructions de programmation qui devraient produire une erre
#: pl_scanner.c:621
#, c-format
msgid "%s at end of input"
-msgstr "%s la fin de l'entre"
+msgstr "%s à la fin de l'entrée"
#. translator: first %s is typically the translation of "syntax error"
#: pl_scanner.c:637
#, c-format
msgid "%s at or near \"%s\""
-msgstr "%s sur ou prs de %s "
+msgstr "%s sur ou près de « %s »"
#~ msgid "EXECUTE statement"
#~ msgstr "instruction EXECUTE"
#~ msgid "relation \"%s.%s\" does not exist"
-#~ msgstr "la relation %s.%s n'existe pas"
+#~ msgstr "la relation « %s.%s » n'existe pas"
#~ msgid "cursor \"%s\" closed unexpectedly"
-#~ msgstr "le curseur %s a t ferm de faon inattendu"
+#~ msgstr "le curseur « %s » a été fermé de façon inattendu"
#~ msgid "row \"%s\" has no field \"%s\""
-#~ msgstr "la ligne %s n'a aucun champ %s "
+#~ msgstr "la ligne « %s » n'a aucun champ « %s »"
#~ msgid "row \"%s.%s\" has no field \"%s\""
-#~ msgstr "la ligne %s.%s n'a aucun champ %s "
+#~ msgstr "la ligne « %s.%s » n'a aucun champ « %s »"
#~ msgid "expected \"[\""
-#~ msgstr " [ attendu"
+#~ msgstr "« [ » attendu"
#~ msgid "type of \"%s\" does not match that when preparing the plan"
-#~ msgstr "le type de %s ne correspond pas ce qui est prpar dans le plan"
+#~ msgstr "le type de « %s » ne correspond pas à ce qui est préparé dans le plan"
#~ msgid "type of \"%s.%s\" does not match that when preparing the plan"
-#~ msgstr "le type de %s.%s ne correspond pas ce qui est prpar dans le plan"
+#~ msgstr "le type de « %s.%s » ne correspond pas à ce qui est préparé dans le plan"
#~ msgid "type of tg_argv[%d] does not match that when preparing the plan"
-#~ msgstr "le type de tg_argv[%d] ne correspond pas ce qui est prpar dans le plan"
+#~ msgstr "le type de tg_argv[%d] ne correspond pas à ce qui est préparé dans le plan"
#~ msgid "N/A (dropped column)"
-#~ msgstr "N/A (colonne supprime)"
+#~ msgstr "N/A (colonne supprimée)"
#~ msgid "Number of returned columns (%d) does not match expected column count (%d)."
#~ msgstr ""
-#~ "Le nombre de colonnes renvoyes (%d) ne correspond pas au nombre de colonnes\n"
+#~ "Le nombre de colonnes renvoyées (%d) ne correspond pas au nombre de colonnes\n"
#~ "attendues (%d)."
#~ msgid "Returned type %s does not match expected type %s in column \"%s\"."
-#~ msgstr "Le type %s renvoy ne correspond pas au type %s attendu dans la colonne %s ."
+#~ msgstr "Le type %s renvoyé ne correspond pas au type %s attendu dans la colonne « %s »."
#~ msgid "only positional parameters can be aliased"
-#~ msgstr "seuls les paramtres de position peuvent avoir un alias"
+#~ msgstr "seuls les paramètres de position peuvent avoir un alias"
#~ msgid "function has no parameter \"%s\""
-#~ msgstr "la fonction n'a pas de paramtre %s "
+#~ msgstr "la fonction n'a pas de paramètre « %s »"
#~ msgid "expected an integer variable"
-#~ msgstr "attend une variable entire"
+#~ msgstr "attend une variable entière"
#~ msgid "syntax error at \"%s\""
-#~ msgstr "erreur de syntaxe %s "
+#~ msgstr "erreur de syntaxe à « %s »"
#~ msgid "Expected \"FOR\", to open a cursor for an unbound cursor variable."
-#~ msgstr "Attendait FOR pour ouvrir un curseur pour une variable sans limite."
+#~ msgstr "Attendait « FOR » pour ouvrir un curseur pour une variable sans limite."
#~ msgid "expected a cursor or refcursor variable"
#~ msgstr "attendait une variable de type cursor ou refcursor"
#~ msgid "too many variables specified in SQL statement"
-#~ msgstr "trop de variables spcifies dans l'instruction SQL"
+#~ msgstr "trop de variables spécifiées dans l'instruction SQL"
#~ msgid "RETURN cannot have a parameter in function returning set; use RETURN NEXT or RETURN QUERY"
#~ msgstr ""
-#~ "RETURN ne peut pas avoir un paramtre dans une fonction renvoyant des\n"
+#~ "RETURN ne peut pas avoir un paramètre dans une fonction renvoyant des\n"
#~ "lignes ; utilisez RETURN NEXT ou RETURN QUERY"
#~ msgid "cannot assign to tg_argv"
-#~ msgstr "ne peut pas affecter tg_argv"
+#~ msgstr "ne peut pas affecter à tg_argv"
#~ msgid "Expected record variable, row variable, or list of scalar variables following INTO."
#~ msgstr ""
@@ -920,34 +920,34 @@ msgstr "%s sur ou prs de %s "
#~ "suivant INTO."
#~ msgid "SQL statement in PL/PgSQL function \"%s\" near line %d"
-#~ msgstr "instruction SQL dans la fonction PL/pgsql %s prs de la ligne %d"
+#~ msgstr "instruction SQL dans la fonction PL/pgsql « %s » près de la ligne %d"
#~ msgid "string literal in PL/PgSQL function \"%s\" near line %d"
-#~ msgstr "chane littrale dans la fonction PL/pgsql %s prs de la ligne %d"
+#~ msgstr "chaîne littérale dans la fonction PL/pgsql « %s » près de la ligne %d"
#~ msgid "expected \")\""
-#~ msgstr " ) attendu"
+#~ msgstr "« ) » attendu"
#~ msgid "variable \"%s\" does not exist in the current block"
-#~ msgstr "la variable %s n'existe pas dans le bloc actuel"
+#~ msgstr "la variable « %s » n'existe pas dans le bloc actuel"
#~ msgid "unterminated \" in identifier: %s"
-#~ msgstr "\" non termin dans l'identifiant : %s"
+#~ msgstr "\" non terminé dans l'identifiant : %s"
#~ msgid "qualified identifier cannot be used here: %s"
-#~ msgstr "l'identifiant qualifi ne peut pas tre utilis ici : %s"
+#~ msgstr "l'identifiant qualifié ne peut pas être utilisé ici : %s"
#~ msgid "unterminated quoted identifier"
-#~ msgstr "identifiant entre guillemets non termin"
+#~ msgstr "identifiant entre guillemets non terminé"
#~ msgid "unterminated /* comment"
-#~ msgstr "commentaire /* non termin"
+#~ msgstr "commentaire /* non terminé"
#~ msgid "unterminated quoted string"
-#~ msgstr "chane entre guillemets non termine"
+#~ msgstr "chaîne entre guillemets non terminée"
#~ msgid "unterminated dollar-quoted string"
-#~ msgstr "chane entre dollars non termine"
+#~ msgstr "chaîne entre dollars non terminée"
#~ msgid "RETURN NEXT must specify a record or row variable in function returning row"
#~ msgstr ""
diff --git a/src/pl/plpgsql/src/po/ko.po b/src/pl/plpgsql/src/po/ko.po
index 56bece03af..95d9dc9d24 100644
--- a/src/pl/plpgsql/src/po/ko.po
+++ b/src/pl/plpgsql/src/po/ko.po
@@ -1,13 +1,13 @@
# Korean message translation file for plpgsql
# Copyright (C) 2010 PostgreSQL Global Development Group
# This file is distributed under the same license as the PostgreSQL package.
-# Ioseph Kim <ioseph@uri.sarang.net>
+# Ioseph Kim <ioseph@uri.sarang.net>, 2010
msgid ""
msgstr ""
-"Project-Id-Version: plpgsql (PostgreSQL 9.5)\n"
+"Project-Id-Version: plpgsql (PostgreSQL 9.6)\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2015-12-27 23:56+0900\n"
-"PO-Revision-Date: 2015-12-28 00:36+0900\n"
+"POT-Creation-Date: 2016-09-26 14:02+0900\n"
+"PO-Revision-Date: 2016-09-26 19:23+0900\n"
"Last-Translator: Ioseph Kim <ioseph@uri.sarang.net>\n"
"Language-Team: Korean <pgsql-kr@postgresql.kr>\n"
"Language: ko\n"
@@ -16,560 +16,556 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-#: pl_comp.c:436 pl_handler.c:448
+#: pl_comp.c:430 pl_handler.c:450
#, c-format
msgid "PL/pgSQL functions cannot accept type %s"
msgstr "PL/pgSQL 함수에 %s 형식을 사용할 수 없음"
-#: pl_comp.c:517
+#: pl_comp.c:511
#, c-format
msgid "could not determine actual return type for polymorphic function \"%s\""
msgstr "다형적 함수 \"%s\"의 실제 반환 형식을 확인할 수 없음"
-#: pl_comp.c:547
+#: pl_comp.c:541
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "트리거 함수는 트리거로만 호출될 수 있음"
-#: pl_comp.c:551 pl_handler.c:433
+#: pl_comp.c:545 pl_handler.c:435
#, c-format
msgid "PL/pgSQL functions cannot return type %s"
msgstr "PL/pgSQL 함수는 %s 형식을 반환할 수 없음"
-#: pl_comp.c:592
+#: pl_comp.c:586
#, c-format
msgid "trigger functions cannot have declared arguments"
msgstr "트리거 함수는 선언된 인수를 포함할 수 없음"
-#: pl_comp.c:593
+#: pl_comp.c:587
#, c-format
msgid ""
"The arguments of the trigger can be accessed through TG_NARGS and TG_ARGV "
"instead."
msgstr "대신 TG_NARGS 및 TG_ARGV를 통해 트리거의 인수에 액세스할 수 있습니다."
-#: pl_comp.c:695
+#: pl_comp.c:689
#, c-format
msgid "event trigger functions cannot have declared arguments"
msgstr "이벤트 트리거 함수는 선언된 인자(declare argument)를 사용할 수 없음"
-#: pl_comp.c:957
+#: pl_comp.c:940
#, c-format
msgid "compilation of PL/pgSQL function \"%s\" near line %d"
msgstr "PL/pgSQL 함수 \"%s\" 컴파일(%d번째 줄 근처)"
-#: pl_comp.c:980
+#: pl_comp.c:963
#, c-format
msgid "parameter name \"%s\" used more than once"
msgstr "\"%s\" 매개 변수가 여러 번 사용 됨"
-#: pl_comp.c:1090
+#: pl_comp.c:1073
#, c-format
msgid "column reference \"%s\" is ambiguous"
msgstr "열 참조 \"%s\" 가 명확하지 않습니다."
-#: pl_comp.c:1092
+#: pl_comp.c:1075
#, c-format
msgid "It could refer to either a PL/pgSQL variable or a table column."
-msgstr ""
+msgstr "PL/pgSQL 변수명도, 테이블 칼럼 이름도 아니여야 함"
-#: pl_comp.c:1272 pl_comp.c:1300 pl_exec.c:4447 pl_exec.c:4795 pl_exec.c:4880
-#: pl_exec.c:4971
+#: pl_comp.c:1255 pl_comp.c:1283 pl_exec.c:4393 pl_exec.c:4742 pl_exec.c:4827
+#: pl_exec.c:4918
#, c-format
msgid "record \"%s\" has no field \"%s\""
msgstr "\"%s\" 레코드에 \"%s\" 필드가 없음"
-#: pl_comp.c:1831
+#: pl_comp.c:1814
#, c-format
msgid "relation \"%s\" does not exist"
msgstr "\"%s\" 이름의 릴레이션(relation)이 없습니다"
-#: pl_comp.c:1940
+#: pl_comp.c:1923
#, c-format
msgid "variable \"%s\" has pseudo-type %s"
msgstr "\"%s\" 변수에 의사 형식 %s이(가) 있음"
-#: pl_comp.c:2006
+#: pl_comp.c:1990
#, c-format
msgid "relation \"%s\" is not a table"
msgstr "\"%s\" 관계가 테이블이 아님"
-#: pl_comp.c:2166
+#: pl_comp.c:2150
#, c-format
msgid "type \"%s\" is only a shell"
msgstr "자료형 \"%s\" 는 오로지 shell 에만 있습니다. "
-#: pl_comp.c:2255 pl_comp.c:2308
+#: pl_comp.c:2244 pl_comp.c:2297
#, c-format
msgid "unrecognized exception condition \"%s\""
msgstr "인식할 수 없는 예외 조건 \"%s\""
-#: pl_comp.c:2466
+#: pl_comp.c:2504
#, c-format
msgid ""
"could not determine actual argument type for polymorphic function \"%s\""
msgstr "다형적 함수 \"%s\"의 실제 인수 형식을 확인할 수 없음"
-#: pl_exec.c:321 pl_exec.c:614 pl_exec.c:893
+#: pl_exec.c:324 pl_exec.c:612 pl_exec.c:872
msgid "during initialization of execution state"
msgstr "실행 상태를 초기화하는 동안"
-#: pl_exec.c:328
+#: pl_exec.c:331
msgid "while storing call arguments into local variables"
msgstr "호출 인수를 로컬 변수에 저장하는 동안"
-#: pl_exec.c:408 pl_exec.c:771
+#: pl_exec.c:416 pl_exec.c:760
msgid "during function entry"
msgstr "함수를 시작하는 동안"
-#: pl_exec.c:439 pl_exec.c:802 pl_exec.c:934
-#, c-format
-msgid "CONTINUE cannot be used outside a loop"
-msgstr "CONTINUE를 루프 외부에 사용할 수 없음"
-
-#: pl_exec.c:443
+#: pl_exec.c:441
#, c-format
msgid "control reached end of function without RETURN"
msgstr "컨트롤이 RETURN 없이 함수 끝에 도달함"
-#: pl_exec.c:450
+#: pl_exec.c:448
msgid "while casting return value to function's return type"
msgstr "함수의 반환 형식으로 반환 값을 형변환하는 동안"
-#: pl_exec.c:463 pl_exec.c:2987
+#: pl_exec.c:461 pl_exec.c:2938
#, c-format
msgid "set-valued function called in context that cannot accept a set"
msgstr ""
"set-values 함수(테이블 리턴 함수)가 set 정의 없이 사용되었습니다 (테이블과 해"
"당 열 alias 지정하세요)"
-#: pl_exec.c:501 pl_exec.c:2829
+#: pl_exec.c:499 pl_exec.c:2779
msgid "returned record type does not match expected record type"
msgstr "반환된 레코드 형식이 필요한 레코드 형식과 일치하지 않음"
-#: pl_exec.c:556 pl_exec.c:810 pl_exec.c:942
+#: pl_exec.c:554 pl_exec.c:789 pl_exec.c:907
msgid "during function exit"
msgstr "함수를 종료하는 동안"
-#: pl_exec.c:806 pl_exec.c:938
+#: pl_exec.c:785 pl_exec.c:903
#, c-format
msgid "control reached end of trigger procedure without RETURN"
msgstr "컨트롤이 RETURN 없이 트리거 프로시저 끝에 도달함"
-#: pl_exec.c:815
+#: pl_exec.c:794
#, c-format
msgid "trigger procedure cannot return a set"
msgstr "트리거 프로시저는 집합을 반환할 수 없음"
-#: pl_exec.c:837
+#: pl_exec.c:816
msgid ""
"returned row structure does not match the structure of the triggering table"
msgstr "반환된 행 구조가 트리거하는 테이블의 구조와 일치하지 않음"
-#: pl_exec.c:993
+#: pl_exec.c:954
#, c-format
msgid "PL/pgSQL function %s line %d %s"
msgstr "PL/pgSQL 함수 \"%s\" 의 %d번째 줄 %s"
-#: pl_exec.c:1004
+#: pl_exec.c:965
#, c-format
msgid "PL/pgSQL function %s %s"
msgstr "PL/pgSQL 함수 %s %s"
#. translator: last %s is a plpgsql statement type name
-#: pl_exec.c:1012
+#: pl_exec.c:973
#, c-format
msgid "PL/pgSQL function %s line %d at %s"
msgstr "PL/pgSQL 함수 \"%s\" 의 %d번째 %s"
-#: pl_exec.c:1018
+#: pl_exec.c:979
#, c-format
msgid "PL/pgSQL function %s"
msgstr "PL/pgSQL 함수 %s"
-#: pl_exec.c:1129
+#: pl_exec.c:1089
msgid "during statement block local variable initialization"
msgstr "문 블록 로컬 변수를 초기화하는 동안"
-#: pl_exec.c:1169
+#: pl_exec.c:1128
#, c-format
msgid "variable \"%s\" declared NOT NULL cannot default to NULL"
msgstr "NOT NULL이 선언된 \"%s\" 변수의 기본 값이 NULL로 설정될 수 없음"
-#: pl_exec.c:1219
+#: pl_exec.c:1178
msgid "during statement block entry"
msgstr "문 블록을 시작하는 동안"
-#: pl_exec.c:1240
+#: pl_exec.c:1199
msgid "during statement block exit"
msgstr "문 블록을 종료하는 동안"
-#: pl_exec.c:1283
+#: pl_exec.c:1242
msgid "during exception cleanup"
msgstr "예외를 정리하는 동안"
-#: pl_exec.c:1634
+#: pl_exec.c:1593
#, c-format
msgid "GET STACKED DIAGNOSTICS cannot be used outside an exception handler"
msgstr "GET STACKED DIAGNOSTICS 구문은 예외처리 헨들러 밖에서 사용할 수 없음"
-#: pl_exec.c:1838
+#: pl_exec.c:1789
#, c-format
msgid "case not found"
msgstr "사례를 찾지 못함"
-#: pl_exec.c:1839
+#: pl_exec.c:1790
#, c-format
msgid "CASE statement is missing ELSE part."
msgstr "CASE 문에 ELSE 부분이 누락되었습니다."
-#: pl_exec.c:1993
+#: pl_exec.c:1944
#, c-format
msgid "lower bound of FOR loop cannot be null"
msgstr "FOR 루프의 하한은 null일 수 없음"
-#: pl_exec.c:2009
+#: pl_exec.c:1960
#, c-format
msgid "upper bound of FOR loop cannot be null"
msgstr "FOR 루프의 상한은 null일 수 없음"
-#: pl_exec.c:2027
+#: pl_exec.c:1978
#, c-format
msgid "BY value of FOR loop cannot be null"
msgstr "FOR 루프의 BY 값은 null일 수 없음"
-#: pl_exec.c:2033
+#: pl_exec.c:1984
#, c-format
msgid "BY value of FOR loop must be greater than zero"
msgstr "FOR 루프의 BY 값은 0보다 커야 함"
-#: pl_exec.c:2203 pl_exec.c:3963
+#: pl_exec.c:2153 pl_exec.c:3910
#, c-format
msgid "cursor \"%s\" already in use"
msgstr "\"%s\" 커서가 이미 사용 중임"
-#: pl_exec.c:2226 pl_exec.c:4025
+#: pl_exec.c:2176 pl_exec.c:3972
#, c-format
msgid "arguments given for cursor without arguments"
msgstr "인수가 없는 커서에 인수가 제공됨"
-#: pl_exec.c:2245 pl_exec.c:4044
+#: pl_exec.c:2195 pl_exec.c:3991
#, c-format
msgid "arguments required for cursor"
msgstr "커서에 인수 필요"
-#: pl_exec.c:2330
+#: pl_exec.c:2280
#, c-format
msgid "FOREACH expression must not be null"
msgstr "FOREACH 구문은 null 이 아니여야 함"
-#: pl_exec.c:2336
+#: pl_exec.c:2286
#, c-format
msgid "FOREACH expression must yield an array, not type %s"
-msgstr ""
+msgstr "FOREACH 구문에서는 배열이 사용됩니다. 사용된 자료형 %s"
-#: pl_exec.c:2353
+#: pl_exec.c:2303
#, c-format
msgid "slice dimension (%d) is out of the valid range 0..%d"
msgstr "slice dimension (%d) 값이 범위를 벗어남, 0..%d"
-#: pl_exec.c:2380
+#: pl_exec.c:2330
#, c-format
msgid "FOREACH ... SLICE loop variable must be of an array type"
-msgstr ""
+msgstr "FOREACH ... SLICE 루프 변수는 배열 자료형이어야 함"
-#: pl_exec.c:2384
+#: pl_exec.c:2334
#, c-format
msgid "FOREACH loop variable must not be of an array type"
msgstr "FOREACH 반복 변수는 배열형이 아니여야 함"
-#: pl_exec.c:2572 pl_exec.c:2654 pl_exec.c:2821
+#: pl_exec.c:2522 pl_exec.c:2604 pl_exec.c:2771
#, c-format
msgid ""
"cannot return non-composite value from function returning composite type"
msgstr ""
"함수의 반환값이 복합 자료형인데, 복합 자료형아닌 자료형을 반환하려고 함"
-#: pl_exec.c:2698 pl_gram.y:3112
+#: pl_exec.c:2648 pl_gram.y:3190
#, c-format
msgid "cannot use RETURN NEXT in a non-SETOF function"
msgstr "SETOF 함수가 아닌 함수에서 RETURN NEXT를 사용할 수 없음"
-#: pl_exec.c:2732 pl_exec.c:2863
+#: pl_exec.c:2682 pl_exec.c:2813
#, c-format
msgid "wrong result type supplied in RETURN NEXT"
msgstr "RETURN NEXT에 잘못된 결과 형식이 제공됨"
-#: pl_exec.c:2761 pl_exec.c:4434 pl_exec.c:4762 pl_exec.c:4788 pl_exec.c:4854
-#: pl_exec.c:4873 pl_exec.c:4941 pl_exec.c:4964
+#: pl_exec.c:2711 pl_exec.c:4380 pl_exec.c:4709 pl_exec.c:4735 pl_exec.c:4801
+#: pl_exec.c:4820 pl_exec.c:4888 pl_exec.c:4911
#, c-format
msgid "record \"%s\" is not assigned yet"
msgstr "\"%s\" 레코드가 아직 할당되지 않음"
-#: pl_exec.c:2763 pl_exec.c:4436 pl_exec.c:4764 pl_exec.c:4790 pl_exec.c:4856
-#: pl_exec.c:4875 pl_exec.c:4943 pl_exec.c:4966
+#: pl_exec.c:2713 pl_exec.c:4382 pl_exec.c:4711 pl_exec.c:4737 pl_exec.c:4803
+#: pl_exec.c:4822 pl_exec.c:4890 pl_exec.c:4913
#, c-format
msgid "The tuple structure of a not-yet-assigned record is indeterminate."
msgstr "아직 할당되지 않은 레코드의 튜플 구조는 미정입니다."
-#: pl_exec.c:2767 pl_exec.c:2787
+#: pl_exec.c:2717 pl_exec.c:2737
#, c-format
msgid "wrong record type supplied in RETURN NEXT"
msgstr "RETURN NEXT에 잘못된 레코드 형식이 제공됨"
-#: pl_exec.c:2882
+#: pl_exec.c:2832
#, c-format
msgid "RETURN NEXT must have a parameter"
msgstr "RETURN NEXT에 매개 변수 필요"
-#: pl_exec.c:2915 pl_gram.y:3174
+#: pl_exec.c:2865 pl_gram.y:3252
#, c-format
msgid "cannot use RETURN QUERY in a non-SETOF function"
msgstr "SETOF 함수가 아닌 함수에서 RETURN QUERY를 사용할 수 없음"
-#: pl_exec.c:2935
+#: pl_exec.c:2886
msgid "structure of query does not match function result type"
msgstr "쿼리 구조가 함수 결과 형식과 일치하지 않음"
-#: pl_exec.c:3015 pl_exec.c:3145
+#: pl_exec.c:2966 pl_exec.c:3096
#, c-format
msgid "RAISE option already specified: %s"
msgstr "RAISE 옵션이 이미 지정됨: %s"
-#: pl_exec.c:3048
+#: pl_exec.c:2999
#, c-format
msgid "RAISE without parameters cannot be used outside an exception handler"
msgstr "매개 변수 없는 RAISE를 예외 처리기 외부에 사용할 수 없음"
-#: pl_exec.c:3135
+#: pl_exec.c:3086
#, c-format
msgid "RAISE statement option cannot be null"
msgstr "RAISE 문 옵션이 null일 수 없음"
-#: pl_exec.c:3206
+#: pl_exec.c:3155
#, c-format
msgid "%s"
msgstr "%s"
-#: pl_exec.c:3279
+#: pl_exec.c:3226
#, c-format
msgid "assertion failed"
msgstr "assertion 실패"
-#: pl_exec.c:3469 pl_exec.c:3613 pl_exec.c:3802
+#: pl_exec.c:3416 pl_exec.c:3560 pl_exec.c:3749
#, c-format
msgid "cannot COPY to/from client in PL/pgSQL"
msgstr "PL/pgSQL의 클라이언트와 상호 복사할 수 없음"
-#: pl_exec.c:3473 pl_exec.c:3617 pl_exec.c:3806
+#: pl_exec.c:3420 pl_exec.c:3564 pl_exec.c:3753
#, c-format
msgid "cannot begin/end transactions in PL/pgSQL"
msgstr "PL/pgSQL의 트랜잭션을 시작/종료할 수 없음"
-#: pl_exec.c:3474 pl_exec.c:3618 pl_exec.c:3807
+#: pl_exec.c:3421 pl_exec.c:3565 pl_exec.c:3754
#, c-format
msgid "Use a BEGIN block with an EXCEPTION clause instead."
msgstr "대신 BEGIN 블록을 EXCEPTION 절과 함께 사용하십시오."
-#: pl_exec.c:3641 pl_exec.c:3831
+#: pl_exec.c:3588 pl_exec.c:3778
#, c-format
msgid "INTO used with a command that cannot return data"
msgstr "데이터를 반환할 수 없는 명령과 함께 INTO가 사용됨"
-#: pl_exec.c:3669 pl_exec.c:3859
+#: pl_exec.c:3616 pl_exec.c:3806
#, c-format
msgid "query returned no rows"
msgstr "쿼리에서 행을 반환하지 않음"
-#: pl_exec.c:3688 pl_exec.c:3878
+#: pl_exec.c:3635 pl_exec.c:3825
#, c-format
msgid "query returned more than one row"
msgstr "쿼리에서 두 개 이상의 행을 반환"
-#: pl_exec.c:3705
+#: pl_exec.c:3652
#, c-format
msgid "query has no destination for result data"
msgstr "쿼리에 결과 데이터의 대상이 없음"
-#: pl_exec.c:3706
+#: pl_exec.c:3653
#, c-format
msgid "If you want to discard the results of a SELECT, use PERFORM instead."
msgstr "SELECT의 결과를 취소하려면 대신 PERFORM을 사용하십시오."
-#: pl_exec.c:3738 pl_exec.c:7026
+#: pl_exec.c:3685 pl_exec.c:7126
#, c-format
msgid "query string argument of EXECUTE is null"
msgstr "EXECUTE의 쿼리 문자열 인수가 null임"
-#: pl_exec.c:3794
+#: pl_exec.c:3741
#, c-format
msgid "EXECUTE of SELECT ... INTO is not implemented"
msgstr "SELECT의 EXECUTE... INTO가 구현되지 않음"
-#: pl_exec.c:3795
+#: pl_exec.c:3742
#, c-format
msgid ""
"You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS "
"instead."
msgstr ""
+"EXECUTE ... INTO 또는 EXECUTE CREATE TABLE ... AS 구문을 사용하세요."
-#: pl_exec.c:4105 pl_exec.c:4197
+#: pl_exec.c:4054 pl_exec.c:4146
#, c-format
msgid "cursor variable \"%s\" is null"
msgstr "커서 변수 \"%s\"이(가) null임"
-#: pl_exec.c:4112 pl_exec.c:4204
+#: pl_exec.c:4061 pl_exec.c:4153
#, c-format
msgid "cursor \"%s\" does not exist"
msgstr "\"%s\" 이름의 커서가 없음"
-#: pl_exec.c:4126
+#: pl_exec.c:4075
#, c-format
msgid "relative or absolute cursor position is null"
msgstr "상대 또는 절대 커서 위치가 null임"
-#: pl_exec.c:4306
+#: pl_exec.c:4255
#, c-format
msgid "null value cannot be assigned to variable \"%s\" declared NOT NULL"
msgstr "NOT NULL이 선언된 \"%s\" 변수에 null 값을 할당할 수 없음"
-#: pl_exec.c:4378
+#: pl_exec.c:4324
#, c-format
msgid "cannot assign non-composite value to a row variable"
msgstr "행 변수에 비복합 값을 할당할 수 없음"
-#: pl_exec.c:4402
+#: pl_exec.c:4348
#, c-format
msgid "cannot assign non-composite value to a record variable"
msgstr "레코드 변수에 비복합 값을 할당할 수 없음"
-#: pl_exec.c:4545
+#: pl_exec.c:4491
#, c-format
msgid "number of array dimensions (%d) exceeds the maximum allowed (%d)"
msgstr "지정한 배열 크기(%d)가 최대치(%d)를 초과했습니다"
-#: pl_exec.c:4577
+#: pl_exec.c:4523
#, c-format
msgid "subscripted object is not an array"
msgstr "하위 스크립트 개체는 배열이 아님"
-#: pl_exec.c:4614
+#: pl_exec.c:4560
#, c-format
msgid "array subscript in assignment must not be null"
msgstr "배열 하위 스크립트로 지정하는 값으로 null 값을 사용할 수 없습니다"
-#: pl_exec.c:5080
+#: pl_exec.c:5027
#, c-format
msgid "query \"%s\" did not return data"
msgstr "\"%s\" 쿼리에서 데이터를 반환하지 않음"
-#: pl_exec.c:5088
+#: pl_exec.c:5035
#, c-format
msgid "query \"%s\" returned %d column"
msgid_plural "query \"%s\" returned %d columns"
msgstr[0] "\"%s\" 쿼리가 %d 개의 칼럼을 반환함"
-#: pl_exec.c:5115
+#: pl_exec.c:5062
#, c-format
msgid "query \"%s\" returned more than one row"
msgstr "\"%s\" 쿼리에서 두 개 이상의 행을 반환함"
-#: pl_exec.c:5170
+#: pl_exec.c:5126
#, c-format
msgid "query \"%s\" is not a SELECT"
msgstr "\"%s\" 쿼리가 SELECT가 아님"
-#: pl_funcs.c:218
+#: pl_funcs.c:237
msgid "statement block"
msgstr "문 블록"
-#: pl_funcs.c:220
+#: pl_funcs.c:239
msgid "assignment"
msgstr "할당"
-#: pl_funcs.c:230
+#: pl_funcs.c:249
msgid "FOR with integer loop variable"
msgstr "정수 루프 변수를 포함하는 FOR"
-#: pl_funcs.c:232
+#: pl_funcs.c:251
msgid "FOR over SELECT rows"
msgstr "SELECT 행을 제어하는 FOR"
-#: pl_funcs.c:234
+#: pl_funcs.c:253
msgid "FOR over cursor"
msgstr "커서를 제어하는 FOR"
-#: pl_funcs.c:236
+#: pl_funcs.c:255
msgid "FOREACH over array"
msgstr "배열 초과된 FOREACH"
-#: pl_funcs.c:250
+#: pl_funcs.c:269
msgid "SQL statement"
msgstr "SQL 문"
-#: pl_funcs.c:254
+#: pl_funcs.c:273
msgid "FOR over EXECUTE statement"
msgstr "EXECUTE 문을 제어하는 FOR"
-#: pl_gram.y:472
+#: pl_gram.y:474
#, c-format
msgid "block label must be placed before DECLARE, not after"
-msgstr ""
+msgstr "블록 라벨은 DECLARE 영역 앞에 있어야 함"
-#: pl_gram.y:492
+#: pl_gram.y:494
#, c-format
msgid "collations are not supported by type %s"
msgstr "%s 자료형은 collation 지원 안함"
-#: pl_gram.y:507
+#: pl_gram.y:509
#, c-format
msgid "row or record variable cannot be CONSTANT"
msgstr "행 또는 레코드 변수는 CONSTANT일 수 없음"
-#: pl_gram.y:517
+#: pl_gram.y:519
#, c-format
msgid "row or record variable cannot be NOT NULL"
msgstr "행 또는 레코드 변수는 NOT NULL일 수 없음"
-#: pl_gram.y:528
+#: pl_gram.y:530
#, c-format
msgid "default value for row or record variable is not supported"
msgstr "행 또는 레코드 변수의 기본 값이 지원되지 않음"
-#: pl_gram.y:673 pl_gram.y:688 pl_gram.y:714
+#: pl_gram.y:675 pl_gram.y:690 pl_gram.y:716
#, c-format
msgid "variable \"%s\" does not exist"
msgstr "\"%s\" 변수가 없음"
-#: pl_gram.y:732 pl_gram.y:760
+#: pl_gram.y:734 pl_gram.y:762
msgid "duplicate declaration"
msgstr "중복 선언"
-#: pl_gram.y:743 pl_gram.y:771
+#: pl_gram.y:745 pl_gram.y:773
#, c-format
msgid "variable \"%s\" shadows a previously defined variable"
msgstr "variable \"%s\" shadows a previously defined variable"
-#: pl_gram.y:950
+#: pl_gram.y:952
#, c-format
msgid "diagnostics item %s is not allowed in GET STACKED DIAGNOSTICS"
-msgstr ""
+msgstr "GET STACKED DIAGNOSTICS 에서 %s 항목을 사용할 수 없음"
-#: pl_gram.y:968
+#: pl_gram.y:970
#, c-format
msgid "diagnostics item %s is not allowed in GET CURRENT DIAGNOSTICS"
-msgstr ""
+msgstr "GET CURRENT DIAGNOSTICS 에서 %s 항목을 사용할 수 없음"
-#: pl_gram.y:1066
+#: pl_gram.y:1068
msgid "unrecognized GET DIAGNOSTICS item"
msgstr "알 수 없는 GET DIAGNOSTICS 항목"
-#: pl_gram.y:1077 pl_gram.y:3361
+#: pl_gram.y:1079 pl_gram.y:3439
#, c-format
msgid "\"%s\" is not a scalar variable"
msgstr "\"%s\"은(는) 스칼라 변수가 아님"
-#: pl_gram.y:1329 pl_gram.y:1523
+#: pl_gram.y:1331 pl_gram.y:1525
#, c-format
msgid ""
"loop variable of loop over rows must be a record or row variable or list of "
@@ -578,242 +574,263 @@ msgstr ""
"행에 있는 루프의 루프 변수는 레코드 또는 행 변수이거나 스칼라 변수의 목록이어"
"야 함"
-#: pl_gram.y:1363
+#: pl_gram.y:1365
#, c-format
msgid "cursor FOR loop must have only one target variable"
msgstr "커서 FOR 루프에 대상 변수가 한 개만 있어야 함"
-#: pl_gram.y:1370
+#: pl_gram.y:1372
#, c-format
msgid "cursor FOR loop must use a bound cursor variable"
msgstr "커서 FOR 루프는 바인딩된 커서 변수를 한 개만 사용해야 함"
-#: pl_gram.y:1454
+#: pl_gram.y:1456
#, c-format
msgid "integer FOR loop must have only one target variable"
msgstr "정수 FOR 루프에 대상 변수가 한 개만 있어야 함"
-#: pl_gram.y:1490
+#: pl_gram.y:1492
#, c-format
msgid "cannot specify REVERSE in query FOR loop"
msgstr "쿼리 FOR 루프에 REVERSE를 지정할 수 없음"
-#: pl_gram.y:1637
+#: pl_gram.y:1639
#, c-format
msgid "loop variable of FOREACH must be a known variable or list of variables"
msgstr "FOREACH의 반복 변수는 알려진 변수이거나 변수의 목록이어야 함"
-#: pl_gram.y:1689 pl_gram.y:1726 pl_gram.y:1774 pl_gram.y:2814 pl_gram.y:2896
-#: pl_gram.y:3007 pl_gram.y:3763
+#: pl_gram.y:1680
+#, c-format
+msgid ""
+"there is no label \"%s\" attached to any block or loop enclosing this "
+"statement"
+msgstr ""
+"임의 블록이나 루프 구문에 할당된 \"%s\" 라벨이 없음"
+
+#: pl_gram.y:1688
+#, c-format
+msgid "block label \"%s\" cannot be used in CONTINUE"
+msgstr "CONTINUE 안에서 \"%s\" 블록 라벨을 사용할 수 없음"
+
+#: pl_gram.y:1703
+#, c-format
+msgid "EXIT cannot be used outside a loop, unless it has a label"
+msgstr "루프 외부에 라벨 지정 없이 EXIT 사용할 수 없음"
+
+#: pl_gram.y:1704
+#, c-format
+msgid "CONTINUE cannot be used outside a loop"
+msgstr "CONTINUE를 루프 외부에 사용할 수 없음"
+
+#: pl_gram.y:1728 pl_gram.y:1765 pl_gram.y:1813 pl_gram.y:2889 pl_gram.y:2974
+#: pl_gram.y:3085 pl_gram.y:3841
msgid "unexpected end of function definition"
msgstr "예기치 않은 함수 정의의 끝"
-#: pl_gram.y:1794 pl_gram.y:1818 pl_gram.y:1834 pl_gram.y:1840 pl_gram.y:1954
-#: pl_gram.y:1962 pl_gram.y:1976 pl_gram.y:2071 pl_gram.y:2252 pl_gram.y:2335
-#: pl_gram.y:2486 pl_gram.y:3604 pl_gram.y:3665 pl_gram.y:3744
+#: pl_gram.y:1833 pl_gram.y:1857 pl_gram.y:1873 pl_gram.y:1879 pl_gram.y:1997
+#: pl_gram.y:2005 pl_gram.y:2019 pl_gram.y:2114 pl_gram.y:2295 pl_gram.y:2389
+#: pl_gram.y:2541 pl_gram.y:3682 pl_gram.y:3743 pl_gram.y:3822
msgid "syntax error"
msgstr "구문 오류"
-#: pl_gram.y:1822 pl_gram.y:1824 pl_gram.y:2256 pl_gram.y:2258
+#: pl_gram.y:1861 pl_gram.y:1863 pl_gram.y:2299 pl_gram.y:2301
msgid "invalid SQLSTATE code"
msgstr "잘못된 SQLSTATE 코드"
-#: pl_gram.y:2018
+#: pl_gram.y:2061
msgid "syntax error, expected \"FOR\""
msgstr "구문 오류, \"FOR\" 필요"
-#: pl_gram.y:2080
+#: pl_gram.y:2123
#, c-format
msgid "FETCH statement cannot return multiple rows"
msgstr "FETCH 구문은 다중 로우를 반환할 수 없음"
-#: pl_gram.y:2136
+#: pl_gram.y:2179
#, c-format
msgid "cursor variable must be a simple variable"
msgstr "커서 변수는 단순 변수여야 함"
-#: pl_gram.y:2142
+#: pl_gram.y:2185
#, c-format
msgid "variable \"%s\" must be of type cursor or refcursor"
msgstr "\"%s\" 변수는 커서 또는 ref 커서 형식이어야 함"
-#: pl_gram.y:2310
-msgid "label does not exist"
-msgstr "레이블이 없음"
-
-#: pl_gram.y:2457 pl_gram.y:2468
+#: pl_gram.y:2512 pl_gram.y:2523
#, c-format
msgid "\"%s\" is not a known variable"
msgstr "\"%s\" (은)는 알려진 변수가 아님"
-#: pl_gram.y:2572 pl_gram.y:2582 pl_gram.y:2738
+#: pl_gram.y:2627 pl_gram.y:2637 pl_gram.y:2793
msgid "mismatched parentheses"
msgstr "괄호의 짝이 맞지 않음"
-#: pl_gram.y:2586
+#: pl_gram.y:2641
#, c-format
msgid "missing \"%s\" at end of SQL expression"
msgstr "SQL 식 끝에 \"%s\" 누락"
-#: pl_gram.y:2592
+#: pl_gram.y:2647
#, c-format
msgid "missing \"%s\" at end of SQL statement"
msgstr "SQL 문 끝에 \"%s\" 누락"
-#: pl_gram.y:2609
+#: pl_gram.y:2664
msgid "missing expression"
msgstr "표현식 빠졌음"
-#: pl_gram.y:2611
+#: pl_gram.y:2666
msgid "missing SQL statement"
msgstr "SQL 문이 빠졌음"
-#: pl_gram.y:2740
+#: pl_gram.y:2795
msgid "incomplete data type declaration"
msgstr "불완전한 데이터 형식 선언"
-#: pl_gram.y:2763
+#: pl_gram.y:2818
msgid "missing data type declaration"
msgstr "데이터 형식 선언 누락"
-#: pl_gram.y:2819
+#: pl_gram.y:2897
msgid "INTO specified more than once"
msgstr "INTO가 여러 번 지정됨"
-#: pl_gram.y:2988
+#: pl_gram.y:3066
msgid "expected FROM or IN"
msgstr "FROM 또는 IN 필요"
-#: pl_gram.y:3048
+#: pl_gram.y:3126
#, c-format
msgid "RETURN cannot have a parameter in function returning set"
msgstr "집합을 반환하는 함수에서 RETURN 구문에는 인자가 없음"
-#: pl_gram.y:3049
+#: pl_gram.y:3127
#, c-format
msgid "Use RETURN NEXT or RETURN QUERY."
msgstr "RETURN NEXT 나 RETURN QUERY 구문을 사용하세요."
-#: pl_gram.y:3057
+#: pl_gram.y:3135
#, c-format
msgid "RETURN cannot have a parameter in function with OUT parameters"
msgstr "RETURN은 OUT 매개 변수가 있는 함수에 매개 변수를 포함할 수 없음"
-#: pl_gram.y:3066
+#: pl_gram.y:3144
#, c-format
msgid "RETURN cannot have a parameter in function returning void"
msgstr "RETURN은 void를 반환하는 함수에 매개 변수를 포함할 수 없음"
-#: pl_gram.y:3126
+#: pl_gram.y:3204
#, c-format
msgid "RETURN NEXT cannot have a parameter in function with OUT parameters"
msgstr "RETURN NEXT는 OUT 매개 변수가 있는 함수에 매개 변수를 포함할 수 없음"
-#: pl_gram.y:3230
+#: pl_gram.y:3308
#, c-format
msgid "\"%s\" is declared CONSTANT"
msgstr "\"%s\"이(가) CONSTANT로 선언됨"
-#: pl_gram.y:3292 pl_gram.y:3304
+#: pl_gram.y:3370 pl_gram.y:3382
#, c-format
msgid "record or row variable cannot be part of multiple-item INTO list"
-msgstr ""
+msgstr "다중 아이템 INTO 목록의 부분으로 record나 row 변수가 사용될 수 없음"
-#: pl_gram.y:3349
+#: pl_gram.y:3427
#, c-format
msgid "too many INTO variables specified"
msgstr "너무 많은 INTO 변수가 지정됨"
-#: pl_gram.y:3557
+#: pl_gram.y:3635
#, c-format
msgid "end label \"%s\" specified for unlabelled block"
msgstr "레이블이 없는 블록에 끝 레이블 \"%s\"이(가) 지정됨"
-#: pl_gram.y:3564
+#: pl_gram.y:3642
#, c-format
msgid "end label \"%s\" differs from block's label \"%s\""
msgstr "끝 레이블 \"%s\"이(가) 블록의 \"%s\" 레이블과 다름"
-#: pl_gram.y:3599
+#: pl_gram.y:3677
#, c-format
msgid "cursor \"%s\" has no arguments"
msgstr "\"%s\" 커서에 인수가 없음"
-#: pl_gram.y:3613
+#: pl_gram.y:3691
#, c-format
msgid "cursor \"%s\" has arguments"
msgstr "\"%s\" 커서에 인수가 있음"
-#: pl_gram.y:3655
+#: pl_gram.y:3733
#, c-format
msgid "cursor \"%s\" has no argument named \"%s\""
msgstr "\"%s\" 커서는 \"%s\" 이름의 인자가 없음"
-#: pl_gram.y:3675
+#: pl_gram.y:3753
#, c-format
msgid "value for parameter \"%s\" of cursor \"%s\" specified more than once"
msgstr "\"%s\" 이름의 인자가 \"%s\" 커서에서 중복됨"
-#: pl_gram.y:3700
+#: pl_gram.y:3778
#, c-format
msgid "not enough arguments for cursor \"%s\""
msgstr "\"%s\" 커서를 위한 충분하지 않은 인자"
-#: pl_gram.y:3707
+#: pl_gram.y:3785
#, c-format
msgid "too many arguments for cursor \"%s\""
msgstr "\"%s\" 커서를 위한 인자가 너무 많음"
-#: pl_gram.y:3795
+#: pl_gram.y:3873
msgid "unrecognized RAISE statement option"
msgstr "인식할 수 없는 RAISE 문 옵션"
-#: pl_gram.y:3799
+#: pl_gram.y:3877
msgid "syntax error, expected \"=\""
msgstr "구문 오류, \"=\" 필요"
-#: pl_gram.y:3840
+#: pl_gram.y:3918
#, c-format
msgid "too many parameters specified for RAISE"
msgstr "RAISE에 지정된 매개 변수가 너무 많음"
-#: pl_gram.y:3844
+#: pl_gram.y:3922
#, c-format
msgid "too few parameters specified for RAISE"
msgstr "RAISE에 지정된 매개 변수가 너무 적음"
-#: pl_handler.c:149
+#: pl_handler.c:151
msgid ""
"Sets handling of conflicts between PL/pgSQL variable names and table column "
"names."
-msgstr ""
+msgstr "PL/pgSQL 변수명과 테이블 칼럼명 사이 충돌이 일어날 경우에 대한 처리를 하세요."
-#: pl_handler.c:158
+#: pl_handler.c:160
msgid ""
"Print information about parameters in the DETAIL part of the error messages "
"generated on INTO ... STRICT failures."
msgstr ""
+"INTO ... STRICT 실패에서 오류 메시지를 만들 때 그 DETAIL 부분에 들어갈 내용을 "
+"출력 하세요"
-#: pl_handler.c:166
+#: pl_handler.c:168
msgid "Perform checks given in ASSERT statements."
-msgstr ""
+msgstr "ASSERT 구문에서 주어진 검사를 수행하세요."
-#: pl_handler.c:174
+#: pl_handler.c:176
msgid "List of programming constructs that should produce a warning."
-msgstr ""
+msgstr "경고로 처리할 프로그래밍 컨스트럭트 목록"
-#: pl_handler.c:184
+#: pl_handler.c:186
msgid "List of programming constructs that should produce an error."
-msgstr ""
+msgstr "오류로 처리할 프로그래밍 컨스트럭트 목록"
#. translator: %s is typically the translation of "syntax error"
-#: pl_scanner.c:621
+#: pl_scanner.c:622
#, c-format
msgid "%s at end of input"
msgstr "%s, 입력 끝부분"
#. translator: first %s is typically the translation of "syntax error"
-#: pl_scanner.c:637
+#: pl_scanner.c:638
#, c-format
msgid "%s at or near \"%s\""
msgstr "%s, \"%s\" 부근"
diff --git a/src/pl/plpgsql/src/po/pt_BR.po b/src/pl/plpgsql/src/po/pt_BR.po
index b198c1b99e..d6b3a0b32a 100644
--- a/src/pl/plpgsql/src/po/pt_BR.po
+++ b/src/pl/plpgsql/src/po/pt_BR.po
@@ -1,13 +1,13 @@
# Brazilian Portuguese message translation file for plpgsql
# Copyright (C) 2010 PostgreSQL Global Development Group
# This file is distributed under the same license as the PostgreSQL package.
-# Euler Taveira de Oliveira <euler@timbira.com>, 2010-2015.
+# Euler Taveira de Oliveira <euler@timbira.com>, 2010-2016.
#
msgid ""
msgstr ""
-"Project-Id-Version: PostgreSQL 9.5\n"
+"Project-Id-Version: PostgreSQL 9.6\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2015-09-17 22:32-0300\n"
+"POT-Creation-Date: 2016-08-09 22:53-0300\n"
"PO-Revision-Date: 2010-07-08 17:13-0300\n"
"Last-Translator: Euler Taveira de Oliveira <euler@timbira.com>\n"
"Language-Team: Brazilian Portuguese <pgbr-dev@listas.postgresql.org.br>\n"
@@ -17,154 +17,149 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n>1);\n"
-#: pl_comp.c:436 pl_handler.c:448
+#: pl_comp.c:432 pl_handler.c:450
#, c-format
msgid "PL/pgSQL functions cannot accept type %s"
msgstr "funções PL/pgSQL não podem aceitar tipo %s"
-#: pl_comp.c:517
+#: pl_comp.c:513
#, c-format
msgid "could not determine actual return type for polymorphic function \"%s\""
msgstr "não pôde determinar tipo de retorno atual para função polimófica \"%s\""
-#: pl_comp.c:547
+#: pl_comp.c:543
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "funções de gatilho só podem ser chamadas como gatilhos"
-#: pl_comp.c:551 pl_handler.c:433
+#: pl_comp.c:547 pl_handler.c:435
#, c-format
msgid "PL/pgSQL functions cannot return type %s"
msgstr "funções PL/pgSQL não podem retornar tipo %s"
-#: pl_comp.c:592
+#: pl_comp.c:588
#, c-format
msgid "trigger functions cannot have declared arguments"
msgstr "funções de gatilho não podem ter argumentos declarados"
-#: pl_comp.c:593
+#: pl_comp.c:589
#, c-format
msgid "The arguments of the trigger can be accessed through TG_NARGS and TG_ARGV instead."
msgstr "Os argumentos de um gatilho podem ser acessados através de TG_NARGS e TG_ARGV."
-#: pl_comp.c:695
+#: pl_comp.c:691
#, c-format
msgid "event trigger functions cannot have declared arguments"
msgstr "funções de gatilho de eventos não podem ter argumentos declarados"
-#: pl_comp.c:957
+#: pl_comp.c:944
#, c-format
msgid "compilation of PL/pgSQL function \"%s\" near line %d"
msgstr "compilação da função PL/pgSQL \"%s\" próximo a linha %d"
-#: pl_comp.c:980
+#: pl_comp.c:967
#, c-format
msgid "parameter name \"%s\" used more than once"
msgstr "nome de parâmetro \"%s\" foi especificado mais de uma vez"
-#: pl_comp.c:1090
+#: pl_comp.c:1077
#, c-format
msgid "column reference \"%s\" is ambiguous"
msgstr "referência à coluna \"%s\" é ambígua"
-#: pl_comp.c:1092
+#: pl_comp.c:1079
#, c-format
msgid "It could refer to either a PL/pgSQL variable or a table column."
msgstr "Ela poderia referenciar uma variável PL/pgSQL ou uma coluna de tabela."
-#: pl_comp.c:1272 pl_comp.c:1300 pl_exec.c:4447 pl_exec.c:4795 pl_exec.c:4880
-#: pl_exec.c:4971
+#: pl_comp.c:1259 pl_comp.c:1287 pl_exec.c:4395 pl_exec.c:4744 pl_exec.c:4829
+#: pl_exec.c:4920
#, c-format
msgid "record \"%s\" has no field \"%s\""
msgstr "registro \"%s\" não tem campo \"%s\""
-#: pl_comp.c:1831
+#: pl_comp.c:1818
#, c-format
msgid "relation \"%s\" does not exist"
msgstr "relação \"%s\" não existe"
-#: pl_comp.c:1940
+#: pl_comp.c:1927
#, c-format
msgid "variable \"%s\" has pseudo-type %s"
msgstr "variável \"%s\" tem pseudo-tipo %s"
-#: pl_comp.c:2006
+#: pl_comp.c:1994
#, c-format
msgid "relation \"%s\" is not a table"
msgstr "relação \"%s\" não é uma tabela"
-#: pl_comp.c:2166
+#: pl_comp.c:2154
#, c-format
msgid "type \"%s\" is only a shell"
msgstr "tipo \"%s\" é indefinido"
-#: pl_comp.c:2255 pl_comp.c:2308
+#: pl_comp.c:2243 pl_comp.c:2296
#, c-format
msgid "unrecognized exception condition \"%s\""
msgstr "condição de exceção \"%s\" é desconhecida"
-#: pl_comp.c:2466
+#: pl_comp.c:2503
#, c-format
msgid "could not determine actual argument type for polymorphic function \"%s\""
msgstr "não pôde determinar tipo do argumento atual para função polimórfica \"%s\""
-#: pl_exec.c:321 pl_exec.c:614 pl_exec.c:893
+#: pl_exec.c:324 pl_exec.c:612 pl_exec.c:872
msgid "during initialization of execution state"
msgstr "durante inicialização de estado de execução"
-#: pl_exec.c:328
+#: pl_exec.c:331
msgid "while storing call arguments into local variables"
msgstr "ao armazenar argumentos em variáveis locais"
-#: pl_exec.c:408 pl_exec.c:771
+#: pl_exec.c:416 pl_exec.c:760
msgid "during function entry"
msgstr "durante entrada da função"
-#: pl_exec.c:439 pl_exec.c:802 pl_exec.c:934
-#, c-format
-msgid "CONTINUE cannot be used outside a loop"
-msgstr "CONTINUE não pode ser utilizado fora de um laço"
-
-#: pl_exec.c:443
+#: pl_exec.c:441
#, c-format
msgid "control reached end of function without RETURN"
msgstr "controle atingiu o fim da função sem RETURN"
-#: pl_exec.c:450
+#: pl_exec.c:448
msgid "while casting return value to function's return type"
msgstr "ao converter valor de retorno para tipo de retorno da função"
-#: pl_exec.c:463 pl_exec.c:2987
+#: pl_exec.c:461 pl_exec.c:2938
#, c-format
msgid "set-valued function called in context that cannot accept a set"
msgstr "função que tem argumento do tipo conjunto foi chamada em um contexto que não pode aceitar um conjunto"
-#: pl_exec.c:501 pl_exec.c:2829
+#: pl_exec.c:499 pl_exec.c:2779
msgid "returned record type does not match expected record type"
msgstr "tipo record retornado não corresponde ao tipo record esperado"
-#: pl_exec.c:556 pl_exec.c:810 pl_exec.c:942
+#: pl_exec.c:554 pl_exec.c:789 pl_exec.c:907
msgid "during function exit"
msgstr "durante saída da função"
-#: pl_exec.c:806 pl_exec.c:938
+#: pl_exec.c:785 pl_exec.c:903
#, c-format
msgid "control reached end of trigger procedure without RETURN"
msgstr "controle atingiu o fim da função de gatilho sem RETURN"
-#: pl_exec.c:815
+#: pl_exec.c:794
#, c-format
msgid "trigger procedure cannot return a set"
msgstr "função de gatilho não pode retornar um conjunto"
-#: pl_exec.c:837
+#: pl_exec.c:816
msgid "returned row structure does not match the structure of the triggering table"
msgstr "estrutura de registro retornada não corresponde a estrutura da tabela que disparou o evento"
#. translator: last %s is a phrase such as "during statement block
#. local variable initialization"
#.
-#: pl_exec.c:993
+#: pl_exec.c:954
#, c-format
msgid "PL/pgSQL function %s line %d %s"
msgstr "função PL/pgSQL %s linha %d %s"
@@ -172,638 +167,654 @@ msgstr "função PL/pgSQL %s linha %d %s"
#. translator: last %s is a phrase such as "while storing call
#. arguments into local variables"
#.
-#: pl_exec.c:1004
+#: pl_exec.c:965
#, c-format
msgid "PL/pgSQL function %s %s"
msgstr "função PL/pgSQL %s %s"
#. translator: last %s is a plpgsql statement type name
-#: pl_exec.c:1012
+#: pl_exec.c:973
#, c-format
msgid "PL/pgSQL function %s line %d at %s"
msgstr "função PL/pgSQL %s linha %d em %s"
-#: pl_exec.c:1018
+#: pl_exec.c:979
#, c-format
msgid "PL/pgSQL function %s"
msgstr "função PL/pgSQL %s"
-#: pl_exec.c:1129
+#: pl_exec.c:1089
msgid "during statement block local variable initialization"
msgstr "durante inicialização de variável local em bloco de comandos"
-#: pl_exec.c:1169
+#: pl_exec.c:1128
#, c-format
msgid "variable \"%s\" declared NOT NULL cannot default to NULL"
msgstr "variável \"%s\" declarada NOT NULL não pode ter valor padrão NULL"
-#: pl_exec.c:1219
+#: pl_exec.c:1178
msgid "during statement block entry"
msgstr "durante entrada em bloco de comandos"
-#: pl_exec.c:1240
+#: pl_exec.c:1199
msgid "during statement block exit"
msgstr "durante saída em bloco de comandos"
-#: pl_exec.c:1283
+#: pl_exec.c:1242
msgid "during exception cleanup"
msgstr "durante término de exceção"
-#: pl_exec.c:1634
+#: pl_exec.c:1593
#, c-format
msgid "GET STACKED DIAGNOSTICS cannot be used outside an exception handler"
msgstr "GET STACKED DIAGNOSTICS não pode ser utilizado fora de um manipulador de exceção"
-#: pl_exec.c:1838
+#: pl_exec.c:1789
#, c-format
msgid "case not found"
msgstr "case não foi encontrado"
-#: pl_exec.c:1839
+#: pl_exec.c:1790
#, c-format
msgid "CASE statement is missing ELSE part."
msgstr "comando CASE está faltando a parte ELSE."
-#: pl_exec.c:1993
+#: pl_exec.c:1944
#, c-format
msgid "lower bound of FOR loop cannot be null"
msgstr "limite inferior do laço FOR não pode ser nulo"
-#: pl_exec.c:2009
+#: pl_exec.c:1960
#, c-format
msgid "upper bound of FOR loop cannot be null"
msgstr "limite superior do laço FOR não pode ser nulo"
-#: pl_exec.c:2027
+#: pl_exec.c:1978
#, c-format
msgid "BY value of FOR loop cannot be null"
msgstr "valor BY do laço FOR não pode ser nulo"
-#: pl_exec.c:2033
+#: pl_exec.c:1984
#, c-format
msgid "BY value of FOR loop must be greater than zero"
msgstr "valor BY do laço FOR deve ser maior do que zero"
-#: pl_exec.c:2203 pl_exec.c:3963
+#: pl_exec.c:2153 pl_exec.c:3912
#, c-format
msgid "cursor \"%s\" already in use"
msgstr "cursor \"%s\" já está em uso"
-#: pl_exec.c:2226 pl_exec.c:4025
+#: pl_exec.c:2176 pl_exec.c:3974
#, c-format
msgid "arguments given for cursor without arguments"
msgstr "argumentos fornecidos a cursor sem argumentos"
-#: pl_exec.c:2245 pl_exec.c:4044
+#: pl_exec.c:2195 pl_exec.c:3993
#, c-format
msgid "arguments required for cursor"
msgstr "argumentos requeridos pelo cursor"
-#: pl_exec.c:2330
+#: pl_exec.c:2280
#, c-format
msgid "FOREACH expression must not be null"
msgstr "expressão FOREACH não deve ser nula"
-#: pl_exec.c:2336
+#: pl_exec.c:2286
#, c-format
msgid "FOREACH expression must yield an array, not type %s"
msgstr "expressão FOREACH deve produzir uma matriz, e não tipo %s"
-#: pl_exec.c:2353
+#: pl_exec.c:2303
#, c-format
msgid "slice dimension (%d) is out of the valid range 0..%d"
msgstr "fatia da dimensão (%d) está fora do intervalo válido, 0..%d"
-#: pl_exec.c:2380
+#: pl_exec.c:2330
#, c-format
msgid "FOREACH ... SLICE loop variable must be of an array type"
msgstr "variável do laço FOREACH ... SLICE deve ser de um tipo matriz"
-#: pl_exec.c:2384
+#: pl_exec.c:2334
#, c-format
msgid "FOREACH loop variable must not be of an array type"
msgstr "variável do laço FOREACH não deve ser de um tipo matriz"
-#: pl_exec.c:2572 pl_exec.c:2654 pl_exec.c:2821
+#: pl_exec.c:2522 pl_exec.c:2604 pl_exec.c:2771
#, c-format
msgid "cannot return non-composite value from function returning composite type"
msgstr "não pode retornar valor não-composto de função que retorna tipo composto"
-#: pl_exec.c:2698 pl_gram.y:3112
+#: pl_exec.c:2648 pl_gram.y:3190
#, c-format
msgid "cannot use RETURN NEXT in a non-SETOF function"
msgstr "não pode utilizar RETURN NEXT em uma função que não foi declarada SETOF"
-#: pl_exec.c:2732 pl_exec.c:2863
+#: pl_exec.c:2682 pl_exec.c:2813
#, c-format
msgid "wrong result type supplied in RETURN NEXT"
msgstr "tipo resultante incorreto foi fornecido em RETURN NEXT"
-#: pl_exec.c:2761 pl_exec.c:4434 pl_exec.c:4762 pl_exec.c:4788 pl_exec.c:4854
-#: pl_exec.c:4873 pl_exec.c:4941 pl_exec.c:4964
+#: pl_exec.c:2711 pl_exec.c:4382 pl_exec.c:4711 pl_exec.c:4737 pl_exec.c:4803
+#: pl_exec.c:4822 pl_exec.c:4890 pl_exec.c:4913
#, c-format
msgid "record \"%s\" is not assigned yet"
msgstr "registro \"%s\" não foi atribuído ainda"
-#: pl_exec.c:2763 pl_exec.c:4436 pl_exec.c:4764 pl_exec.c:4790 pl_exec.c:4856
-#: pl_exec.c:4875 pl_exec.c:4943 pl_exec.c:4966
+#: pl_exec.c:2713 pl_exec.c:4384 pl_exec.c:4713 pl_exec.c:4739 pl_exec.c:4805
+#: pl_exec.c:4824 pl_exec.c:4892 pl_exec.c:4915
#, c-format
msgid "The tuple structure of a not-yet-assigned record is indeterminate."
msgstr "A estrutura da tupla de um registro não atribuído é indeterminada."
-#: pl_exec.c:2767 pl_exec.c:2787
+#: pl_exec.c:2717 pl_exec.c:2737
#, c-format
msgid "wrong record type supplied in RETURN NEXT"
msgstr "tipo registro incorreto foi fornecido em RETURN NEXT"
-#: pl_exec.c:2882
+#: pl_exec.c:2832
#, c-format
msgid "RETURN NEXT must have a parameter"
msgstr "RETURN NEXT deve ter um parâmetro"
-#: pl_exec.c:2915 pl_gram.y:3174
+#: pl_exec.c:2865 pl_gram.y:3252
#, c-format
msgid "cannot use RETURN QUERY in a non-SETOF function"
msgstr "não pode utilizar RETURN QUERY em uma função que não foi declarada SETOF"
-#: pl_exec.c:2935
+#: pl_exec.c:2886
msgid "structure of query does not match function result type"
msgstr "estrutura da consulta não corresponde ao tipo resultante da função"
-#: pl_exec.c:3015 pl_exec.c:3145
+#: pl_exec.c:2966 pl_exec.c:3096
#, c-format
msgid "RAISE option already specified: %s"
msgstr "opção RAISE já foi especificada: %s"
-#: pl_exec.c:3048
+#: pl_exec.c:2999
#, c-format
msgid "RAISE without parameters cannot be used outside an exception handler"
msgstr "RAISE sem parâmetros não pode ser utilizado fora de um manipulador de exceção"
-#: pl_exec.c:3135
+#: pl_exec.c:3086
#, c-format
msgid "RAISE statement option cannot be null"
msgstr "opção do comando RAISE não pode ser nulo"
-#: pl_exec.c:3206
+#: pl_exec.c:3155
#, c-format
msgid "%s"
msgstr "%s"
-#: pl_exec.c:3279
+#: pl_exec.c:3226
#, c-format
msgid "assertion failed"
msgstr "asserção falhou"
-#: pl_exec.c:3469 pl_exec.c:3613 pl_exec.c:3802
+#: pl_exec.c:3418 pl_exec.c:3562 pl_exec.c:3751
#, c-format
msgid "cannot COPY to/from client in PL/pgSQL"
msgstr "não pode executar COPY para/do cliente em PL/pgSQL"
-#: pl_exec.c:3473 pl_exec.c:3617 pl_exec.c:3806
+#: pl_exec.c:3422 pl_exec.c:3566 pl_exec.c:3755
#, c-format
msgid "cannot begin/end transactions in PL/pgSQL"
msgstr "não pode iniciar/terminar transações em PL/pgSQL"
-#: pl_exec.c:3474 pl_exec.c:3618 pl_exec.c:3807
+#: pl_exec.c:3423 pl_exec.c:3567 pl_exec.c:3756
#, c-format
msgid "Use a BEGIN block with an EXCEPTION clause instead."
msgstr "Ao invés disso utilize um bloco BEGIN com uma cláusula EXCEPTION."
-#: pl_exec.c:3641 pl_exec.c:3831
+#: pl_exec.c:3590 pl_exec.c:3780
#, c-format
msgid "INTO used with a command that cannot return data"
msgstr "INTO utilizado com um comando que não pode retornar dados"
-#: pl_exec.c:3669 pl_exec.c:3859
+#: pl_exec.c:3618 pl_exec.c:3808
#, c-format
msgid "query returned no rows"
msgstr "consulta não retornou registros"
-#: pl_exec.c:3688 pl_exec.c:3878
+#: pl_exec.c:3637 pl_exec.c:3827
#, c-format
msgid "query returned more than one row"
msgstr "consulta retornou mais de um registro"
-#: pl_exec.c:3705
+#: pl_exec.c:3654
#, c-format
msgid "query has no destination for result data"
msgstr "consulta não tem destino para os dados resultantes"
-#: pl_exec.c:3706
+#: pl_exec.c:3655
#, c-format
msgid "If you want to discard the results of a SELECT, use PERFORM instead."
msgstr "Se você quer descartar os resultados de um SELECT, utilize PERFORM."
-#: pl_exec.c:3738 pl_exec.c:7026
+#: pl_exec.c:3687 pl_exec.c:7128
#, c-format
msgid "query string argument of EXECUTE is null"
msgstr "argumento da cadeia de caracteres do EXECUTE é nulo"
-#: pl_exec.c:3794
+#: pl_exec.c:3743
#, c-format
msgid "EXECUTE of SELECT ... INTO is not implemented"
msgstr "EXECUTE de SELECT ... INTO não está implementado"
-#: pl_exec.c:3795
+#: pl_exec.c:3744
#, c-format
msgid "You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS instead."
msgstr "Ao invés disso, você pode querer utilizar EXECUTE ... INTO ou EXECUTE CREATE TABLE ... AS."
-#: pl_exec.c:4105 pl_exec.c:4197
+#: pl_exec.c:4056 pl_exec.c:4148
#, c-format
msgid "cursor variable \"%s\" is null"
msgstr "variável do cursor \"%s\" é nula"
-#: pl_exec.c:4112 pl_exec.c:4204
+#: pl_exec.c:4063 pl_exec.c:4155
#, c-format
msgid "cursor \"%s\" does not exist"
msgstr "cursor \"%s\" não existe"
-#: pl_exec.c:4126
+#: pl_exec.c:4077
#, c-format
msgid "relative or absolute cursor position is null"
msgstr "posição relativa ou absoluta do cursor é nula"
-#: pl_exec.c:4306
+#: pl_exec.c:4257
#, c-format
msgid "null value cannot be assigned to variable \"%s\" declared NOT NULL"
msgstr "valor nulo não pode ser atribuído a variável \"%s\" declarada NOT NULL"
-#: pl_exec.c:4378
+#: pl_exec.c:4326
#, c-format
msgid "cannot assign non-composite value to a row variable"
msgstr "não pode atribuir valor que não é composto a variável do tipo row"
-#: pl_exec.c:4402
+#: pl_exec.c:4350
#, c-format
msgid "cannot assign non-composite value to a record variable"
msgstr "não pode atribuir valor que não é composto a variável do tipo record"
-#: pl_exec.c:4545
+#: pl_exec.c:4493
#, c-format
msgid "number of array dimensions (%d) exceeds the maximum allowed (%d)"
msgstr "número de dimensões da matriz (%d) excede o máximo permitido (%d)"
-#: pl_exec.c:4577
+#: pl_exec.c:4525
#, c-format
msgid "subscripted object is not an array"
msgstr "objeto com índice não é uma matriz"
-#: pl_exec.c:4614
+#: pl_exec.c:4562
#, c-format
msgid "array subscript in assignment must not be null"
msgstr "índice da matriz em atribuição não deve ser nulo"
-#: pl_exec.c:5080
+#: pl_exec.c:5029
#, c-format
msgid "query \"%s\" did not return data"
msgstr "consulta \"%s\" não retornou dados"
-#: pl_exec.c:5088
+#: pl_exec.c:5037
#, c-format
msgid "query \"%s\" returned %d column"
msgid_plural "query \"%s\" returned %d columns"
msgstr[0] "consulta \"%s\" retornou %d coluna"
msgstr[1] "consulta \"%s\" retornou %d colunas"
-#: pl_exec.c:5115
+#: pl_exec.c:5064
#, c-format
msgid "query \"%s\" returned more than one row"
msgstr "consulta \"%s\" retornou mais do que um registro"
-#: pl_exec.c:5170
+#: pl_exec.c:5128
#, c-format
msgid "query \"%s\" is not a SELECT"
msgstr "consulta \"%s\" não é um SELECT"
-#: pl_funcs.c:218
+#: pl_funcs.c:237
msgid "statement block"
msgstr "bloco de comandos"
-#: pl_funcs.c:220
+#: pl_funcs.c:239
msgid "assignment"
msgstr "atribuição"
-#: pl_funcs.c:230
+#: pl_funcs.c:249
msgid "FOR with integer loop variable"
msgstr "FOR com variável de laço inteira"
-#: pl_funcs.c:232
+#: pl_funcs.c:251
msgid "FOR over SELECT rows"
msgstr "FOR sobre registros de SELECT"
-#: pl_funcs.c:234
+#: pl_funcs.c:253
msgid "FOR over cursor"
msgstr "FOR sobre cursor"
-#: pl_funcs.c:236
+#: pl_funcs.c:255
msgid "FOREACH over array"
msgstr "FOREACH sobre matriz"
-#: pl_funcs.c:250
+#: pl_funcs.c:269
msgid "SQL statement"
msgstr "comando SQL"
-#: pl_funcs.c:254
+#: pl_funcs.c:273
msgid "FOR over EXECUTE statement"
msgstr "FOR sobre comando EXECUTE"
-#: pl_gram.y:472
+#: pl_gram.y:474
#, c-format
msgid "block label must be placed before DECLARE, not after"
msgstr "rótulo de bloco deve estar localizado antes do DECLARE e não depois"
-#: pl_gram.y:492
+#: pl_gram.y:494
#, c-format
msgid "collations are not supported by type %s"
msgstr "ordenações não são suportadas pelo tipo %s"
-#: pl_gram.y:507
+#: pl_gram.y:509
#, c-format
msgid "row or record variable cannot be CONSTANT"
msgstr "variável do tipo row ou record não pode ser CONSTANT"
-#: pl_gram.y:517
+#: pl_gram.y:519
#, c-format
msgid "row or record variable cannot be NOT NULL"
msgstr "variável do tipo row ou record não pode ser NOT NULL"
-#: pl_gram.y:528
+#: pl_gram.y:530
#, c-format
msgid "default value for row or record variable is not supported"
msgstr "valor padrão para variável do tipo row ou record não é suportado"
-#: pl_gram.y:673 pl_gram.y:688 pl_gram.y:714
+#: pl_gram.y:675 pl_gram.y:690 pl_gram.y:716
#, c-format
msgid "variable \"%s\" does not exist"
msgstr "variável \"%s\" não existe"
-#: pl_gram.y:732 pl_gram.y:760
+#: pl_gram.y:734 pl_gram.y:762
msgid "duplicate declaration"
msgstr "declaração duplicada"
-#: pl_gram.y:743 pl_gram.y:771
+#: pl_gram.y:745 pl_gram.y:773
#, c-format
msgid "variable \"%s\" shadows a previously defined variable"
msgstr "variável \"%s\" esconde uma variável previamente definida"
-#: pl_gram.y:950
+#: pl_gram.y:952
#, c-format
msgid "diagnostics item %s is not allowed in GET STACKED DIAGNOSTICS"
msgstr "item de diagnóstico %s não é permitido em GET STACKED DIAGNOSTICS"
-#: pl_gram.y:968
+#: pl_gram.y:970
#, c-format
msgid "diagnostics item %s is not allowed in GET CURRENT DIAGNOSTICS"
msgstr "item de diagnóstico %s não é permitido em GET CURRENT DIAGNOSTICS"
-#: pl_gram.y:1066
+#: pl_gram.y:1068
msgid "unrecognized GET DIAGNOSTICS item"
msgstr "item de GET DIAGNOSTICS desconhecido"
-#: pl_gram.y:1077 pl_gram.y:3361
+#: pl_gram.y:1079 pl_gram.y:3439
#, c-format
msgid "\"%s\" is not a scalar variable"
msgstr "\"%s\" não é uma variável escalar"
-#: pl_gram.y:1329 pl_gram.y:1523
+#: pl_gram.y:1331 pl_gram.y:1525
#, c-format
msgid "loop variable of loop over rows must be a record or row variable or list of scalar variables"
msgstr "variável de laço sobre registros deve ser uma variável do tipo record ou row ou lista de variáveis escalares"
-#: pl_gram.y:1363
+#: pl_gram.y:1365
#, c-format
msgid "cursor FOR loop must have only one target variable"
msgstr "cursor do laço FOR deve ter somente uma variável alvo"
-#: pl_gram.y:1370
+#: pl_gram.y:1372
#, c-format
msgid "cursor FOR loop must use a bound cursor variable"
msgstr "cursor do laço FOR deve utilizar uma variável cursor limitado"
-#: pl_gram.y:1454
+#: pl_gram.y:1456
#, c-format
msgid "integer FOR loop must have only one target variable"
msgstr "inteiro do laço FOR deve ter somente uma variável alvo"
-#: pl_gram.y:1490
+#: pl_gram.y:1492
#, c-format
msgid "cannot specify REVERSE in query FOR loop"
msgstr "não pode especificar REVERSE na consulta do laço FOR"
-#: pl_gram.y:1637
+#: pl_gram.y:1639
#, c-format
msgid "loop variable of FOREACH must be a known variable or list of variables"
msgstr "variável do laço FOEACH deve ser uma variável ou lista de variáveis conhecida"
-#: pl_gram.y:1689 pl_gram.y:1726 pl_gram.y:1774 pl_gram.y:2814 pl_gram.y:2896
-#: pl_gram.y:3007 pl_gram.y:3763
+#: pl_gram.y:1680
+#, c-format
+msgid "there is no label \"%s\" attached to any block or loop enclosing this statement"
+msgstr "não há rótulo \"%s\" ligado a qualquer bloco ou laço que contém este comando"
+
+#: pl_gram.y:1688
+#, c-format
+msgid "block label \"%s\" cannot be used in CONTINUE"
+msgstr "rótulo de bloco \"%s\" não pode ser utilizado no CONTINUE"
+
+#: pl_gram.y:1703
+#, c-format
+msgid "EXIT cannot be used outside a loop, unless it has a label"
+msgstr "EXIT não pode ser utilizado fora de um laço, a menos que ele tenha um rótulo"
+
+#: pl_gram.y:1704
+#, c-format
+msgid "CONTINUE cannot be used outside a loop"
+msgstr "CONTINUE não pode ser utilizado fora de um laço"
+
+#: pl_gram.y:1728 pl_gram.y:1765 pl_gram.y:1813 pl_gram.y:2889 pl_gram.y:2974
+#: pl_gram.y:3085 pl_gram.y:3841
msgid "unexpected end of function definition"
msgstr "fim de definição da função inesperado"
-#: pl_gram.y:1794 pl_gram.y:1818 pl_gram.y:1834 pl_gram.y:1840 pl_gram.y:1954
-#: pl_gram.y:1962 pl_gram.y:1976 pl_gram.y:2071 pl_gram.y:2252 pl_gram.y:2335
-#: pl_gram.y:2486 pl_gram.y:3604 pl_gram.y:3665 pl_gram.y:3744
+#: pl_gram.y:1833 pl_gram.y:1857 pl_gram.y:1873 pl_gram.y:1879 pl_gram.y:1997
+#: pl_gram.y:2005 pl_gram.y:2019 pl_gram.y:2114 pl_gram.y:2295 pl_gram.y:2389
+#: pl_gram.y:2541 pl_gram.y:3682 pl_gram.y:3743 pl_gram.y:3822
msgid "syntax error"
msgstr "erro de sintaxe"
-#: pl_gram.y:1822 pl_gram.y:1824 pl_gram.y:2256 pl_gram.y:2258
+#: pl_gram.y:1861 pl_gram.y:1863 pl_gram.y:2299 pl_gram.y:2301
msgid "invalid SQLSTATE code"
msgstr "código SQLSTATE inválido"
-#: pl_gram.y:2018
+#: pl_gram.y:2061
msgid "syntax error, expected \"FOR\""
msgstr "erro de sintaxe, \"FOR\" esperado"
-#: pl_gram.y:2080
+#: pl_gram.y:2123
#, c-format
msgid "FETCH statement cannot return multiple rows"
msgstr "comando FETCH não pode retornar múltiplos registros"
-#: pl_gram.y:2136
+#: pl_gram.y:2179
#, c-format
msgid "cursor variable must be a simple variable"
msgstr "variável do cursor deve ser uma variável simples"
-#: pl_gram.y:2142
+#: pl_gram.y:2185
#, c-format
msgid "variable \"%s\" must be of type cursor or refcursor"
msgstr "variável \"%s\" deve ser do tipo cursor ou refcursor"
-#: pl_gram.y:2310
-msgid "label does not exist"
-msgstr "rótulo não existe"
-
-#: pl_gram.y:2457 pl_gram.y:2468
+#: pl_gram.y:2512 pl_gram.y:2523
#, c-format
msgid "\"%s\" is not a known variable"
msgstr "\"%s\" não é uma variável conhecida"
-#: pl_gram.y:2572 pl_gram.y:2582 pl_gram.y:2738
+#: pl_gram.y:2627 pl_gram.y:2637 pl_gram.y:2793
msgid "mismatched parentheses"
msgstr "parênteses não correspondem"
-#: pl_gram.y:2586
+#: pl_gram.y:2641
#, c-format
msgid "missing \"%s\" at end of SQL expression"
msgstr "faltando \"%s\" ao fim da expressão SQL"
-#: pl_gram.y:2592
+#: pl_gram.y:2647
#, c-format
msgid "missing \"%s\" at end of SQL statement"
msgstr "faltando \"%s\" ao fim do comando SQL"
-#: pl_gram.y:2609
+#: pl_gram.y:2664
msgid "missing expression"
msgstr "faltando expressão"
-#: pl_gram.y:2611
+#: pl_gram.y:2666
msgid "missing SQL statement"
msgstr "faltando comando SQL"
-#: pl_gram.y:2740
+#: pl_gram.y:2795
msgid "incomplete data type declaration"
msgstr "declaração de tipo de dado incompleta"
-#: pl_gram.y:2763
+#: pl_gram.y:2818
msgid "missing data type declaration"
msgstr "faltando declaração de tipo de dado"
-#: pl_gram.y:2819
+#: pl_gram.y:2897
msgid "INTO specified more than once"
msgstr "INTO especificado mais de uma vez"
-#: pl_gram.y:2988
+#: pl_gram.y:3066
msgid "expected FROM or IN"
msgstr "FROM ou IN esperado"
-#: pl_gram.y:3048
+#: pl_gram.y:3126
#, c-format
msgid "RETURN cannot have a parameter in function returning set"
msgstr "RETURN não pode ter um parâmetro na função que retorna conjunto"
-#: pl_gram.y:3049
+#: pl_gram.y:3127
#, c-format
msgid "Use RETURN NEXT or RETURN QUERY."
msgstr "Utilize RETURN NEXT ou RETURN QUERY."
-#: pl_gram.y:3057
+#: pl_gram.y:3135
#, c-format
msgid "RETURN cannot have a parameter in function with OUT parameters"
msgstr "RETURN não pode ter um parâmetro na função com parâmetros OUT"
-#: pl_gram.y:3066
+#: pl_gram.y:3144
#, c-format
msgid "RETURN cannot have a parameter in function returning void"
msgstr "RETURN não pode ter um parâmetro na função que retorna void"
-#: pl_gram.y:3126
+#: pl_gram.y:3204
#, c-format
msgid "RETURN NEXT cannot have a parameter in function with OUT parameters"
msgstr "RETURN NEXT não pode ter um parâmetro na função com parâmetros OUT"
-#: pl_gram.y:3230
+#: pl_gram.y:3308
#, c-format
msgid "\"%s\" is declared CONSTANT"
msgstr "\"%s\" está declarado CONSTANT"
-#: pl_gram.y:3292 pl_gram.y:3304
+#: pl_gram.y:3370 pl_gram.y:3382
#, c-format
msgid "record or row variable cannot be part of multiple-item INTO list"
msgstr "variável do tipo record ou row não pode ser parte de uma lista INTO de múltiplos itens"
-#: pl_gram.y:3349
+#: pl_gram.y:3427
#, c-format
msgid "too many INTO variables specified"
msgstr "muitas variáveis INTO especificadas"
-#: pl_gram.y:3557
+#: pl_gram.y:3635
#, c-format
msgid "end label \"%s\" specified for unlabelled block"
msgstr "rótulo de fim \"%s\" especificado para bloco sem rótulo"
-#: pl_gram.y:3564
+#: pl_gram.y:3642
#, c-format
msgid "end label \"%s\" differs from block's label \"%s\""
msgstr "rótulo de fim \"%s\" difere de rótulo do bloco \"%s\""
-#: pl_gram.y:3599
+#: pl_gram.y:3677
#, c-format
msgid "cursor \"%s\" has no arguments"
msgstr "cursor \"%s\" não tem argumentos"
-#: pl_gram.y:3613
+#: pl_gram.y:3691
#, c-format
msgid "cursor \"%s\" has arguments"
msgstr "cursor \"%s\" tem argumentos"
-#: pl_gram.y:3655
+#: pl_gram.y:3733
#, c-format
msgid "cursor \"%s\" has no argument named \"%s\""
msgstr "cursor \"%s\" não tem argumento chamado \"%s\""
-#: pl_gram.y:3675
+#: pl_gram.y:3753
#, c-format
msgid "value for parameter \"%s\" of cursor \"%s\" specified more than once"
msgstr "valor para parâmetro \"%s\" do cursor \"%s\" foi especificado mais de uma vez"
-#: pl_gram.y:3700
+#: pl_gram.y:3778
#, c-format
msgid "not enough arguments for cursor \"%s\""
msgstr "argumentos insuficientes para cursor \"%s\""
-#: pl_gram.y:3707
+#: pl_gram.y:3785
#, c-format
msgid "too many arguments for cursor \"%s\""
msgstr "muitos argumentos para cursor \"%s\""
-#: pl_gram.y:3795
+#: pl_gram.y:3873
msgid "unrecognized RAISE statement option"
msgstr "opção do comando RAISE desconhecida"
-#: pl_gram.y:3799
+#: pl_gram.y:3877
msgid "syntax error, expected \"=\""
msgstr "erro de sintaxe, \"=\" esperado"
-#: pl_gram.y:3840
+#: pl_gram.y:3918
#, c-format
msgid "too many parameters specified for RAISE"
msgstr "muitos parâmetros especificados para RAISE"
-#: pl_gram.y:3844
+#: pl_gram.y:3922
#, c-format
msgid "too few parameters specified for RAISE"
msgstr "poucos parâmetros especificados para RAISE"
-#: pl_handler.c:149
+#: pl_handler.c:151
msgid "Sets handling of conflicts between PL/pgSQL variable names and table column names."
msgstr "Define resolução de conflitos entre nomes de variáveis PL/pgSQL e nomes de colunas de tabelas."
-#: pl_handler.c:158
+#: pl_handler.c:160
msgid "Print information about parameters in the DETAIL part of the error messages generated on INTO ... STRICT failures."
msgstr "Mostra informação sobre parâmetros na parte DETALHE das mensagens de erro geradas nas falhas INTO ... STRICT."
-#: pl_handler.c:166
+#: pl_handler.c:168
msgid "Perform checks given in ASSERT statements."
msgstr "Realiza verificações informadas em comandos ASSERT."
-#: pl_handler.c:174
+#: pl_handler.c:176
msgid "List of programming constructs that should produce a warning."
msgstr "Lista de construções de programação que devem produzir um aviso."
-#: pl_handler.c:184
+#: pl_handler.c:186
msgid "List of programming constructs that should produce an error."
msgstr "Lista de construções de programação que devem produzir um erro."
#. translator: %s is typically the translation of "syntax error"
-#: pl_scanner.c:621
+#: pl_scanner.c:622
#, c-format
msgid "%s at end of input"
msgstr "%s no fim da entrada"
#. translator: first %s is typically the translation of "syntax error"
-#: pl_scanner.c:637
+#: pl_scanner.c:638
#, c-format
msgid "%s at or near \"%s\""
msgstr "%s em ou próximo a \"%s\""
diff --git a/src/pl/plpgsql/src/po/ru.po b/src/pl/plpgsql/src/po/ru.po
index 6abf05cae5..759f368b13 100644
--- a/src/pl/plpgsql/src/po/ru.po
+++ b/src/pl/plpgsql/src/po/ru.po
@@ -1,59 +1,51 @@
# Russian message translation file for plpgsql
-#
-# Copyright (C) 2012 PostgreSQL Global Development Group
-# Copyright (c) 2012-2013 Alexander Lakhin, exclusion@gmail.com
+# Copyright (C) 2012-2016 PostgreSQL Global Development Group
# This file is distributed under the same license as the PostgreSQL package.
+# Alexander Lakhin <exclusion@gmail.com>, 2012-2017.
#
-# ChangeLog:
-# - August 24, 2014: Updates for 9.4. Alexander Lakhin <exclusion@gmail.com>.
-# - March 14, 2013: Updates for 9.3. Alexander Lakhin <exclusion@gmail.com>.
-# - June 27, 2012: Updates for 9.2. Alexander Lakhin <exclusion@gmail.com>.
-# - April 2, 2012: Bug fixes. Alexander Lakhin <exclusion@gmail.com>.
-# - February 19, 2012: Complete translation for 9.1. Alexander Lakhin <exclusion@gmail.com>.
msgid ""
msgstr ""
-"Project-Id-Version: PostgreSQL 9 current\n"
+"Project-Id-Version: plpgsql (PostgreSQL current)\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2015-09-19 03:07+0000\n"
-"PO-Revision-Date: 2015-10-16 21:32+0400\n"
+"POT-Creation-Date: 2017-04-02 23:37+0000\n"
+"PO-Revision-Date: 2016-11-24 11:24+0300\n"
"Last-Translator: Alexander Lakhin <exclusion@gmail.com>\n"
-"Language-Team: Russian <pgsql-translators@postgresql.org>\n"
+"Language-Team: Russian <pgsql-ru-general@postgresql.org>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-"X-Generator: Lokalize 2.0\n"
-#: pl_comp.c:432 pl_handler.c:448
+#: pl_comp.c:433 pl_handler.c:451
#, c-format
msgid "PL/pgSQL functions cannot accept type %s"
msgstr "функции PL/pgSQL не могут принимать тип %s"
-#: pl_comp.c:513
+#: pl_comp.c:514
#, c-format
msgid "could not determine actual return type for polymorphic function \"%s\""
msgstr ""
"не удалось определить фактический тип результата для полиморфной функции \"%s"
"\""
-#: pl_comp.c:543
+#: pl_comp.c:544
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "триггерные функции могут вызываться только в триггерах"
-#: pl_comp.c:547 pl_handler.c:433
+#: pl_comp.c:548 pl_handler.c:436
#, c-format
msgid "PL/pgSQL functions cannot return type %s"
msgstr "функции PL/pgSQL не могут возвращать тип %s"
-#: pl_comp.c:588
+#: pl_comp.c:589
#, c-format
msgid "trigger functions cannot have declared arguments"
msgstr "у триггерных функций не может быть объявленных аргументов"
-#: pl_comp.c:589
+#: pl_comp.c:590
#, c-format
msgid ""
"The arguments of the trigger can be accessed through TG_NARGS and TG_ARGV "
@@ -62,63 +54,63 @@ msgstr ""
"При необходимости к аргументам триггера можно обращаться через переменные "
"TG_NARGS and TG_ARGV."
-#: pl_comp.c:691
+#: pl_comp.c:692
#, c-format
msgid "event trigger functions cannot have declared arguments"
msgstr "у функций событийных триггеров не может быть объявленных аргументов"
-#: pl_comp.c:944
+#: pl_comp.c:943
#, c-format
msgid "compilation of PL/pgSQL function \"%s\" near line %d"
msgstr "компиляция функции PL/pgSQL \"%s\" в районе строки %d"
-#: pl_comp.c:967
+#: pl_comp.c:966
#, c-format
msgid "parameter name \"%s\" used more than once"
msgstr "имя параметра \"%s\" указано неоднократно"
-#: pl_comp.c:1077
+#: pl_comp.c:1076
#, c-format
msgid "column reference \"%s\" is ambiguous"
msgstr "неоднозначная ссылка на столбец \"%s\""
-#: pl_comp.c:1079
+#: pl_comp.c:1078
#, c-format
msgid "It could refer to either a PL/pgSQL variable or a table column."
msgstr "Подразумевается ссылка на переменную PL/pgSQL или столбец таблицы."
-#: pl_comp.c:1259 pl_comp.c:1287 pl_exec.c:4394 pl_exec.c:4743 pl_exec.c:4828
-#: pl_exec.c:4919
+#: pl_comp.c:1258 pl_comp.c:1286 pl_exec.c:4624 pl_exec.c:4953 pl_exec.c:5038
+#: pl_exec.c:5129
#, c-format
msgid "record \"%s\" has no field \"%s\""
msgstr "в записи \"%s\" нет поля \"%s\""
-#: pl_comp.c:1818
+#: pl_comp.c:1817
#, c-format
msgid "relation \"%s\" does not exist"
msgstr "отношение \"%s\" не существует"
-#: pl_comp.c:1927
+#: pl_comp.c:1926
#, c-format
msgid "variable \"%s\" has pseudo-type %s"
msgstr "переменная \"%s\" имеет псевдотип %s"
-#: pl_comp.c:1994
+#: pl_comp.c:1993
#, c-format
msgid "relation \"%s\" is not a table"
msgstr "отношение \"%s\" не является таблицей"
-#: pl_comp.c:2154
+#: pl_comp.c:2153
#, c-format
msgid "type \"%s\" is only a shell"
msgstr "тип \"%s\" - лишь пустышка"
-#: pl_comp.c:2243 pl_comp.c:2296
+#: pl_comp.c:2247 pl_comp.c:2300
#, c-format
msgid "unrecognized exception condition \"%s\""
msgstr "нераспознанное условие исключения \"%s\""
-#: pl_comp.c:2503
+#: pl_comp.c:2508
#, c-format
msgid ""
"could not determine actual argument type for polymorphic function \"%s\""
@@ -126,52 +118,52 @@ msgstr ""
"не удалось определить фактический тип аргумента для полиморфной функции \"%s"
"\""
-#: pl_exec.c:324 pl_exec.c:612 pl_exec.c:872
+#: pl_exec.c:355 pl_exec.c:644 pl_exec.c:951
msgid "during initialization of execution state"
msgstr "в процессе инициализации состояния выполнения"
-#: pl_exec.c:331
+#: pl_exec.c:362
msgid "while storing call arguments into local variables"
msgstr "при сохранении аргументов вызова в локальных переменных"
-#: pl_exec.c:416 pl_exec.c:760
+#: pl_exec.c:447 pl_exec.c:833
msgid "during function entry"
msgstr "при входе в функцию"
-#: pl_exec.c:441
+#: pl_exec.c:472
#, c-format
msgid "control reached end of function without RETURN"
msgstr "конец функции достигнут без RETURN"
-#: pl_exec.c:448
+#: pl_exec.c:479
msgid "while casting return value to function's return type"
msgstr "при приведении возвращаемого значения к типу результата функции"
-#: pl_exec.c:461 pl_exec.c:2938
+#: pl_exec.c:492 pl_exec.c:3138
#, c-format
msgid "set-valued function called in context that cannot accept a set"
msgstr ""
"функция, возвращающая множество, вызвана в контексте, где ему нет места"
-#: pl_exec.c:499 pl_exec.c:2779
+#: pl_exec.c:530 pl_exec.c:2985
msgid "returned record type does not match expected record type"
msgstr "возвращаемый тип записи не соответствует ожидаемому"
-#: pl_exec.c:554 pl_exec.c:789 pl_exec.c:907
+#: pl_exec.c:585 pl_exec.c:862 pl_exec.c:986
msgid "during function exit"
msgstr "при выходе из функции"
-#: pl_exec.c:785 pl_exec.c:903
+#: pl_exec.c:858 pl_exec.c:982
#, c-format
msgid "control reached end of trigger procedure without RETURN"
msgstr "конец триггерной процедуры достигнут без RETURN"
-#: pl_exec.c:794
+#: pl_exec.c:867
#, c-format
msgid "trigger procedure cannot return a set"
msgstr "триггерная процедура не может возвращать множество"
-#: pl_exec.c:816
+#: pl_exec.c:889
msgid ""
"returned row structure does not match the structure of the triggering table"
msgstr ""
@@ -181,7 +173,7 @@ msgstr ""
#. translator: last %s is a phrase such as "during statement block
#. local variable initialization"
#.
-#: pl_exec.c:954
+#: pl_exec.c:1034
#, c-format
msgid "PL/pgSQL function %s line %d %s"
msgstr "функция PL/pgSQL %s, строка %d, %s"
@@ -189,249 +181,250 @@ msgstr "функция PL/pgSQL %s, строка %d, %s"
#. translator: last %s is a phrase such as "while storing call
#. arguments into local variables"
#.
-#: pl_exec.c:965
+#: pl_exec.c:1045
#, c-format
msgid "PL/pgSQL function %s %s"
msgstr "функция PL/pgSQL %s, %s"
#. translator: last %s is a plpgsql statement type name
-#: pl_exec.c:973
+#: pl_exec.c:1053
#, c-format
msgid "PL/pgSQL function %s line %d at %s"
msgstr "функция PL/pgSQL %s, строка %d, оператор %s"
-#: pl_exec.c:979
+#: pl_exec.c:1059
#, c-format
msgid "PL/pgSQL function %s"
msgstr "функция PL/pgSQL %s"
-#: pl_exec.c:1089
+#: pl_exec.c:1224
msgid "during statement block local variable initialization"
msgstr "при инициализации локальной переменной в блоке операторов"
-#: pl_exec.c:1128
+#: pl_exec.c:1263
#, c-format
msgid "variable \"%s\" declared NOT NULL cannot default to NULL"
msgstr ""
"переменная \"%s\", объявленная NOT NULL, не может иметь значение по "
"умолчанию NULL"
-#: pl_exec.c:1178
+#: pl_exec.c:1314
msgid "during statement block entry"
msgstr "при входе в блок операторов"
-#: pl_exec.c:1199
+#: pl_exec.c:1346
msgid "during statement block exit"
msgstr "при выходе из блока операторов"
-#: pl_exec.c:1242
+#: pl_exec.c:1388
msgid "during exception cleanup"
msgstr "при очистке после исключения"
-#: pl_exec.c:1593
+#: pl_exec.c:1754
#, c-format
msgid "GET STACKED DIAGNOSTICS cannot be used outside an exception handler"
msgstr ""
"GET STACKED DIAGNOSTICS нельзя использовать вне блока обработчика исключения"
-#: pl_exec.c:1789
+#: pl_exec.c:1959
#, c-format
msgid "case not found"
msgstr "неправильный CASE"
-#: pl_exec.c:1790
+#: pl_exec.c:1960
#, c-format
msgid "CASE statement is missing ELSE part."
msgstr "В операторе CASE не хватает части ELSE."
-#: pl_exec.c:1944
+#: pl_exec.c:2114
#, c-format
msgid "lower bound of FOR loop cannot be null"
msgstr "нижняя граница цикла FOR не может быть равна NULL"
-#: pl_exec.c:1960
+#: pl_exec.c:2130
#, c-format
msgid "upper bound of FOR loop cannot be null"
msgstr "верхняя граница цикла FOR не может быть равна NULL"
-#: pl_exec.c:1978
+#: pl_exec.c:2148
#, c-format
msgid "BY value of FOR loop cannot be null"
msgstr "значение BY в цикле FOR не может быть равно NULL"
-#: pl_exec.c:1984
+#: pl_exec.c:2154
#, c-format
msgid "BY value of FOR loop must be greater than zero"
msgstr "значение BY в цикле FOR должно быть больше нуля"
-#: pl_exec.c:2153 pl_exec.c:3911
+#: pl_exec.c:2331 pl_exec.c:4125
#, c-format
msgid "cursor \"%s\" already in use"
msgstr "курсор \"%s\" уже используется"
-#: pl_exec.c:2176 pl_exec.c:3973
+#: pl_exec.c:2354 pl_exec.c:4190
#, c-format
msgid "arguments given for cursor without arguments"
msgstr "курсору без аргументов были переданы аргументы"
-#: pl_exec.c:2195 pl_exec.c:3992
+#: pl_exec.c:2373 pl_exec.c:4209
#, c-format
msgid "arguments required for cursor"
msgstr "курсору требуются аргументы"
-#: pl_exec.c:2280
+#: pl_exec.c:2460
#, c-format
msgid "FOREACH expression must not be null"
msgstr "выражение FOREACH не может быть равно NULL"
-#: pl_exec.c:2286
+#: pl_exec.c:2475
#, c-format
msgid "FOREACH expression must yield an array, not type %s"
msgstr "выражение в FOREACH должно быть массивом, но не типом %s"
-#: pl_exec.c:2303
+#: pl_exec.c:2492
#, c-format
msgid "slice dimension (%d) is out of the valid range 0..%d"
msgstr "размерность среза (%d) вне допустимого диапазона 0..%d"
-#: pl_exec.c:2330
+#: pl_exec.c:2519
#, c-format
msgid "FOREACH ... SLICE loop variable must be of an array type"
msgstr "переменная цикла FOREACH ... SLICE должна быть массивом"
-#: pl_exec.c:2334
+#: pl_exec.c:2523
#, c-format
msgid "FOREACH loop variable must not be of an array type"
msgstr "переменная цикла FOREACH не должна быть массивом"
-#: pl_exec.c:2522 pl_exec.c:2604 pl_exec.c:2771
+#: pl_exec.c:2726 pl_exec.c:2808 pl_exec.c:2978
#, c-format
msgid ""
"cannot return non-composite value from function returning composite type"
msgstr ""
"функция, возвращающая составной тип, не может вернуть несоставное значение"
-#: pl_exec.c:2648 pl_gram.y:3161
+#: pl_exec.c:2852 pl_gram.y:3199
#, c-format
msgid "cannot use RETURN NEXT in a non-SETOF function"
msgstr ""
"RETURN NEXT можно использовать только в функциях, возвращающих множества"
-#: pl_exec.c:2682 pl_exec.c:2813
+#: pl_exec.c:2886 pl_exec.c:3013
#, c-format
msgid "wrong result type supplied in RETURN NEXT"
msgstr "в RETURN NEXT передан неправильный тип результата"
-#: pl_exec.c:2711 pl_exec.c:4381 pl_exec.c:4710 pl_exec.c:4736 pl_exec.c:4802
-#: pl_exec.c:4821 pl_exec.c:4889 pl_exec.c:4912
+#: pl_exec.c:2915 pl_exec.c:4612 pl_exec.c:4920 pl_exec.c:4946 pl_exec.c:5012
+#: pl_exec.c:5031 pl_exec.c:5099 pl_exec.c:5122
#, c-format
msgid "record \"%s\" is not assigned yet"
msgstr "записи \"%s\" не присвоено значение"
-#: pl_exec.c:2713 pl_exec.c:4383 pl_exec.c:4712 pl_exec.c:4738 pl_exec.c:4804
-#: pl_exec.c:4823 pl_exec.c:4891 pl_exec.c:4914
+#: pl_exec.c:2917 pl_exec.c:4614 pl_exec.c:4922 pl_exec.c:4948 pl_exec.c:5014
+#: pl_exec.c:5033 pl_exec.c:5101 pl_exec.c:5124
#, c-format
msgid "The tuple structure of a not-yet-assigned record is indeterminate."
msgstr ""
"Для записи, которой не присвоено значение, структура кортежа не определена."
-#: pl_exec.c:2717 pl_exec.c:2737
+#: pl_exec.c:2924 pl_exec.c:2943
#, c-format
msgid "wrong record type supplied in RETURN NEXT"
msgstr "в RETURN NEXT передан неправильный тип записи"
-#: pl_exec.c:2832
+#: pl_exec.c:3032
#, c-format
msgid "RETURN NEXT must have a parameter"
msgstr "у оператора RETURN NEXT должен быть параметр"
-#: pl_exec.c:2865 pl_gram.y:3223
+#: pl_exec.c:3058 pl_gram.y:3261
#, c-format
msgid "cannot use RETURN QUERY in a non-SETOF function"
msgstr ""
"RETURN QUERY можно использовать только в функциях, возвращающих множества"
-#: pl_exec.c:2886
+#: pl_exec.c:3082
msgid "structure of query does not match function result type"
msgstr "структура запроса не соответствует типу результата функции"
-#: pl_exec.c:2966 pl_exec.c:3096
+#: pl_exec.c:3166 pl_exec.c:3304
#, c-format
msgid "RAISE option already specified: %s"
msgstr "этот параметр RAISE уже указан: %s"
-#: pl_exec.c:2999
+#: pl_exec.c:3200
#, c-format
msgid "RAISE without parameters cannot be used outside an exception handler"
msgstr ""
"RAISE без параметров нельзя использовать вне блока обработчика исключения"
-#: pl_exec.c:3086
+#: pl_exec.c:3294
#, c-format
msgid "RAISE statement option cannot be null"
msgstr "параметром оператора RAISE не может быть NULL"
-#: pl_exec.c:3155
+#: pl_exec.c:3364
#, c-format
msgid "%s"
msgstr "%s"
-#: pl_exec.c:3226
+#: pl_exec.c:3419
#, c-format
msgid "assertion failed"
msgstr "нарушение истинности"
-#: pl_exec.c:3417 pl_exec.c:3561 pl_exec.c:3750
+#: pl_exec.c:3623 pl_exec.c:3769 pl_exec.c:3959
#, c-format
msgid "cannot COPY to/from client in PL/pgSQL"
msgstr "в PL/pgSQL нельзя выполнить COPY с участием клиента"
-#: pl_exec.c:3421 pl_exec.c:3565 pl_exec.c:3754
+#: pl_exec.c:3627 pl_exec.c:3773 pl_exec.c:3963
#, c-format
msgid "cannot begin/end transactions in PL/pgSQL"
msgstr "в PL/pgSQL нельзя начинать/заканчивать транзакции"
-#: pl_exec.c:3422 pl_exec.c:3566 pl_exec.c:3755
+#: pl_exec.c:3628 pl_exec.c:3774 pl_exec.c:3964
#, c-format
msgid "Use a BEGIN block with an EXCEPTION clause instead."
msgstr "Используйте блок BEGIN с предложением EXCEPTION."
-#: pl_exec.c:3589 pl_exec.c:3779
+#: pl_exec.c:3797 pl_exec.c:3988
#, c-format
msgid "INTO used with a command that cannot return data"
msgstr "INTO с командой не может возвращать данные"
-#: pl_exec.c:3617 pl_exec.c:3807
+#: pl_exec.c:3825 pl_exec.c:4016
#, c-format
msgid "query returned no rows"
msgstr "запрос не вернул строк"
-#: pl_exec.c:3636 pl_exec.c:3826
+#: pl_exec.c:3844 pl_exec.c:4035
#, c-format
msgid "query returned more than one row"
msgstr "запрос вернул несколько строк"
-#: pl_exec.c:3653
+#: pl_exec.c:3861
#, c-format
msgid "query has no destination for result data"
msgstr "в запросе нет назначения для данных результата"
-#: pl_exec.c:3654
+#: pl_exec.c:3862
#, c-format
msgid "If you want to discard the results of a SELECT, use PERFORM instead."
msgstr "Если вам нужно отбросить результаты SELECT, используйте PERFORM."
-#: pl_exec.c:3686 pl_exec.c:7122
+#: pl_exec.c:3895 pl_exec.c:7332
#, c-format
msgid "query string argument of EXECUTE is null"
msgstr "в качестве текста запроса в EXECUTE передан NULL"
-#: pl_exec.c:3742
+#: pl_exec.c:3951
#, c-format
msgid "EXECUTE of SELECT ... INTO is not implemented"
msgstr "возможность выполнения SELECT ... INTO в EXECUTE не реализована"
-#: pl_exec.c:3743
+# skip-rule: space-before-ellipsis
+#: pl_exec.c:3952
#, c-format
msgid ""
"You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS "
@@ -440,57 +433,57 @@ msgstr ""
"Альтернативой может стать EXECUTE ... INTO или EXECUTE CREATE TABLE ... "
"AS ..."
-#: pl_exec.c:4055 pl_exec.c:4147
+#: pl_exec.c:4273 pl_exec.c:4369
#, c-format
msgid "cursor variable \"%s\" is null"
msgstr "переменная курсора \"%s\" равна NULL"
-#: pl_exec.c:4062 pl_exec.c:4154
+#: pl_exec.c:4284 pl_exec.c:4380
#, c-format
msgid "cursor \"%s\" does not exist"
msgstr "курсор \"%s\" не существует"
-#: pl_exec.c:4076
+#: pl_exec.c:4297
#, c-format
msgid "relative or absolute cursor position is null"
msgstr "относительная или абсолютная позиция курсора равна NULL"
-#: pl_exec.c:4256
+#: pl_exec.c:4488
#, c-format
msgid "null value cannot be assigned to variable \"%s\" declared NOT NULL"
msgstr "значение NULL нельзя присвоить переменной \"%s\", объявленной NOT NULL"
-#: pl_exec.c:4325
+#: pl_exec.c:4557
#, c-format
msgid "cannot assign non-composite value to a row variable"
msgstr "переменной типа кортеж можно присвоить только составное значение"
-#: pl_exec.c:4349
+#: pl_exec.c:4581
#, c-format
msgid "cannot assign non-composite value to a record variable"
msgstr "переменной типа запись можно присвоить только составное значение"
-#: pl_exec.c:4492
+#: pl_exec.c:4701
#, c-format
msgid "number of array dimensions (%d) exceeds the maximum allowed (%d)"
msgstr "число размерностей массива (%d) превышает предел (%d)"
-#: pl_exec.c:4524
+#: pl_exec.c:4733
#, c-format
msgid "subscripted object is not an array"
msgstr "для объекта указан индекс, но этот объект - не массив"
-#: pl_exec.c:4561
+#: pl_exec.c:4771
#, c-format
msgid "array subscript in assignment must not be null"
msgstr "индекс элемента массива в присваивании не может быть NULL"
-#: pl_exec.c:5028
+#: pl_exec.c:5238
#, c-format
msgid "query \"%s\" did not return data"
msgstr "запрос \"%s\" не вернул данные"
-#: pl_exec.c:5036
+#: pl_exec.c:5246
#, c-format
msgid "query \"%s\" returned %d column"
msgid_plural "query \"%s\" returned %d columns"
@@ -498,107 +491,107 @@ msgstr[0] "запрос \"%s\" вернул %d строку"
msgstr[1] "запрос \"%s\" вернул %d строки"
msgstr[2] "запрос \"%s\" вернул %d строк"
-#: pl_exec.c:5063
+#: pl_exec.c:5273
#, c-format
msgid "query \"%s\" returned more than one row"
msgstr "запрос \"%s\" вернул несколько строк"
-#: pl_exec.c:5127
+#: pl_exec.c:5341
#, c-format
msgid "query \"%s\" is not a SELECT"
msgstr "запрос \"%s\" - не SELECT"
-#: pl_funcs.c:237
+#: pl_funcs.c:239
msgid "statement block"
msgstr "блок операторов"
-#: pl_funcs.c:239
+#: pl_funcs.c:241
msgid "assignment"
msgstr "присваивание"
-#: pl_funcs.c:249
+#: pl_funcs.c:251
msgid "FOR with integer loop variable"
msgstr "FOR с целочисленной переменной цикла"
-#: pl_funcs.c:251
+#: pl_funcs.c:253
msgid "FOR over SELECT rows"
msgstr "FOR по результатам SELECT"
-#: pl_funcs.c:253
+#: pl_funcs.c:255
msgid "FOR over cursor"
msgstr "FOR по курсору"
-#: pl_funcs.c:255
+#: pl_funcs.c:257
msgid "FOREACH over array"
msgstr "FOREACH для массива"
-#: pl_funcs.c:269
+#: pl_funcs.c:271
msgid "SQL statement"
msgstr "SQL-оператор"
-#: pl_funcs.c:273
+#: pl_funcs.c:275
msgid "FOR over EXECUTE statement"
msgstr "FOR по результатам EXECUTE"
-#: pl_gram.y:473
+#: pl_gram.y:478
#, c-format
msgid "block label must be placed before DECLARE, not after"
msgstr "метка блока должна помещаться до DECLARE, а не после"
-#: pl_gram.y:493
+#: pl_gram.y:498
#, c-format
msgid "collations are not supported by type %s"
msgstr "тип %s не поддерживает сортировку (COLLATION)"
-#: pl_gram.y:508
+#: pl_gram.y:513
#, c-format
msgid "row or record variable cannot be CONSTANT"
msgstr "переменная типа кортеж или запись не может быть константой"
-#: pl_gram.y:518
+#: pl_gram.y:523
#, c-format
msgid "row or record variable cannot be NOT NULL"
msgstr "переменная типа кортеж или запись не может быть NULL"
-#: pl_gram.y:529
+#: pl_gram.y:534
#, c-format
msgid "default value for row or record variable is not supported"
msgstr "переменная типа кортеж или запись не может иметь значения по умолчанию"
-#: pl_gram.y:674 pl_gram.y:689 pl_gram.y:715
+#: pl_gram.y:679 pl_gram.y:694 pl_gram.y:720
#, c-format
msgid "variable \"%s\" does not exist"
msgstr "переменная \"%s\" не существует"
-#: pl_gram.y:733 pl_gram.y:761
+#: pl_gram.y:738 pl_gram.y:766
msgid "duplicate declaration"
msgstr "повторяющееся объявление"
-#: pl_gram.y:744 pl_gram.y:772
+#: pl_gram.y:749 pl_gram.y:777
#, c-format
msgid "variable \"%s\" shadows a previously defined variable"
msgstr "переменная \"%s\" скрывает ранее определённую переменную"
-#: pl_gram.y:951
+#: pl_gram.y:956
#, c-format
msgid "diagnostics item %s is not allowed in GET STACKED DIAGNOSTICS"
msgstr "команда GET STACKED DIAGNOSTICS не принимает элемент %s"
-#: pl_gram.y:969
+#: pl_gram.y:974
#, c-format
msgid "diagnostics item %s is not allowed in GET CURRENT DIAGNOSTICS"
msgstr "команда GET CURRENT DIAGNOSTICS не принимает элемент %s"
-#: pl_gram.y:1067
+#: pl_gram.y:1072
msgid "unrecognized GET DIAGNOSTICS item"
msgstr "нераспознанный элемент GET DIAGNOSTICS"
-#: pl_gram.y:1078 pl_gram.y:3410
+#: pl_gram.y:1082 pl_gram.y:3448
#, c-format
msgid "\"%s\" is not a scalar variable"
msgstr "\"%s\" - не скалярная переменная"
-#: pl_gram.y:1330 pl_gram.y:1524
+#: pl_gram.y:1334 pl_gram.y:1528
#, c-format
msgid ""
"loop variable of loop over rows must be a record or row variable or list of "
@@ -607,234 +600,234 @@ msgstr ""
"переменная цикла по кортежам должна быть переменной типа запись или кортеж "
"или списком скалярных переменных"
-#: pl_gram.y:1364
+#: pl_gram.y:1368
#, c-format
msgid "cursor FOR loop must have only one target variable"
msgstr "в цикле FOR с курсором должна быть только одна переменная"
-#: pl_gram.y:1371
+#: pl_gram.y:1375
#, c-format
msgid "cursor FOR loop must use a bound cursor variable"
msgstr ""
"в цикле FOR с курсором должен использоваться курсор, привязанный к запросу"
-#: pl_gram.y:1455
+#: pl_gram.y:1459
#, c-format
msgid "integer FOR loop must have only one target variable"
msgstr "в целочисленном цикле FOR должна быть только одна переменная"
-#: pl_gram.y:1491
+#: pl_gram.y:1495
#, c-format
msgid "cannot specify REVERSE in query FOR loop"
msgstr "в цикле FOR с запросом нельзя указать REVERSE"
-#: pl_gram.y:1638
+#: pl_gram.y:1642
#, c-format
msgid "loop variable of FOREACH must be a known variable or list of variables"
msgstr ""
"переменной цикла FOREACH должна быть известная переменная или список "
"переменных"
-#: pl_gram.y:1679
+#: pl_gram.y:1683
#, c-format
msgid ""
"there is no label \"%s\" attached to any block or loop enclosing this "
"statement"
msgstr "в блоке или цикле, окружающем этот оператор, нет метки \"%s\""
-#: pl_gram.y:1687
+#: pl_gram.y:1691
#, c-format
msgid "block label \"%s\" cannot be used in CONTINUE"
msgstr "метку блока \"%s\" нельзя использовать в CONTINUE"
-#: pl_gram.y:1702
+#: pl_gram.y:1706
#, c-format
msgid "EXIT cannot be used outside a loop, unless it has a label"
msgstr "EXIT можно использовать вне цикла только с указанием метки"
-#: pl_gram.y:1703
+#: pl_gram.y:1707
#, c-format
msgid "CONTINUE cannot be used outside a loop"
msgstr "CONTINUE нельзя использовать вне цикла"
-#: pl_gram.y:1727 pl_gram.y:1764 pl_gram.y:1812 pl_gram.y:2863 pl_gram.y:2945
-#: pl_gram.y:3056 pl_gram.y:3812
+#: pl_gram.y:1731 pl_gram.y:1768 pl_gram.y:1816 pl_gram.y:2898 pl_gram.y:2983
+#: pl_gram.y:3094 pl_gram.y:3850
msgid "unexpected end of function definition"
msgstr "неожиданный конец определения функции"
-#: pl_gram.y:1832 pl_gram.y:1856 pl_gram.y:1872 pl_gram.y:1878 pl_gram.y:1992
-#: pl_gram.y:2000 pl_gram.y:2014 pl_gram.y:2109 pl_gram.y:2290 pl_gram.y:2384
-#: pl_gram.y:2535 pl_gram.y:3653 pl_gram.y:3714 pl_gram.y:3793
+#: pl_gram.y:1836 pl_gram.y:1860 pl_gram.y:1876 pl_gram.y:1882 pl_gram.y:2000
+#: pl_gram.y:2008 pl_gram.y:2022 pl_gram.y:2117 pl_gram.y:2304 pl_gram.y:2398
+#: pl_gram.y:2550 pl_gram.y:3691 pl_gram.y:3752 pl_gram.y:3831
msgid "syntax error"
msgstr "ошибка синтаксиса"
-#: pl_gram.y:1860 pl_gram.y:1862 pl_gram.y:2294 pl_gram.y:2296
+#: pl_gram.y:1864 pl_gram.y:1866 pl_gram.y:2308 pl_gram.y:2310
msgid "invalid SQLSTATE code"
msgstr "неверный код SQLSTATE"
-#: pl_gram.y:2056
+#: pl_gram.y:2064
msgid "syntax error, expected \"FOR\""
msgstr "ошибка синтаксиса, ожидался \"FOR\""
-#: pl_gram.y:2118
+#: pl_gram.y:2126
#, c-format
msgid "FETCH statement cannot return multiple rows"
msgstr "оператор FETCH не может вернуть несколько строк"
-#: pl_gram.y:2174
+#: pl_gram.y:2188
#, c-format
msgid "cursor variable must be a simple variable"
msgstr "переменная-курсор должна быть простой переменной"
-#: pl_gram.y:2180
+#: pl_gram.y:2194
#, c-format
msgid "variable \"%s\" must be of type cursor or refcursor"
msgstr "переменная \"%s\" должна быть типа cursor или refcursor"
-#: pl_gram.y:2506 pl_gram.y:2517
+#: pl_gram.y:2521 pl_gram.y:2532
#, c-format
msgid "\"%s\" is not a known variable"
msgstr "\"%s\" - не известная переменная"
-#: pl_gram.y:2621 pl_gram.y:2631 pl_gram.y:2787
+#: pl_gram.y:2636 pl_gram.y:2646 pl_gram.y:2802
msgid "mismatched parentheses"
msgstr "непарные скобки"
-#: pl_gram.y:2635
+#: pl_gram.y:2650
#, c-format
msgid "missing \"%s\" at end of SQL expression"
msgstr "отсутствует \"%s\" в конце выражения SQL"
-#: pl_gram.y:2641
+#: pl_gram.y:2656
#, c-format
msgid "missing \"%s\" at end of SQL statement"
msgstr "отсутствует \"%s\" в конце оператора SQL"
-#: pl_gram.y:2658
+#: pl_gram.y:2673
msgid "missing expression"
msgstr "отсутствует выражение"
-#: pl_gram.y:2660
+#: pl_gram.y:2675
msgid "missing SQL statement"
msgstr "отсутствует оператор SQL"
-#: pl_gram.y:2789
+#: pl_gram.y:2804
msgid "incomplete data type declaration"
msgstr "неполное определение типа данных"
-#: pl_gram.y:2812
+#: pl_gram.y:2827
msgid "missing data type declaration"
msgstr "отсутствует определение типа данных"
-#: pl_gram.y:2868
+#: pl_gram.y:2906
msgid "INTO specified more than once"
msgstr "INTO указано неоднократно"
-#: pl_gram.y:3037
+#: pl_gram.y:3075
msgid "expected FROM or IN"
msgstr "ожидалось FROM или IN"
-#: pl_gram.y:3097
+#: pl_gram.y:3135
#, c-format
msgid "RETURN cannot have a parameter in function returning set"
msgstr "в функции, возвращающей множество, RETURN должен быть без параметров"
-#: pl_gram.y:3098
+#: pl_gram.y:3136
#, c-format
msgid "Use RETURN NEXT or RETURN QUERY."
msgstr "Используйте RETURN NEXT или RETURN QUERY."
-#: pl_gram.y:3106
+#: pl_gram.y:3144
#, c-format
msgid "RETURN cannot have a parameter in function with OUT parameters"
msgstr "RETURN должен быть без параметров в функции с параметрами OUT"
-#: pl_gram.y:3115
+#: pl_gram.y:3153
#, c-format
msgid "RETURN cannot have a parameter in function returning void"
msgstr "в функции, не возвращающей ничего, RETURN не должен иметь параметров"
-#: pl_gram.y:3175
+#: pl_gram.y:3213
#, c-format
msgid "RETURN NEXT cannot have a parameter in function with OUT parameters"
msgstr "RETURN NEXT должен быть без параметров в функции с параметрами OUT"
-#: pl_gram.y:3279
+#: pl_gram.y:3317
#, c-format
msgid "\"%s\" is declared CONSTANT"
msgstr "\"%s\" объявлена как CONSTANT"
-#: pl_gram.y:3341 pl_gram.y:3353
+#: pl_gram.y:3379 pl_gram.y:3391
#, c-format
msgid "record or row variable cannot be part of multiple-item INTO list"
msgstr ""
"переменная типа запись или кортеж не может быть частью списка INTO с "
"несколькими элементами"
-#: pl_gram.y:3398
+#: pl_gram.y:3436
#, c-format
msgid "too many INTO variables specified"
msgstr "указано слишком много переменных INTO"
-#: pl_gram.y:3606
+#: pl_gram.y:3644
#, c-format
msgid "end label \"%s\" specified for unlabelled block"
msgstr "конечная метка \"%s\" указана для не помеченного блока"
-#: pl_gram.y:3613
+#: pl_gram.y:3651
#, c-format
msgid "end label \"%s\" differs from block's label \"%s\""
msgstr "конечная метка \"%s\" отличается от метки блока \"%s\""
-#: pl_gram.y:3648
+#: pl_gram.y:3686
#, c-format
msgid "cursor \"%s\" has no arguments"
msgstr "курсор \"%s\" не имеет аргументов"
-#: pl_gram.y:3662
+#: pl_gram.y:3700
#, c-format
msgid "cursor \"%s\" has arguments"
msgstr "курсор \"%s\" имеет аргументы"
-#: pl_gram.y:3704
+#: pl_gram.y:3742
#, c-format
msgid "cursor \"%s\" has no argument named \"%s\""
msgstr "курсор \"%s\" не имеет аргумента \"%s\""
-#: pl_gram.y:3724
+#: pl_gram.y:3762
#, c-format
msgid "value for parameter \"%s\" of cursor \"%s\" specified more than once"
msgstr "значение параметра \"%s\" курсора \"%s\" указано неоднократно"
-#: pl_gram.y:3749
+#: pl_gram.y:3787
#, c-format
msgid "not enough arguments for cursor \"%s\""
msgstr "недостаточно аргументов для курсора \"%s\""
-#: pl_gram.y:3756
+#: pl_gram.y:3794
#, c-format
msgid "too many arguments for cursor \"%s\""
msgstr "слишком много аргументов для курсора \"%s\""
-#: pl_gram.y:3844
+#: pl_gram.y:3882
msgid "unrecognized RAISE statement option"
msgstr "нераспознанный параметр оператора RAISE"
-#: pl_gram.y:3848
+#: pl_gram.y:3886
msgid "syntax error, expected \"=\""
msgstr "ошибка синтаксиса, ожидалось \"=\""
-#: pl_gram.y:3889
+#: pl_gram.y:3927
#, c-format
msgid "too many parameters specified for RAISE"
msgstr "слишком много параметров для RAISE"
-#: pl_gram.y:3893
+#: pl_gram.y:3931
#, c-format
msgid "too few parameters specified for RAISE"
msgstr "недостаточно параметров для RAISE"
-#: pl_handler.c:149
+#: pl_handler.c:154
msgid ""
"Sets handling of conflicts between PL/pgSQL variable names and table column "
"names."
@@ -842,7 +835,7 @@ msgstr ""
"Выбирает режим разрешения конфликтов между именами переменных PL/pgSQL и "
"именами столбцов таблиц."
-#: pl_handler.c:158
+#: pl_handler.c:163
msgid ""
"Print information about parameters in the DETAIL part of the error messages "
"generated on INTO ... STRICT failures."
@@ -850,27 +843,27 @@ msgstr ""
"Добавляет информацию о параметрах в раздел DETAIL сообщений, выводимых при "
"ошибках в INTO ... STRICT."
-#: pl_handler.c:166
+#: pl_handler.c:171
msgid "Perform checks given in ASSERT statements."
msgstr "Выполняет проверки, заданные в операторах ASSERT."
-#: pl_handler.c:174
+#: pl_handler.c:179
msgid "List of programming constructs that should produce a warning."
msgstr ""
"Список программных конструкций, которые должны выдавать предупреждения."
-#: pl_handler.c:184
+#: pl_handler.c:189
msgid "List of programming constructs that should produce an error."
msgstr "Список программных конструкций, которые должны выдавать ошибку."
#. translator: %s is typically the translation of "syntax error"
-#: pl_scanner.c:621
+#: pl_scanner.c:624
#, c-format
msgid "%s at end of input"
msgstr "%s в конце"
#. translator: first %s is typically the translation of "syntax error"
-#: pl_scanner.c:637
+#: pl_scanner.c:640
#, c-format
msgid "%s at or near \"%s\""
msgstr "%s (примерное положение: \"%s\")"
diff --git a/src/pl/plpython/Makefile b/src/pl/plpython/Makefile
index 647b4b1b96..7680d49cb6 100644
--- a/src/pl/plpython/Makefile
+++ b/src/pl/plpython/Makefile
@@ -95,7 +95,9 @@ REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
include $(top_srcdir)/src/Makefile.shlib
-all: submake-generated-headers all-lib
+all: all-lib
+
+$(OBJS): | submake-generated-headers
install: all install-lib install-data
diff --git a/src/pl/plpython/expected/plpython_composite.out b/src/pl/plpython/expected/plpython_composite.out
index 0ef0c21235..7fc6a928e7 100644
--- a/src/pl/plpython/expected/plpython_composite.out
+++ b/src/pl/plpython/expected/plpython_composite.out
@@ -465,13 +465,13 @@ SELECT * FROM changing_test();
-- tables of composite types
CREATE FUNCTION composite_types_table(OUT tab table_record[], OUT typ type_record[] ) RETURNS SETOF record AS $$
-yield {'tab': [['first', 1], ['second', 2]],
+yield {'tab': [('first', 1), ('second', 2)],
'typ': [{'first': 'third', 'second': 3},
{'first': 'fourth', 'second': 4}]}
-yield {'tab': [['first', 1], ['second', 2]],
+yield {'tab': [('first', 1), ('second', 2)],
'typ': [{'first': 'third', 'second': 3},
{'first': 'fourth', 'second': 4}]}
-yield {'tab': [['first', 1], ['second', 2]],
+yield {'tab': [('first', 1), ('second', 2)],
'typ': [{'first': 'third', 'second': 3},
{'first': 'fourth', 'second': 4}]}
$$ LANGUAGE plpythonu;
@@ -569,3 +569,26 @@ SELECT * FROM return_record_2('v3') AS (v1 int, v2 int, v3 int);
1 | 2 | 3
(1 row)
+-- multi-dimensional array of composite types.
+CREATE FUNCTION composite_type_as_list() RETURNS type_record[] AS $$
+ return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]];
+$$ LANGUAGE plpythonu;
+SELECT * FROM composite_type_as_list();
+ composite_type_as_list
+------------------------------------------------------------------------------------
+ {{"(first,1)","(second,1)"},{"(first,2)","(second,2)"},{"(first,3)","(second,3)"}}
+(1 row)
+
+-- Starting with PostgreSQL 10, a composite type in an array cannot be
+-- represented as a Python list, because it's ambiguous with multi-dimensional
+-- arrays. So this throws an error now. The error should contain a useful hint
+-- on the issue.
+CREATE FUNCTION composite_type_as_list_broken() RETURNS type_record[] AS $$
+ return [['first', 1]];
+$$ LANGUAGE plpythonu;
+SELECT * FROM composite_type_as_list_broken();
+ERROR: malformed record literal: "first"
+DETAIL: Missing left parenthesis.
+HINT: To return a composite type in an array, return the composite type as a Python tuple, e.g. "[('foo')]"
+CONTEXT: while creating return value
+PL/Python function "composite_type_as_list_broken"
diff --git a/src/pl/plpython/expected/plpython_ereport.out b/src/pl/plpython/expected/plpython_ereport.out
index 13bd0ab335..e11999ce8c 100644
--- a/src/pl/plpython/expected/plpython_ereport.out
+++ b/src/pl/plpython/expected/plpython_ereport.out
@@ -68,13 +68,13 @@ CONTEXT: Traceback (most recent call last):
plpy.info('unsupported argument', blabla='fooboo')
PL/Python anonymous code block
DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpythonu;
-ERROR: TypeError: Argument 'message' given by name and position
+ERROR: TypeError: argument 'message' given by name and position
CONTEXT: Traceback (most recent call last):
PL/Python anonymous code block, line 1, in <module>
plpy.info('first message', message='second message')
PL/Python anonymous code block
DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpythonu;
-ERROR: TypeError: Argument 'message' given by name and position
+ERROR: TypeError: argument 'message' given by name and position
CONTEXT: Traceback (most recent call last):
PL/Python anonymous code block, line 1, in <module>
plpy.info('first message', 'second message', message='third message')
@@ -94,26 +94,22 @@ kwargs = {
"column_name": _column_name, "datatype_name": _datatype_name,
"constraint_name": _constraint_name
}
-# ignore None values - should work on Python2.3
-dict = {}
-for k in kwargs:
- if kwargs[k] is not None:
- dict[k] = kwargs[k]
-plpy.error(**dict)
+# ignore None values
+plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
$$ LANGUAGE plpythonu;
SELECT raise_exception('hello', 'world');
ERROR: plpy.Error: hello
DETAIL: world
CONTEXT: Traceback (most recent call last):
- PL/Python function "raise_exception", line 13, in <module>
- plpy.error(**dict)
+ PL/Python function "raise_exception", line 9, in <module>
+ plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
PL/Python function "raise_exception"
SELECT raise_exception('message text', 'detail text', _sqlstate => 'YY333');
ERROR: plpy.Error: message text
DETAIL: detail text
CONTEXT: Traceback (most recent call last):
- PL/Python function "raise_exception", line 13, in <module>
- plpy.error(**dict)
+ PL/Python function "raise_exception", line 9, in <module>
+ plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
PL/Python function "raise_exception"
SELECT raise_exception(_message => 'message text',
_detail => 'detail text',
@@ -128,8 +124,8 @@ ERROR: plpy.Error: message text
DETAIL: detail text
HINT: hint text
CONTEXT: Traceback (most recent call last):
- PL/Python function "raise_exception", line 13, in <module>
- plpy.error(**dict)
+ PL/Python function "raise_exception", line 9, in <module>
+ plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
PL/Python function "raise_exception"
SELECT raise_exception(_message => 'message text',
_hint => 'hint text',
@@ -139,8 +135,8 @@ SELECT raise_exception(_message => 'message text',
ERROR: plpy.Error: message text
HINT: hint text
CONTEXT: Traceback (most recent call last):
- PL/Python function "raise_exception", line 13, in <module>
- plpy.error(**dict)
+ PL/Python function "raise_exception", line 9, in <module>
+ plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
PL/Python function "raise_exception"
DO $$
DECLARE
diff --git a/src/pl/plpython/expected/plpython_setof.out b/src/pl/plpython/expected/plpython_setof.out
index 308d2abb7f..170dbc394d 100644
--- a/src/pl/plpython/expected/plpython_setof.out
+++ b/src/pl/plpython/expected/plpython_setof.out
@@ -147,11 +147,8 @@ SELECT ugly(1,3), ugly(7,8);
------+------
1 | 7
2 | 8
- 3 | 7
- 1 | 8
- 2 | 7
- 3 | 8
-(6 rows)
+ 3 |
+(3 rows)
-- returns set of named-composite-type tuples
CREATE OR REPLACE FUNCTION get_user_records()
diff --git a/src/pl/plpython/expected/plpython_spi.out b/src/pl/plpython/expected/plpython_spi.out
index dbde36f841..e54dca9e2e 100644
--- a/src/pl/plpython/expected/plpython_spi.out
+++ b/src/pl/plpython/expected/plpython_spi.out
@@ -31,6 +31,19 @@ except Exception, ex:
return None
'
LANGUAGE plpythonu;
+CREATE FUNCTION spi_prepared_plan_test_two(a text) RETURNS text
+ AS
+'if "myplan" not in SD:
+ q = "SELECT count(*) FROM users WHERE lname = $1"
+ SD["myplan"] = plpy.prepare(q, [ "text" ])
+try:
+ rv = SD["myplan"].execute([a])
+ return "there are " + str(rv[0]["count"]) + " " + str(a) + "s"
+except Exception, ex:
+ plpy.error(str(ex))
+return None
+'
+ LANGUAGE plpythonu;
CREATE FUNCTION spi_prepared_plan_test_nested(a text) RETURNS text
AS
'if "myplan" not in SD:
@@ -80,8 +93,8 @@ select spi_prepared_plan_test_one('doe');
there are 3 does
(1 row)
-select spi_prepared_plan_test_one('smith');
- spi_prepared_plan_test_one
+select spi_prepared_plan_test_two('smith');
+ spi_prepared_plan_test_two
----------------------------
there are 1 smiths
(1 row)
@@ -220,8 +233,8 @@ SELECT result_len_test($$UPDATE foo3 SET b= '' WHERE a = 2$$);
CREATE FUNCTION result_subscript_test() RETURNS void
AS $$
-result = plpy.execute("SELECT 1 AS c UNION SELECT 2 "
- "UNION SELECT 3 UNION SELECT 4")
+result = plpy.execute("SELECT 1 AS c UNION ALL SELECT 2 "
+ "UNION ALL SELECT 3 UNION ALL SELECT 4")
plpy.info(result[1]['c'])
plpy.info(result[-1]['c'])
@@ -372,7 +385,7 @@ plan = plpy.prepare(
["text"])
for row in plpy.cursor(plan, ["w"]):
yield row['fname']
-for row in plpy.cursor(plan, ["j"]):
+for row in plan.cursor(["j"]):
yield row['fname']
$$ LANGUAGE plpythonu;
CREATE FUNCTION cursor_plan_wrong_args() RETURNS SETOF text AS $$
diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out
index adb82a89d6..847e4cc412 100644
--- a/src/pl/plpython/expected/plpython_test.out
+++ b/src/pl/plpython/expected/plpython_test.out
@@ -36,17 +36,34 @@ select "Argument test #1"(users, fname, lname) from users where lname = 'doe' or
(3 rows)
-- check module contents
-CREATE FUNCTION module_contents() RETURNS text AS
+CREATE FUNCTION module_contents() RETURNS SETOF text AS
$$
contents = list(filter(lambda x: not x.startswith("__"), dir(plpy)))
contents.sort()
-return ", ".join(contents)
+return contents
$$ LANGUAGE plpythonu;
select module_contents();
- module_contents
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- Error, Fatal, SPIError, cursor, debug, error, execute, fatal, info, log, notice, prepare, quote_ident, quote_literal, quote_nullable, spiexceptions, subtransaction, warning
-(1 row)
+ module_contents
+-----------------
+ Error
+ Fatal
+ SPIError
+ cursor
+ debug
+ error
+ execute
+ fatal
+ info
+ log
+ notice
+ prepare
+ quote_ident
+ quote_literal
+ quote_nullable
+ spiexceptions
+ subtransaction
+ warning
+(18 rows)
CREATE FUNCTION elog_test_basic() RETURNS void
AS $$
diff --git a/src/pl/plpython/expected/plpython_trigger.out b/src/pl/plpython/expected/plpython_trigger.out
index 4148963f3a..d7ab8ac6b8 100644
--- a/src/pl/plpython/expected/plpython_trigger.out
+++ b/src/pl/plpython/expected/plpython_trigger.out
@@ -503,3 +503,24 @@ SELECT * FROM b;
1234
(1 row)
+-- check that SQL run in trigger code can see transition tables
+CREATE TABLE transition_table_test (id int, name text);
+INSERT INTO transition_table_test VALUES (1, 'a');
+CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpythonu AS
+$$
+ rv = plpy.execute("SELECT * FROM old_table")
+ assert(rv.nrows() == 1)
+ plpy.info("old: " + str(rv[0]["id"]) + " -> " + rv[0]["name"])
+ rv = plpy.execute("SELECT * FROM new_table")
+ assert(rv.nrows() == 1)
+ plpy.info("new: " + str(rv[0]["id"]) + " -> " + rv[0]["name"])
+ return None
+$$;
+CREATE TRIGGER a_t AFTER UPDATE ON transition_table_test
+ REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
+ FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_test_f();
+UPDATE transition_table_test SET name = 'b';
+INFO: old: 1 -> a
+INFO: new: 1 -> b
+DROP TABLE transition_table_test;
+DROP FUNCTION transition_table_test_f();
diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out
index f0b6abd274..10f7125d84 100644
--- a/src/pl/plpython/expected/plpython_types.out
+++ b/src/pl/plpython/expected/plpython_types.out
@@ -537,9 +537,111 @@ INFO: (None, <type 'NoneType'>)
(1 row)
SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
-ERROR: cannot convert multidimensional array to Python list
-DETAIL: PL/Python only supports one-dimensional arrays.
-CONTEXT: PL/Python function "test_type_conversion_array_int4"
+INFO: ([[1, 2, 3], [4, 5, 6]], <type 'list'>)
+ test_type_conversion_array_int4
+---------------------------------
+ {{1,2,3},{4,5,6}}
+(1 row)
+
+SELECT * FROM test_type_conversion_array_int4(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]);
+INFO: ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <type 'list'>)
+ test_type_conversion_array_int4
+---------------------------------------------------
+ {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
+(1 row)
+
+SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
+INFO: ([1, 2, 3], <type 'list'>)
+ test_type_conversion_array_int4
+---------------------------------
+ {1,2,3}
+(1 row)
+
+CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
+INFO: ([[[1L, 2L, None], [None, 5L, 6L]], [[None, 8L, 9L], [10L, 11L, 12L]]], <type 'list'>)
+ test_type_conversion_array_int8
+---------------------------------------------------
+ {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
+(1 row)
+
+CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
+ [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
+INFO: ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']], [[None, '11-21-2016', '10-21-2016'], ['09-21-2015', '09-22-2015', '09-21-2014']]], <type 'list'>)
+ test_type_conversion_array_date
+---------------------------------------------------------------------------------------------------------------------------------
+ {{{09-21-2016,09-22-2016,NULL},{NULL,10-21-2016,10-22-2016}},{{NULL,11-21-2016,10-21-2016},{09-21-2015,09-22-2015,09-21-2014}}}
+(1 row)
+
+CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
+ [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
+ [[NULL,'2016-01-21 11:34:24.078792-04','2016-11-21 11:34:24.108792-04'],
+ ['2015-09-21 11:34:24.079792-04','2014-09-21 11:34:24.078792-04','2013-09-21 11:34:24.078792-04']]]::timestamp[]);
+INFO: ([[['Wed Sep 21 15:34:24.078792 2016', 'Sat Oct 22 11:34:24.078795 2016', None], [None, 'Fri Oct 21 11:34:25.078792 2016', 'Fri Oct 21 11:34:24.098792 2016']], [[None, 'Thu Jan 21 11:34:24.078792 2016', 'Mon Nov 21 11:34:24.108792 2016'], ['Mon Sep 21 11:34:24.079792 2015', 'Sun Sep 21 11:34:24.078792 2014', 'Sat Sep 21 11:34:24.078792 2013']]], <type 'list'>)
+ test_type_conversion_array_timestamp
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {{{"Wed Sep 21 15:34:24.078792 2016","Sat Oct 22 11:34:24.078795 2016",NULL},{NULL,"Fri Oct 21 11:34:25.078792 2016","Fri Oct 21 11:34:24.098792 2016"}},{{NULL,"Thu Jan 21 11:34:24.078792 2016","Mon Nov 21 11:34:24.108792 2016"},{"Mon Sep 21 11:34:24.079792 2015","Sun Sep 21 11:34:24.078792 2014","Sat Sep 21 11:34:24.078792 2013"}}}
+(1 row)
+
+CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 ) RETURNS int4[] AS $BODY$
+m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
+plpy.info(m, type(m))
+return m
+$BODY$ LANGUAGE plpythonu;
+select pyreturnmultidemint4(8,5,3,2);
+INFO: ([[[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]], [[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]]], <type 'list'>)
+ pyreturnmultidemint4
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}},{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}}}
+(1 row)
+
+CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 ) RETURNS int8[] AS $BODY$
+m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
+plpy.info(m, type(m))
+return m
+$BODY$ LANGUAGE plpythonu;
+select pyreturnmultidemint8(5,5,3,2);
+INFO: ([[[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], [[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]], <type 'list'>)
+ pyreturnmultidemint8
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}},{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}}}
+(1 row)
+
+CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4 ) RETURNS float4[] AS $BODY$
+m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
+plpy.info(m, type(m))
+return m
+$BODY$ LANGUAGE plpythonu;
+select pyreturnmultidemfloat4(6,5,3,2);
+INFO: ([[[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]], [[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]]], <type 'list'>)
+ pyreturnmultidemfloat4
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}},{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}}}
+(1 row)
+
+CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4 ) RETURNS float8[] AS $BODY$
+m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
+plpy.info(m, type(m))
+return m
+$BODY$ LANGUAGE plpythonu;
+select pyreturnmultidemfloat8(7,5,3,2);
+INFO: ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]], [[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]]], <type 'list'>)
+ pyreturnmultidemfloat8
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}},{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}}}
+(1 row)
+
CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
plpy.info(x, type(x))
return x
@@ -551,6 +653,13 @@ INFO: (['foo', 'bar'], <type 'list'>)
{foo,bar}
(1 row)
+SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
+INFO: ([['foo', 'bar'], ['foo2', 'bar2']], <type 'list'>)
+ test_type_conversion_array_text
+---------------------------------
+ {{foo,bar},{foo2,bar2}}
+(1 row)
+
CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
plpy.info(x, type(x))
return x
@@ -578,6 +687,20 @@ SELECT * FROM test_type_conversion_array_mixed2();
ERROR: invalid input syntax for integer: "abc"
CONTEXT: while creating return value
PL/Python function "test_type_conversion_array_mixed2"
+CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
+return [[1,2,3],[4,5]]
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_mdarray_malformed();
+ERROR: multidimensional arrays must have array expressions with matching dimensions. PL/Python function return value has sequence length 2 while expected 3
+CONTEXT: while creating return value
+PL/Python function "test_type_conversion_mdarray_malformed"
+CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
+return [[[[[[[1]]]]]]]
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_conversion_mdarray_toodeep();
+ERROR: number of array dimensions exceeds the maximum allowed (6)
+CONTEXT: while creating return value
+PL/Python function "test_type_conversion_mdarray_toodeep"
CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
$$ LANGUAGE plpythonu;
diff --git a/src/pl/plpython/expected/plpython_types_3.out b/src/pl/plpython/expected/plpython_types_3.out
index 56b78e1a5f..f7f0d31fef 100644
--- a/src/pl/plpython/expected/plpython_types_3.out
+++ b/src/pl/plpython/expected/plpython_types_3.out
@@ -537,9 +537,111 @@ INFO: (None, <class 'NoneType'>)
(1 row)
SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
-ERROR: cannot convert multidimensional array to Python list
-DETAIL: PL/Python only supports one-dimensional arrays.
-CONTEXT: PL/Python function "test_type_conversion_array_int4"
+INFO: ([[1, 2, 3], [4, 5, 6]], <class 'list'>)
+ test_type_conversion_array_int4
+---------------------------------
+ {{1,2,3},{4,5,6}}
+(1 row)
+
+SELECT * FROM test_type_conversion_array_int4(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]);
+INFO: ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
+ test_type_conversion_array_int4
+---------------------------------------------------
+ {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
+(1 row)
+
+SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
+INFO: ([1, 2, 3], <class 'list'>)
+ test_type_conversion_array_int4
+---------------------------------
+ {1,2,3}
+(1 row)
+
+CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpython3u;
+SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
+INFO: ([[[1, 2, None], [None, 5, 6]], [[None, 8, 9], [10, 11, 12]]], <class 'list'>)
+ test_type_conversion_array_int8
+---------------------------------------------------
+ {{{1,2,NULL},{NULL,5,6}},{{NULL,8,9},{10,11,12}}}
+(1 row)
+
+CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpython3u;
+SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
+ [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
+INFO: ([[['09-21-2016', '09-22-2016', None], [None, '10-21-2016', '10-22-2016']], [[None, '11-21-2016', '10-21-2016'], ['09-21-2015', '09-22-2015', '09-21-2014']]], <class 'list'>)
+ test_type_conversion_array_date
+---------------------------------------------------------------------------------------------------------------------------------
+ {{{09-21-2016,09-22-2016,NULL},{NULL,10-21-2016,10-22-2016}},{{NULL,11-21-2016,10-21-2016},{09-21-2015,09-22-2015,09-21-2014}}}
+(1 row)
+
+CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpython3u;
+SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
+ [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
+ [[NULL,'2016-01-21 11:34:24.078792-04','2016-11-21 11:34:24.108792-04'],
+ ['2015-09-21 11:34:24.079792-04','2014-09-21 11:34:24.078792-04','2013-09-21 11:34:24.078792-04']]]::timestamp[]);
+INFO: ([[['Wed Sep 21 15:34:24.078792 2016', 'Sat Oct 22 11:34:24.078795 2016', None], [None, 'Fri Oct 21 11:34:25.078792 2016', 'Fri Oct 21 11:34:24.098792 2016']], [[None, 'Thu Jan 21 11:34:24.078792 2016', 'Mon Nov 21 11:34:24.108792 2016'], ['Mon Sep 21 11:34:24.079792 2015', 'Sun Sep 21 11:34:24.078792 2014', 'Sat Sep 21 11:34:24.078792 2013']]], <class 'list'>)
+ test_type_conversion_array_timestamp
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {{{"Wed Sep 21 15:34:24.078792 2016","Sat Oct 22 11:34:24.078795 2016",NULL},{NULL,"Fri Oct 21 11:34:25.078792 2016","Fri Oct 21 11:34:24.098792 2016"}},{{NULL,"Thu Jan 21 11:34:24.078792 2016","Mon Nov 21 11:34:24.108792 2016"},{"Mon Sep 21 11:34:24.079792 2015","Sun Sep 21 11:34:24.078792 2014","Sat Sep 21 11:34:24.078792 2013"}}}
+(1 row)
+
+CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 ) RETURNS int4[] AS $BODY$
+m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
+plpy.info(m, type(m))
+return m
+$BODY$ LANGUAGE plpython3u;
+select pyreturnmultidemint4(8,5,3,2);
+INFO: ([[[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]], [[[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]], [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]]], <class 'list'>)
+ pyreturnmultidemint4
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}},{{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}},{{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7},{0,1,2,3,4,5,6,7}}}}
+(1 row)
+
+CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 ) RETURNS int8[] AS $BODY$
+m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
+plpy.info(m, type(m))
+return m
+$BODY$ LANGUAGE plpython3u;
+select pyreturnmultidemint8(5,5,3,2);
+INFO: ([[[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], [[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]], <class 'list'>)
+ pyreturnmultidemint8
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}},{{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}},{{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4},{0,1,2,3,4}}}}
+(1 row)
+
+CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4 ) RETURNS float4[] AS $BODY$
+m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
+plpy.info(m, type(m))
+return m
+$BODY$ LANGUAGE plpython3u;
+select pyreturnmultidemfloat4(6,5,3,2);
+INFO: ([[[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]], [[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]], [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]]], <class 'list'>)
+ pyreturnmultidemfloat4
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}},{{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}},{{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5},{0,1,2,3,4,5}}}}
+(1 row)
+
+CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4 ) RETURNS float8[] AS $BODY$
+m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
+plpy.info(m, type(m))
+return m
+$BODY$ LANGUAGE plpython3u;
+select pyreturnmultidemfloat8(7,5,3,2);
+INFO: ([[[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]], [[[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6]]]], <class 'list'>)
+ pyreturnmultidemfloat8
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ {{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}},{{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}},{{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6},{0,1,2,3,4,5,6}}}}
+(1 row)
+
CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
plpy.info(x, type(x))
return x
@@ -551,6 +653,13 @@ INFO: (['foo', 'bar'], <class 'list'>)
{foo,bar}
(1 row)
+SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
+INFO: ([['foo', 'bar'], ['foo2', 'bar2']], <class 'list'>)
+ test_type_conversion_array_text
+---------------------------------
+ {{foo,bar},{foo2,bar2}}
+(1 row)
+
CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
plpy.info(x, type(x))
return x
@@ -578,6 +687,20 @@ SELECT * FROM test_type_conversion_array_mixed2();
ERROR: invalid input syntax for integer: "abc"
CONTEXT: while creating return value
PL/Python function "test_type_conversion_array_mixed2"
+CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
+return [[1,2,3],[4,5]]
+$$ LANGUAGE plpython3u;
+SELECT * FROM test_type_conversion_mdarray_malformed();
+ERROR: multidimensional arrays must have array expressions with matching dimensions. PL/Python function return value has sequence length 2 while expected 3
+CONTEXT: while creating return value
+PL/Python function "test_type_conversion_mdarray_malformed"
+CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
+return [[[[[[[1]]]]]]]
+$$ LANGUAGE plpython3u;
+SELECT * FROM test_type_conversion_mdarray_toodeep();
+ERROR: number of array dimensions exceeds the maximum allowed (6)
+CONTEXT: while creating return value
+PL/Python function "test_type_conversion_mdarray_toodeep"
CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
$$ LANGUAGE plpython3u;
diff --git a/src/pl/plpython/generate-spiexceptions.pl b/src/pl/plpython/generate-spiexceptions.pl
index f424160c47..a9ee9601b3 100644
--- a/src/pl/plpython/generate-spiexceptions.pl
+++ b/src/pl/plpython/generate-spiexceptions.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
#
# Generate the spiexceptions.h header from errcodes.txt
-# Copyright (c) 2000-2016, PostgreSQL Global Development Group
+# Copyright (c) 2000-2017, PostgreSQL Global Development Group
use warnings;
use strict;
@@ -10,7 +10,7 @@ print
"/* autogenerated from src/backend/utils/errcodes.txt, do not edit */\n";
print "/* there is deliberately not an #ifndef SPIEXCEPTIONS_H here */\n";
-open my $errcodes, $ARGV[0] or die;
+open my $errcodes, '<', $ARGV[0] or die;
while (<$errcodes>)
{
diff --git a/src/pl/plpython/plpy_cursorobject.c b/src/pl/plpython/plpy_cursorobject.c
index 44ba76e765..18e689f141 100644
--- a/src/pl/plpython/plpy_cursorobject.c
+++ b/src/pl/plpython/plpy_cursorobject.c
@@ -25,7 +25,6 @@
static PyObject *PLy_cursor_query(const char *query);
-static PyObject *PLy_cursor_plan(PyObject *ob, PyObject *args);
static void PLy_cursor_dealloc(PyObject *arg);
static PyObject *PLy_cursor_iternext(PyObject *self);
static PyObject *PLy_cursor_fetch(PyObject *self, PyObject *args);
@@ -116,9 +115,7 @@ PLy_cursor_query(const char *query)
cursor->closed = false;
cursor->mcxt = AllocSetContextCreate(TopMemoryContext,
"PL/Python cursor context",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
+ ALLOCSET_DEFAULT_SIZES);
PLy_typeinfo_init(&cursor->result, cursor->mcxt);
oldcontext = CurrentMemoryContext;
@@ -162,7 +159,7 @@ PLy_cursor_query(const char *query)
return (PyObject *) cursor;
}
-static PyObject *
+PyObject *
PLy_cursor_plan(PyObject *ob, PyObject *args)
{
PLyCursorObject *cursor;
@@ -210,9 +207,7 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
cursor->closed = false;
cursor->mcxt = AllocSetContextCreate(TopMemoryContext,
"PL/Python cursor context",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
+ ALLOCSET_DEFAULT_SIZES);
PLy_typeinfo_init(&cursor->result, cursor->mcxt);
oldcontext = CurrentMemoryContext;
@@ -244,7 +239,8 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
plan->values[j] =
plan->args[j].out.d.func(&(plan->args[j].out.d),
-1,
- elem);
+ elem,
+ false);
}
PG_CATCH();
{
@@ -409,7 +405,7 @@ PLy_cursor_fetch(PyObject *self, PyObject *args)
volatile ResourceOwner oldowner;
Portal portal;
- if (!PyArg_ParseTuple(args, "i", &count))
+ if (!PyArg_ParseTuple(args, "i:fetch", &count))
return NULL;
cursor = (PLyCursorObject *) self;
diff --git a/src/pl/plpython/plpy_cursorobject.h b/src/pl/plpython/plpy_cursorobject.h
index c73033c486..ef23865dd2 100644
--- a/src/pl/plpython/plpy_cursorobject.h
+++ b/src/pl/plpython/plpy_cursorobject.h
@@ -19,5 +19,6 @@ typedef struct PLyCursorObject
extern void PLy_cursor_init_type(void);
extern PyObject *PLy_cursor(PyObject *self, PyObject *args);
+extern PyObject *PLy_cursor_plan(PyObject *ob, PyObject *args);
#endif /* PLPY_CURSOROBJECT_H */
diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
index d61493fac8..c4806274bc 100644
--- a/src/pl/plpython/plpy_elog.c
+++ b/src/pl/plpython/plpy_elog.c
@@ -303,7 +303,7 @@ PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
long plain_lineno;
/*
- * The second frame points at the internal function, but to mimick
+ * The second frame points at the internal function, but to mimic
* Python error reporting we want to say <module>.
*/
if (*tb_depth == 1)
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index 25e4744c7d..aa4d68664f 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -245,7 +245,7 @@ PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc)
desc = lookup_rowtype_tupdesc(proc->result.out.d.typoid,
proc->result.out.d.typmod);
- rv = PLyObject_ToCompositeDatum(&proc->result, desc, plrv);
+ rv = PLyObject_ToCompositeDatum(&proc->result, desc, plrv, false);
fcinfo->isnull = (rv == (Datum) NULL);
ReleaseTupleDesc(desc);
@@ -253,7 +253,7 @@ PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc)
else
{
fcinfo->isnull = false;
- rv = (proc->result.out.d.func) (&proc->result.out.d, -1, plrv);
+ rv = (proc->result.out.d.func) (&proc->result.out.d, -1, plrv, false);
}
}
PG_CATCH();
@@ -345,6 +345,11 @@ PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
PG_TRY();
{
+ int rc PG_USED_FOR_ASSERTS_ONLY;
+
+ rc = SPI_register_trigger_data(tdata);
+ Assert(rc >= 0);
+
plargs = PLy_trigger_build_args(fcinfo, proc, &rv);
plrv = PLy_procedure_call(proc, "TD", plargs);
@@ -896,18 +901,13 @@ static HeapTuple
PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
HeapTuple otup)
{
+ HeapTuple rtup;
PyObject *volatile plntup;
PyObject *volatile plkeys;
PyObject *volatile plval;
- HeapTuple rtup;
- int natts,
- i,
- attn,
- atti;
- int *volatile modattrs;
Datum *volatile modvalues;
- char *volatile modnulls;
- TupleDesc tupdesc;
+ bool *volatile modnulls;
+ bool *volatile modrepls;
ErrorContextCallback plerrcontext;
plerrcontext.callback = plpython_trigger_error_callback;
@@ -915,12 +915,16 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
error_context_stack = &plerrcontext;
plntup = plkeys = plval = NULL;
- modattrs = NULL;
modvalues = NULL;
modnulls = NULL;
+ modrepls = NULL;
PG_TRY();
{
+ TupleDesc tupdesc;
+ int nkeys,
+ i;
+
if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -932,18 +936,20 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
errmsg("TD[\"new\"] is not a dictionary")));
plkeys = PyDict_Keys(plntup);
- natts = PyList_Size(plkeys);
-
- modattrs = (int *) palloc(natts * sizeof(int));
- modvalues = (Datum *) palloc(natts * sizeof(Datum));
- modnulls = (char *) palloc(natts * sizeof(char));
+ nkeys = PyList_Size(plkeys);
tupdesc = tdata->tg_relation->rd_att;
- for (i = 0; i < natts; i++)
+ modvalues = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
+ modnulls = (bool *) palloc0(tupdesc->natts * sizeof(bool));
+ modrepls = (bool *) palloc0(tupdesc->natts * sizeof(bool));
+
+ for (i = 0; i < nkeys; i++)
{
PyObject *platt;
char *plattstr;
+ int attn;
+ PLyObToDatum *att;
platt = PyList_GetItem(plkeys, i);
if (PyString_Check(platt))
@@ -963,7 +969,12 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row",
plattstr)));
- atti = attn - 1;
+ if (attn <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot set system attribute \"%s\"",
+ plattstr)));
+ att = &proc->result.out.r.atts[attn - 1];
plval = PyDict_GetItem(plntup, platt);
if (plval == NULL)
@@ -971,40 +982,31 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
Py_INCREF(plval);
- modattrs[i] = attn;
-
- if (tupdesc->attrs[atti]->attisdropped)
+ if (plval != Py_None)
{
- modvalues[i] = (Datum) 0;
- modnulls[i] = 'n';
- }
- else if (plval != Py_None)
- {
- PLyObToDatum *att = &proc->result.out.r.atts[atti];
-
- modvalues[i] = (att->func) (att,
- tupdesc->attrs[atti]->atttypmod,
- plval);
- modnulls[i] = ' ';
+ modvalues[attn - 1] =
+ (att->func) (att,
+ tupdesc->attrs[attn - 1]->atttypmod,
+ plval,
+ false);
+ modnulls[attn - 1] = false;
}
else
{
- modvalues[i] =
- InputFunctionCall(&proc->result.out.r.atts[atti].typfunc,
+ modvalues[attn - 1] =
+ InputFunctionCall(&att->typfunc,
NULL,
- proc->result.out.r.atts[atti].typioparam,
- tupdesc->attrs[atti]->atttypmod);
- modnulls[i] = 'n';
+ att->typioparam,
+ tupdesc->attrs[attn - 1]->atttypmod);
+ modnulls[attn - 1] = true;
}
+ modrepls[attn - 1] = true;
Py_DECREF(plval);
plval = NULL;
}
- rtup = SPI_modifytuple(tdata->tg_relation, otup, natts,
- modattrs, modvalues, modnulls);
- if (rtup == NULL)
- elog(ERROR, "SPI_modifytuple failed: error %d", SPI_result);
+ rtup = heap_modify_tuple(otup, tupdesc, modvalues, modnulls, modrepls);
}
PG_CATCH();
{
@@ -1012,12 +1014,12 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
Py_XDECREF(plkeys);
Py_XDECREF(plval);
- if (modnulls)
- pfree(modnulls);
if (modvalues)
pfree(modvalues);
- if (modattrs)
- pfree(modattrs);
+ if (modnulls)
+ pfree(modnulls);
+ if (modrepls)
+ pfree(modrepls);
PG_RE_THROW();
}
@@ -1026,9 +1028,9 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
Py_DECREF(plntup);
Py_DECREF(plkeys);
- pfree(modattrs);
pfree(modvalues);
pfree(modnulls);
+ pfree(modrepls);
error_context_stack = plerrcontext.previous;
@@ -1106,8 +1108,6 @@ PLy_abort_open_subtransactions(int save_subxact_level)
RollbackAndReleaseCurrentSubTransaction();
- SPI_restore_connection();
-
subtransactiondata = (PLySubtransactionData *) linitial(explicit_subtransactions);
explicit_subtransactions = list_delete_first(explicit_subtransactions);
diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c
index f95039406a..860b804e54 100644
--- a/src/pl/plpython/plpy_main.c
+++ b/src/pl/plpython/plpy_main.c
@@ -315,9 +315,7 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
MemSet(&proc, 0, sizeof(PLyProcedure));
proc.mcxt = AllocSetContextCreate(TopMemoryContext,
"__plpython_inline_block",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
+ ALLOCSET_DEFAULT_SIZES);
proc.pyname = MemoryContextStrdup(proc.mcxt, "__plpython_inline_block");
proc.langid = codeblock->langOid;
proc.result.out.d.typoid = VOIDOID;
@@ -416,9 +414,7 @@ PLy_get_scratch_context(PLyExecutionContext *context)
context->scratch_ctx =
AllocSetContextCreate(TopTransactionContext,
"PL/Python scratch context",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
+ ALLOCSET_DEFAULT_SIZES);
return context->scratch_ctx;
}
diff --git a/src/pl/plpython/plpy_planobject.c b/src/pl/plpython/plpy_planobject.c
index a9040efb50..390b4e90d4 100644
--- a/src/pl/plpython/plpy_planobject.c
+++ b/src/pl/plpython/plpy_planobject.c
@@ -10,11 +10,15 @@
#include "plpy_planobject.h"
+#include "plpy_cursorobject.h"
#include "plpy_elog.h"
+#include "plpy_spi.h"
#include "utils/memutils.h"
static void PLy_plan_dealloc(PyObject *arg);
+static PyObject *PLy_plan_cursor(PyObject *self, PyObject *args);
+static PyObject *PLy_plan_execute(PyObject *self, PyObject *args);
static PyObject *PLy_plan_status(PyObject *self, PyObject *args);
static char PLy_plan_doc[] = {
@@ -22,6 +26,8 @@ static char PLy_plan_doc[] = {
};
static PyMethodDef PLy_plan_methods[] = {
+ {"cursor", PLy_plan_cursor, METH_VARARGS, NULL},
+ {"execute", PLy_plan_execute, METH_VARARGS, NULL},
{"status", PLy_plan_status, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL}
};
@@ -112,14 +118,38 @@ PLy_plan_dealloc(PyObject *arg)
static PyObject *
+PLy_plan_cursor(PyObject *self, PyObject *args)
+{
+ PyObject *planargs = NULL;
+
+ if (!PyArg_ParseTuple(args, "|O", &planargs))
+ return NULL;
+
+ return PLy_cursor_plan(self, planargs);
+}
+
+
+static PyObject *
+PLy_plan_execute(PyObject *self, PyObject *args)
+{
+ PyObject *list = NULL;
+ long limit = 0;
+
+ if (!PyArg_ParseTuple(args, "|Ol", &list, &limit))
+ return NULL;
+
+ return PLy_spi_execute_plan(self, list, limit);
+}
+
+
+static PyObject *
PLy_plan_status(PyObject *self, PyObject *args)
{
- if (PyArg_ParseTuple(args, ""))
+ if (PyArg_ParseTuple(args, ":status"))
{
Py_INCREF(Py_True);
return Py_True;
/* return PyInt_FromLong(self->status); */
}
- PLy_exception_set(PLy_exc_error, "plan.status takes no arguments");
return NULL;
}
diff --git a/src/pl/plpython/plpy_plpymodule.c b/src/pl/plpython/plpy_plpymodule.c
index f520e7725f..ad160aeec3 100644
--- a/src/pl/plpython/plpy_plpymodule.c
+++ b/src/pl/plpython/plpy_plpymodule.c
@@ -25,6 +25,9 @@ HTAB *PLy_spi_exceptions = NULL;
static void PLy_add_exceptions(PyObject *plpy);
+static PyObject *PLy_create_exception(char *name,
+ PyObject *base, PyObject *dict,
+ const char *modname, PyObject *mod);
static void PLy_generate_spi_exceptions(PyObject *mod, PyObject *base);
/* module functions */
@@ -192,47 +195,63 @@ PLy_add_exceptions(PyObject *plpy)
#else
excmod = PyModule_Create(&PLy_exc_module);
#endif
- if (PyModule_AddObject(plpy, "spiexceptions", excmod) < 0)
- PLy_elog(ERROR, "could not add the spiexceptions module");
+ if (excmod == NULL)
+ PLy_elog(ERROR, "could not create the spiexceptions module");
/*
- * XXX it appears that in some circumstances the reference count of the
- * spiexceptions module drops to zero causing a Python assert failure when
- * the garbage collector visits the module. This has been observed on the
- * buildfarm. To fix this, add an additional ref for the module here.
- *
- * This shouldn't cause a memory leak - we don't want this garbage
- * collected, and this function shouldn't be called more than once per
- * backend.
+ * PyModule_AddObject does not add a refcount to the object, for some odd
+ * reason; we must do that.
*/
Py_INCREF(excmod);
+ if (PyModule_AddObject(plpy, "spiexceptions", excmod) < 0)
+ PLy_elog(ERROR, "could not add the spiexceptions module");
- PLy_exc_error = PyErr_NewException("plpy.Error", NULL, NULL);
- PLy_exc_fatal = PyErr_NewException("plpy.Fatal", NULL, NULL);
- PLy_exc_spi_error = PyErr_NewException("plpy.SPIError", NULL, NULL);
-
- if (PLy_exc_error == NULL ||
- PLy_exc_fatal == NULL ||
- PLy_exc_spi_error == NULL)
- PLy_elog(ERROR, "could not create the base SPI exceptions");
-
- Py_INCREF(PLy_exc_error);
- PyModule_AddObject(plpy, "Error", PLy_exc_error);
- Py_INCREF(PLy_exc_fatal);
- PyModule_AddObject(plpy, "Fatal", PLy_exc_fatal);
- Py_INCREF(PLy_exc_spi_error);
- PyModule_AddObject(plpy, "SPIError", PLy_exc_spi_error);
+ PLy_exc_error = PLy_create_exception("plpy.Error", NULL, NULL,
+ "Error", plpy);
+ PLy_exc_fatal = PLy_create_exception("plpy.Fatal", NULL, NULL,
+ "Fatal", plpy);
+ PLy_exc_spi_error = PLy_create_exception("plpy.SPIError", NULL, NULL,
+ "SPIError", plpy);
memset(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(int);
hash_ctl.entrysize = sizeof(PLyExceptionEntry);
- PLy_spi_exceptions = hash_create("SPI exceptions", 256,
+ PLy_spi_exceptions = hash_create("PL/Python SPI exceptions", 256,
&hash_ctl, HASH_ELEM | HASH_BLOBS);
PLy_generate_spi_exceptions(excmod, PLy_exc_spi_error);
}
/*
+ * Create an exception object and add it to the module
+ */
+static PyObject *
+PLy_create_exception(char *name, PyObject *base, PyObject *dict,
+ const char *modname, PyObject *mod)
+{
+ PyObject *exc;
+
+ exc = PyErr_NewException(name, base, dict);
+ if (exc == NULL)
+ PLy_elog(ERROR, "could not create exception \"%s\"", name);
+
+ /*
+ * PyModule_AddObject does not add a refcount to the object, for some odd
+ * reason; we must do that.
+ */
+ Py_INCREF(exc);
+ PyModule_AddObject(mod, modname, exc);
+
+ /*
+ * The caller will also store a pointer to the exception object in some
+ * permanent variable, so add another ref to account for that. This is
+ * probably excessively paranoid, but let's be sure.
+ */
+ Py_INCREF(exc);
+ return exc;
+}
+
+/*
* Add all the autogenerated exceptions as subclasses of SPIError
*/
static void
@@ -257,12 +276,14 @@ PLy_generate_spi_exceptions(PyObject *mod, PyObject *base)
PyDict_SetItemString(dict, "sqlstate", sqlstate);
Py_DECREF(sqlstate);
- exc = PyErr_NewException(exception_map[i].name, base, dict);
- PyModule_AddObject(mod, exception_map[i].classname, exc);
+
+ exc = PLy_create_exception(exception_map[i].name, base, dict,
+ exception_map[i].classname, mod);
+
entry = hash_search(PLy_spi_exceptions, &exception_map[i].sqlstate,
HASH_ENTER, &found);
- entry->exc = exc;
Assert(!found);
+ entry->exc = exc;
}
}
@@ -323,7 +344,7 @@ PLy_quote_literal(PyObject *self, PyObject *args)
char *quoted;
PyObject *ret;
- if (!PyArg_ParseTuple(args, "s", &str))
+ if (!PyArg_ParseTuple(args, "s:quote_literal", &str))
return NULL;
quoted = quote_literal_cstr(str);
@@ -340,7 +361,7 @@ PLy_quote_nullable(PyObject *self, PyObject *args)
char *quoted;
PyObject *ret;
- if (!PyArg_ParseTuple(args, "z", &str))
+ if (!PyArg_ParseTuple(args, "z:quote_nullable", &str))
return NULL;
if (str == NULL)
@@ -360,7 +381,7 @@ PLy_quote_ident(PyObject *self, PyObject *args)
const char *quoted;
PyObject *ret;
- if (!PyArg_ParseTuple(args, "s", &str))
+ if (!PyArg_ParseTuple(args, "s:quote_ident", &str))
return NULL;
quoted = quote_identifier(str);
@@ -442,10 +463,10 @@ PLy_output(volatile int level, PyObject *self, PyObject *args, PyObject *kw)
if (strcmp(keyword, "message") == 0)
{
- /* the message should not be overwriten */
+ /* the message should not be overwritten */
if (PyTuple_Size(args) != 0)
{
- PLy_exception_set(PyExc_TypeError, "Argument 'message' given by name and position");
+ PLy_exception_set(PyExc_TypeError, "argument 'message' given by name and position");
return NULL;
}
diff --git a/src/pl/plpython/plpy_procedure.c b/src/pl/plpython/plpy_procedure.c
index 70b75f5d95..e86117c837 100644
--- a/src/pl/plpython/plpy_procedure.c
+++ b/src/pl/plpython/plpy_procedure.c
@@ -122,7 +122,7 @@ PLy_procedure_get(Oid fn_oid, Oid fn_rel, bool is_trigger)
}
PG_CATCH();
{
- /* Do not leave an uninitialised entry in the cache */
+ /* Do not leave an uninitialized entry in the cache */
if (use_cache)
hash_search(PLy_procedure_cache, &key, HASH_REMOVE, NULL);
PG_RE_THROW();
@@ -167,9 +167,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
cxt = AllocSetContextCreate(TopMemoryContext,
procName,
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
+ ALLOCSET_DEFAULT_SIZES);
oldcxt = MemoryContextSwitchTo(cxt);
diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c
index 1e965cf85f..c6856ccbac 100644
--- a/src/pl/plpython/plpy_spi.c
+++ b/src/pl/plpython/plpy_spi.c
@@ -30,7 +30,6 @@
static PyObject *PLy_spi_execute_query(char *query, long limit);
-static PyObject *PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit);
static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *tuptable,
uint64 rows, int status);
static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);
@@ -51,7 +50,7 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
volatile ResourceOwner oldowner;
volatile int nargs;
- if (!PyArg_ParseTuple(args, "s|O", &query, &list))
+ if (!PyArg_ParseTuple(args, "s|O:prepare", &query, &list))
return NULL;
if (list && (!PySequence_Check(list)))
@@ -66,9 +65,7 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
plan->mcxt = AllocSetContextCreate(TopMemoryContext,
"PL/Python plan context",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
+ ALLOCSET_DEFAULT_SIZES);
oldcontext = MemoryContextSwitchTo(plan->mcxt);
nargs = list ? PySequence_Length(list) : 0;
@@ -195,7 +192,7 @@ PLy_spi_execute(PyObject *self, PyObject *args)
return NULL;
}
-static PyObject *
+PyObject *
PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
{
volatile int nargs;
@@ -266,7 +263,8 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
plan->values[j] =
plan->args[j].out.d.func(&(plan->args[j].out.d),
-1,
- elem);
+ elem,
+ false);
}
PG_CATCH();
{
@@ -413,9 +411,7 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 rows, int status)
cxt = AllocSetContextCreate(CurrentMemoryContext,
"PL/Python temp context",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
+ ALLOCSET_DEFAULT_SIZES);
PLy_typeinfo_init(&args, cxt);
oldcontext = CurrentMemoryContext;
@@ -519,12 +515,6 @@ PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner)
ReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
-
- /*
- * AtEOSubXact_SPI() should not have popped any SPI context, but just in
- * case it did, make sure we remain connected.
- */
- SPI_restore_connection();
}
void
@@ -544,13 +534,6 @@ PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
- /*
- * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
- * have left us in a disconnected state. We need this hack to return to
- * connected state.
- */
- SPI_restore_connection();
-
/* Look up the correct exception */
entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
HASH_FIND, NULL);
diff --git a/src/pl/plpython/plpy_spi.h b/src/pl/plpython/plpy_spi.h
index b0427947ef..817a7584e7 100644
--- a/src/pl/plpython/plpy_spi.h
+++ b/src/pl/plpython/plpy_spi.h
@@ -10,6 +10,7 @@
extern PyObject *PLy_spi_prepare(PyObject *self, PyObject *args);
extern PyObject *PLy_spi_execute(PyObject *self, PyObject *args);
+extern PyObject *PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit);
typedef struct PLyExceptionEntry
{
diff --git a/src/pl/plpython/plpy_subxactobject.c b/src/pl/plpython/plpy_subxactobject.c
index 81fb3a3a4a..9f1caa87d9 100644
--- a/src/pl/plpython/plpy_subxactobject.c
+++ b/src/pl/plpython/plpy_subxactobject.c
@@ -7,7 +7,6 @@
#include "postgres.h"
#include "access/xact.h"
-#include "executor/spi.h"
#include "utils/memutils.h"
#include "plpython.h"
@@ -213,12 +212,6 @@ PLy_subtransaction_exit(PyObject *self, PyObject *args)
CurrentResourceOwner = subxactdata->oldowner;
pfree(subxactdata);
- /*
- * AtEOSubXact_SPI() should not have popped any SPI context, but just in
- * case it did, make sure we remain connected.
- */
- SPI_restore_connection();
-
Py_INCREF(Py_None);
return Py_None;
}
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index 7ad7a4400a..0e04753fa1 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -14,6 +14,7 @@
#include "parser/parse_type.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/numeric.h"
@@ -45,20 +46,25 @@ static PyObject *PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d);
static PyObject *PLyString_FromDatum(PLyDatumToOb *arg, Datum d);
static PyObject *PLyObject_FromTransform(PLyDatumToOb *arg, Datum d);
static PyObject *PLyList_FromArray(PLyDatumToOb *arg, Datum d);
+static PyObject *PLyList_FromArray_recurse(PLyDatumToOb *elm, int *dims, int ndim, int dim,
+ char **dataptr_p, bits8 **bitmap_p, int *bitmask_p);
/* conversion from Python objects to Datums */
-static Datum PLyObject_ToBool(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
-static Datum PLyObject_ToBytea(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
-static Datum PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
-static Datum PLyObject_ToDatum(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
-static Datum PLyObject_ToTransform(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
-static Datum PLySequence_ToArray(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
+static Datum PLyObject_ToBool(PLyObToDatum *arg, int32 typmod, PyObject *plrv, bool inarray);
+static Datum PLyObject_ToBytea(PLyObToDatum *arg, int32 typmod, PyObject *plrv, bool inarray);
+static Datum PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv, bool inarray);
+static Datum PLyObject_ToDatum(PLyObToDatum *arg, int32 typmod, PyObject *plrv, bool inarray);
+static Datum PLyObject_ToTransform(PLyObToDatum *arg, int32 typmod, PyObject *plrv, bool inarray);
+static Datum PLySequence_ToArray(PLyObToDatum *arg, int32 typmod, PyObject *plrv, bool inarray);
+static void PLySequence_ToArray_recurse(PLyObToDatum *elm, PyObject *list,
+ int *dims, int ndim, int dim,
+ Datum *elems, bool *nulls, int *currelem);
/* conversion from Python objects to composite Datums (used by triggers and SRFs) */
-static Datum PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string);
+static Datum PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string, bool inarray);
static Datum PLyMapping_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping);
static Datum PLySequence_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence);
-static Datum PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object);
+static Datum PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object, bool inarray);
void
PLy_typeinfo_init(PLyTypeInfo *arg, MemoryContext mcxt)
@@ -336,12 +342,12 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc)
* as an object that has __getattr__ support.
*/
Datum
-PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv)
+PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv, bool inarray)
{
Datum datum;
if (PyString_Check(plrv) || PyUnicode_Check(plrv))
- datum = PLyString_ToComposite(info, desc, plrv);
+ datum = PLyString_ToComposite(info, desc, plrv, inarray);
else if (PySequence_Check(plrv))
/* composite type as sequence (tuple, list etc) */
datum = PLySequence_ToComposite(info, desc, plrv);
@@ -350,7 +356,7 @@ PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv)
datum = PLyMapping_ToComposite(info, desc, plrv);
else
/* returned as smth, must provide method __getattr__(name) */
- datum = PLyGenericObject_ToComposite(info, desc, plrv);
+ datum = PLyGenericObject_ToComposite(info, desc, plrv, inarray);
return datum;
}
@@ -515,15 +521,9 @@ PLy_input_datum_func2(PLyDatumToOb *arg, MemoryContext arg_mcxt, Oid typeOid, He
static PyObject *
PLyBool_FromBool(PLyDatumToOb *arg, Datum d)
{
- /*
- * We would like to use Py_RETURN_TRUE and Py_RETURN_FALSE here for
- * generating SQL from trigger functions, but those are only supported in
- * Python >= 2.4, and we support older versions.
- * http://docs.python.org/api/boolObjects.html
- */
if (DatumGetBool(d))
- return PyBool_FromLong(1);
- return PyBool_FromLong(0);
+ Py_RETURN_TRUE;
+ Py_RETURN_FALSE;
}
static PyObject *
@@ -603,9 +603,9 @@ PLyLong_FromOid(PLyDatumToOb *arg, Datum d)
static PyObject *
PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d)
{
- text *txt = DatumGetByteaP(d);
- char *str = VARDATA(txt);
- size_t size = VARSIZE(txt) - VARHDRSZ;
+ text *txt = DatumGetByteaPP(d);
+ char *str = VARDATA_ANY(txt);
+ size_t size = VARSIZE_ANY_EXHDR(txt);
return PyBytes_FromStringAndSize(str, size);
}
@@ -631,43 +631,105 @@ PLyList_FromArray(PLyDatumToOb *arg, Datum d)
{
ArrayType *array = DatumGetArrayTypeP(d);
PLyDatumToOb *elm = arg->elm;
- PyObject *list;
- int length;
- int lbound;
- int i;
+ int ndim;
+ int *dims;
+ char *dataptr;
+ bits8 *bitmap;
+ int bitmask;
if (ARR_NDIM(array) == 0)
return PyList_New(0);
- if (ARR_NDIM(array) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot convert multidimensional array to Python list"),
- errdetail("PL/Python only supports one-dimensional arrays.")));
+ /* Array dimensions and left bounds */
+ ndim = ARR_NDIM(array);
+ dims = ARR_DIMS(array);
+ Assert(ndim < MAXDIM);
+
+ /*
+ * We iterate the SQL array in the physical order it's stored in the
+ * datum. For example, for a 3-dimensional array the order of iteration
+ * would be the following: [0,0,0] elements through [0,0,k], then [0,1,0]
+ * through [0,1,k] till [0,m,k], then [1,0,0] through [1,0,k] till
+ * [1,m,k], and so on.
+ *
+ * In Python, there are no multi-dimensional lists as such, but they are
+ * represented as a list of lists. So a 3-d array of [n,m,k] elements is a
+ * list of n m-element arrays, each element of which is k-element array.
+ * PLyList_FromArray_recurse() builds the Python list for a single
+ * dimension, and recurses for the next inner dimension.
+ */
+ dataptr = ARR_DATA_PTR(array);
+ bitmap = ARR_NULLBITMAP(array);
+ bitmask = 1;
- length = ARR_DIMS(array)[0];
- lbound = ARR_LBOUND(array)[0];
- list = PyList_New(length);
- if (list == NULL)
- PLy_elog(ERROR, "could not create new Python list");
+ return PLyList_FromArray_recurse(elm, dims, ndim, 0,
+ &dataptr, &bitmap, &bitmask);
+}
- for (i = 0; i < length; i++)
+static PyObject *
+PLyList_FromArray_recurse(PLyDatumToOb *elm, int *dims, int ndim, int dim,
+ char **dataptr_p, bits8 **bitmap_p, int *bitmask_p)
+{
+ int i;
+ PyObject *list;
+
+ list = PyList_New(dims[dim]);
+
+ if (dim < ndim - 1)
{
- Datum elem;
- bool isnull;
- int offset;
-
- offset = lbound + i;
- elem = array_ref(array, 1, &offset, arg->typlen,
- elm->typlen, elm->typbyval, elm->typalign,
- &isnull);
- if (isnull)
+ /* Outer dimension. Recurse for each inner slice. */
+ for (i = 0; i < dims[dim]; i++)
{
- Py_INCREF(Py_None);
- PyList_SET_ITEM(list, i, Py_None);
+ PyObject *sublist;
+
+ sublist = PLyList_FromArray_recurse(elm, dims, ndim, dim + 1,
+ dataptr_p, bitmap_p, bitmask_p);
+ PyList_SET_ITEM(list, i, sublist);
}
- else
- PyList_SET_ITEM(list, i, elm->func(elm, elem));
+ }
+ else
+ {
+ /*
+ * Innermost dimension. Fill the list with the values from the array
+ * for this slice.
+ */
+ char *dataptr = *dataptr_p;
+ bits8 *bitmap = *bitmap_p;
+ int bitmask = *bitmask_p;
+
+ for (i = 0; i < dims[dim]; i++)
+ {
+ /* checking for NULL */
+ if (bitmap && (*bitmap & bitmask) == 0)
+ {
+ Py_INCREF(Py_None);
+ PyList_SET_ITEM(list, i, Py_None);
+ }
+ else
+ {
+ Datum itemvalue;
+
+ itemvalue = fetch_att(dataptr, elm->typbyval, elm->typlen);
+ PyList_SET_ITEM(list, i, elm->func(elm, itemvalue));
+ dataptr = att_addlength_pointer(dataptr, elm->typlen, dataptr);
+ dataptr = (char *) att_align_nominal(dataptr, elm->typalign);
+ }
+
+ /* advance bitmap pointer if any */
+ if (bitmap)
+ {
+ bitmask <<= 1;
+ if (bitmask == 0x100 /* (1<<8) */ )
+ {
+ bitmap++;
+ bitmask = 1;
+ }
+ }
+ }
+
+ *dataptr_p = dataptr;
+ *bitmap_p = bitmap;
+ *bitmask_p = bitmask;
}
return list;
@@ -680,7 +742,7 @@ PLyList_FromArray(PLyDatumToOb *arg, Datum d)
* type can parse.
*/
static Datum
-PLyObject_ToBool(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
+PLyObject_ToBool(PLyObToDatum *arg, int32 typmod, PyObject *plrv, bool inarray)
{
Datum rv;
@@ -699,7 +761,7 @@ PLyObject_ToBool(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
* with embedded nulls. And it's faster this way.
*/
static Datum
-PLyObject_ToBytea(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
+PLyObject_ToBytea(PLyObToDatum *arg, int32 typmod, PyObject *plrv, bool inarray)
{
PyObject *volatile plrv_so = NULL;
Datum rv;
@@ -743,7 +805,7 @@ PLyObject_ToBytea(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
* for obtaining PostgreSQL tuples.
*/
static Datum
-PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
+PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv, bool inarray)
{
Datum rv;
PLyTypeInfo info;
@@ -756,9 +818,7 @@ PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
/* Create a dummy PLyTypeInfo */
cxt = AllocSetContextCreate(CurrentMemoryContext,
"PL/Python temp context",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
+ ALLOCSET_DEFAULT_SIZES);
MemSet(&info, 0, sizeof(PLyTypeInfo));
PLy_typeinfo_init(&info, cxt);
/* Mark it as needing output routines lookup */
@@ -768,11 +828,11 @@ PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
/*
* This will set up the dummy PLyTypeInfo's output conversion routines,
- * since we left is_rowtype as 2. A future optimisation could be caching
+ * since we left is_rowtype as 2. A future optimization could be caching
* that info instead of looking it up every time a tuple is returned from
* the function.
*/
- rv = PLyObject_ToCompositeDatum(&info, desc, plrv);
+ rv = PLyObject_ToCompositeDatum(&info, desc, plrv, inarray);
ReleaseTupleDesc(desc);
@@ -844,61 +904,166 @@ PLyObject_AsString(PyObject *plrv)
* cstring into PostgreSQL type.
*/
static Datum
-PLyObject_ToDatum(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
+PLyObject_ToDatum(PLyObToDatum *arg, int32 typmod, PyObject *plrv, bool inarray)
{
+ char *str;
+
Assert(plrv != Py_None);
+ str = PLyObject_AsString(plrv);
+
+ /*
+ * If we are parsing a composite type within an array, and the string
+ * isn't a valid record literal, there's a high chance that the function
+ * did something like:
+ *
+ * CREATE FUNCTION .. RETURNS comptype[] AS $$ return [['foo', 'bar']] $$
+ * LANGUAGE plpython;
+ *
+ * Before PostgreSQL 10, that was interpreted as a single-dimensional
+ * array, containing record ('foo', 'bar'). PostgreSQL 10 added support
+ * for multi-dimensional arrays, and it is now interpreted as a
+ * two-dimensional array, containing two records, 'foo', and 'bar'.
+ * record_in() will throw an error, because "foo" is not a valid record
+ * literal.
+ *
+ * To make that less confusing to users who are upgrading from older
+ * versions, try to give a hint in the typical instances of that. If we
+ * are parsing an array of composite types, and we see a string literal
+ * that is not a valid record literal, give a hint. We only want to give
+ * the hint in the narrow case of a malformed string literal, not any
+ * error from record_in(), so check for that case here specifically.
+ *
+ * This check better match the one in record_in(), so that we don't forbid
+ * literals that are actually valid!
+ */
+ if (inarray && arg->typfunc.fn_oid == F_RECORD_IN)
+ {
+ char *ptr = str;
+
+ /* Allow leading whitespace */
+ while (*ptr && isspace((unsigned char) *ptr))
+ ptr++;
+ if (*ptr++ != '(')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed record literal: \"%s\"", str),
+ errdetail("Missing left parenthesis."),
+ errhint("To return a composite type in an array, return the composite type as a Python tuple, e.g. \"[('foo')]\"")));
+ }
+
return InputFunctionCall(&arg->typfunc,
- PLyObject_AsString(plrv),
+ str,
arg->typioparam,
typmod);
}
static Datum
-PLyObject_ToTransform(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
+PLyObject_ToTransform(PLyObToDatum *arg, int32 typmod, PyObject *plrv, bool inarray)
{
return FunctionCall1(&arg->typtransform, PointerGetDatum(plrv));
}
static Datum
-PLySequence_ToArray(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
+PLySequence_ToArray(PLyObToDatum *arg, int32 typmod, PyObject *plrv, bool inarray)
{
ArrayType *array;
- Datum rv;
int i;
Datum *elems;
bool *nulls;
- int len;
- int lbs;
+ int64 len;
+ int ndim;
+ int dims[MAXDIM];
+ int lbs[MAXDIM];
+ int currelem;
+ Datum rv;
+ PyObject *pyptr = plrv;
+ PyObject *next;
Assert(plrv != Py_None);
- if (!PySequence_Check(plrv))
- PLy_elog(ERROR, "return value of function with array return type is not a Python sequence");
+ /*
+ * Determine the number of dimensions, and their sizes.
+ */
+ ndim = 0;
+ len = 1;
- len = PySequence_Length(plrv);
- elems = palloc(sizeof(*elems) * len);
- nulls = palloc(sizeof(*nulls) * len);
+ Py_INCREF(plrv);
- for (i = 0; i < len; i++)
+ for (;;)
{
- PyObject *obj = PySequence_GetItem(plrv, i);
+ if (!PyList_Check(pyptr))
+ break;
- if (obj == Py_None)
- nulls[i] = true;
- else
+ if (ndim == MAXDIM)
+ PLy_elog(ERROR, "number of array dimensions exceeds the maximum allowed (%d)", MAXDIM);
+
+ dims[ndim] = PySequence_Length(pyptr);
+ if (dims[ndim] < 0)
+ PLy_elog(ERROR, "cannot determine sequence length for function return value");
+
+ if (dims[ndim] > MaxAllocSize)
+ PLy_elog(ERROR, "array size exceeds the maximum allowed");
+
+ len *= dims[ndim];
+ if (len > MaxAllocSize)
+ PLy_elog(ERROR, "array size exceeds the maximum allowed");
+
+ if (dims[ndim] == 0)
{
- nulls[i] = false;
- elems[i] = arg->elm->func(arg->elm, -1, obj);
+ /* empty sequence */
+ break;
}
- Py_XDECREF(obj);
+
+ ndim++;
+
+ next = PySequence_GetItem(pyptr, 0);
+ Py_XDECREF(pyptr);
+ pyptr = next;
+ }
+ Py_XDECREF(pyptr);
+
+ /*
+ * Check for zero dimensions. This happens if the object is a tuple or a
+ * string, rather than a list, or is not a sequence at all. We don't map
+ * tuples or strings to arrays in general, but in the first level, be
+ * lenient, for historical reasons. So if the object is a sequence of any
+ * kind, treat it as a one-dimensional array.
+ */
+ if (ndim == 0)
+ {
+ if (!PySequence_Check(plrv))
+ PLy_elog(ERROR, "return value of function with array return type is not a Python sequence");
+
+ ndim = 1;
+ len = dims[0] = PySequence_Length(plrv);
}
- lbs = 1;
- array = construct_md_array(elems, nulls, 1, &len, &lbs,
- get_base_element_type(arg->typoid), arg->elm->typlen, arg->elm->typbyval, arg->elm->typalign);
+ /*
+ * Traverse the Python lists, in depth-first order, and collect all the
+ * elements at the bottom level into 'elems'/'nulls' arrays.
+ */
+ elems = palloc(sizeof(Datum) * len);
+ nulls = palloc(sizeof(bool) * len);
+ currelem = 0;
+ PLySequence_ToArray_recurse(arg->elm, plrv,
+ dims, ndim, 0,
+ elems, nulls, &currelem);
+
+ for (i = 0; i < ndim; i++)
+ lbs[i] = 1;
+
+ array = construct_md_array(elems,
+ nulls,
+ ndim,
+ dims,
+ lbs,
+ get_base_element_type(arg->typoid),
+ arg->elm->typlen,
+ arg->elm->typbyval,
+ arg->elm->typalign);
/*
* If the result type is a domain of array, the resulting array must be
@@ -910,9 +1075,59 @@ PLySequence_ToArray(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
return rv;
}
+/*
+ * Helper function for PLySequence_ToArray. Traverse a Python list of lists in
+ * depth-first order, storing the elements in 'elems'.
+ */
+static void
+PLySequence_ToArray_recurse(PLyObToDatum *elm, PyObject *list,
+ int *dims, int ndim, int dim,
+ Datum *elems, bool *nulls, int *currelem)
+{
+ int i;
+
+ if (PySequence_Length(list) != dims[dim])
+ PLy_elog(ERROR,
+ "multidimensional arrays must have array expressions with matching dimensions. "
+ "PL/Python function return value has sequence length %d while expected %d",
+ (int) PySequence_Length(list), dims[dim]);
+
+ if (dim < ndim - 1)
+ {
+ for (i = 0; i < dims[dim]; i++)
+ {
+ PyObject *sublist = PySequence_GetItem(list, i);
+
+ PLySequence_ToArray_recurse(elm, sublist, dims, ndim, dim + 1,
+ elems, nulls, currelem);
+ Py_XDECREF(sublist);
+ }
+ }
+ else
+ {
+ for (i = 0; i < dims[dim]; i++)
+ {
+ PyObject *obj = PySequence_GetItem(list, i);
+
+ if (obj == Py_None)
+ {
+ nulls[*currelem] = true;
+ elems[*currelem] = (Datum) 0;
+ }
+ else
+ {
+ nulls[*currelem] = false;
+ elems[*currelem] = elm->func(elm, -1, obj, true);
+ }
+ Py_XDECREF(obj);
+ (*currelem)++;
+ }
+ }
+}
+
static Datum
-PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string)
+PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string, bool inarray)
{
Datum result;
HeapTuple typeTup;
@@ -923,9 +1138,7 @@ PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string)
/* Create a dummy PLyTypeInfo */
cxt = AllocSetContextCreate(CurrentMemoryContext,
"PL/Python temp context",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
+ ALLOCSET_DEFAULT_SIZES);
MemSet(&locinfo, 0, sizeof(PLyTypeInfo));
PLy_typeinfo_init(&locinfo, cxt);
@@ -939,7 +1152,7 @@ PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string)
ReleaseSysCache(typeTup);
- result = PLyObject_ToDatum(&locinfo.out.d, desc->tdtypmod, string);
+ result = PLyObject_ToDatum(&locinfo.out.d, desc->tdtypmod, string, inarray);
MemoryContextDelete(cxt);
@@ -991,7 +1204,7 @@ PLyMapping_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping)
}
else if (value)
{
- values[i] = (att->func) (att, -1, value);
+ values[i] = (att->func) (att, -1, value, false);
nulls[i] = false;
}
else
@@ -1084,7 +1297,7 @@ PLySequence_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence)
}
else if (value)
{
- values[i] = (att->func) (att, -1, value);
+ values[i] = (att->func) (att, -1, value, false);
nulls[i] = false;
}
@@ -1113,7 +1326,7 @@ PLySequence_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence)
static Datum
-PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object)
+PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object, bool inarray)
{
Datum result;
HeapTuple tuple;
@@ -1154,16 +1367,29 @@ PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object
}
else if (value)
{
- values[i] = (att->func) (att, -1, value);
+ values[i] = (att->func) (att, -1, value, false);
nulls[i] = false;
}
else
+ {
+ /*
+ * No attribute for this column in the object.
+ *
+ * If we are parsing a composite type in an array, a likely
+ * cause is that the function contained something like "[[123,
+ * 'foo']]". Before PostgreSQL 10, that was interpreted as an
+ * array, with a composite type (123, 'foo') in it. But now
+ * it's interpreted as a two-dimensional array, and we try to
+ * interpret "123" as the composite type. See also similar
+ * heuristic in PLyObject_ToDatum().
+ */
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("attribute \"%s\" does not exist in Python object", key),
- errhint("To return null in a column, "
- "let the returned object have an attribute named "
- "after column with value None.")));
+ inarray ?
+ errhint("To return a composite type in an array, return the composite type as a Python tuple, e.g. \"[('foo')]\"") :
+ errhint("To return null in a column, let the returned object have an attribute named after column with value None.")));
+ }
Py_XDECREF(value);
value = NULL;
diff --git a/src/pl/plpython/plpy_typeio.h b/src/pl/plpython/plpy_typeio.h
index 29fff61dc5..e04722c47a 100644
--- a/src/pl/plpython/plpy_typeio.h
+++ b/src/pl/plpython/plpy_typeio.h
@@ -10,8 +10,11 @@
#include "fmgr.h"
#include "storage/itemptr.h"
+/*
+ * Conversion from PostgreSQL Datum to a Python object.
+ */
struct PLyDatumToOb;
-typedef PyObject *(*PLyDatumToObFunc) (struct PLyDatumToOb *, Datum);
+typedef PyObject *(*PLyDatumToObFunc) (struct PLyDatumToOb *arg, Datum val);
typedef struct PLyDatumToOb
{
@@ -39,11 +42,15 @@ typedef union PLyTypeInput
PLyTupleToOb r;
} PLyTypeInput;
-/* convert PyObject to a Postgresql Datum or tuple.
- * output from Python
+/*
+ * Conversion from Python object to a PostgreSQL Datum.
+ *
+ * The 'inarray' argument to the conversion function is true, if the
+ * converted value was in an array (Python list). It is used to give a
+ * better error message in some cases.
*/
struct PLyObToDatum;
-typedef Datum (*PLyObToDatumFunc) (struct PLyObToDatum *, int32, PyObject *);
+typedef Datum (*PLyObToDatumFunc) (struct PLyObToDatum *arg, int32 typmod, PyObject *val, bool inarray);
typedef struct PLyObToDatum
{
@@ -71,7 +78,7 @@ typedef union PLyTypeOutput
PLyObToTuple r;
} PLyTypeOutput;
-/* all we need to move Postgresql data to Python objects,
+/* all we need to move PostgreSQL data to Python objects,
* and vice versa
*/
typedef struct PLyTypeInfo
@@ -104,7 +111,7 @@ extern void PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc);
extern void PLy_output_record_funcs(PLyTypeInfo *arg, TupleDesc desc);
/* conversion from Python objects to composite Datums */
-extern Datum PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv);
+extern Datum PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv, bool isarray);
/* conversion from heap tuples to Python dictionaries */
extern PyObject *PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc);
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index a5d0988f68..d687860ab2 100644
--- a/src/pl/plpython/plpython.h
+++ b/src/pl/plpython/plpython.h
@@ -2,7 +2,7 @@
*
* plpython.h - Python as a procedural language for PostgreSQL
*
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/pl/plpython/plpython.h
diff --git a/src/pl/plpython/po/de.po b/src/pl/plpython/po/de.po
index 131f6c4744..bdd75d07e4 100644
--- a/src/pl/plpython/po/de.po
+++ b/src/pl/plpython/po/de.po
@@ -1,16 +1,16 @@
# German message translation file for plpython
-# Copyright (C) 2009 - 2016 PostgreSQL Global Development Group
+# Copyright (C) 2009 - 2017 PostgreSQL Global Development Group
# This file is distributed under the same license as the PostgreSQL package.
-# Peter Eisentraut <peter_e@gmx.net>, 2009 - 2016.
+# Peter Eisentraut <peter_e@gmx.net>, 2009 - 2017.
#
# Use these quotes: »%s«
#
msgid ""
msgstr ""
-"Project-Id-Version: PostgreSQL 9.5\n"
+"Project-Id-Version: PostgreSQL 10\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2016-07-04 20:37+0000\n"
-"PO-Revision-Date: 2016-07-04 19:11-0400\n"
+"POT-Creation-Date: 2017-03-13 22:07+0000\n"
+"PO-Revision-Date: 2017-03-13 23:08-0400\n"
"Last-Translator: Peter Eisentraut <peter_e@gmx.net>\n"
"Language-Team: German <peter_e@gmx.net>\n"
"Language: de\n"
@@ -24,49 +24,49 @@ msgstr ""
msgid "plpy.cursor expected a query or a plan"
msgstr "plpy.cursor hat eine Anfrage oder einen Plan erwartet"
-#: plpy_cursorobject.c:179
+#: plpy_cursorobject.c:177
#, c-format
msgid "plpy.cursor takes a sequence as its second argument"
msgstr "plpy.cursor nimmt eine Sequenz als zweites Argument"
-#: plpy_cursorobject.c:195 plpy_spi.c:229
+#: plpy_cursorobject.c:193 plpy_spi.c:227
#, c-format
msgid "could not execute plan"
msgstr "konnte Plan nicht ausführen"
-#: plpy_cursorobject.c:198 plpy_spi.c:232
+#: plpy_cursorobject.c:196 plpy_spi.c:230
#, c-format
msgid "Expected sequence of %d argument, got %d: %s"
msgid_plural "Expected sequence of %d arguments, got %d: %s"
msgstr[0] "Sequenz aus %d Argument erwartet, aber %d erhalten: %s"
msgstr[1] "Sequenz aus %d Argumenten erwartet, aber %d erhalten: %s"
-#: plpy_cursorobject.c:354
+#: plpy_cursorobject.c:351
#, c-format
msgid "iterating a closed cursor"
msgstr "Iteration mit einem geschlossenen Cursor"
-#: plpy_cursorobject.c:362 plpy_cursorobject.c:427
+#: plpy_cursorobject.c:359 plpy_cursorobject.c:424
#, c-format
msgid "iterating a cursor in an aborted subtransaction"
msgstr "Iteration mit einem Cursor in einer abgebrochenen Transaktionen"
-#: plpy_cursorobject.c:419
+#: plpy_cursorobject.c:416
#, c-format
msgid "fetch from a closed cursor"
msgstr "Lesen aus einem geschlossenen Cursor"
-#: plpy_cursorobject.c:467 plpy_spi.c:438
+#: plpy_cursorobject.c:464 plpy_spi.c:435
#, c-format
msgid "query result has too many rows to fit in a Python list"
msgstr "Anfrageergebnis hat zu viele Zeilen, um in eine Python-Liste zu passen"
-#: plpy_cursorobject.c:508
+#: plpy_cursorobject.c:505
#, c-format
msgid "closing a cursor in an aborted subtransaction"
msgstr "Schließen eines Cursors in einer abgebrochenen Subtransaktion"
-#: plpy_elog.c:127 plpy_elog.c:128 plpy_plpymodule.c:527
+#: plpy_elog.c:127 plpy_elog.c:128 plpy_plpymodule.c:548
#, c-format
msgid "%s"
msgstr "%s"
@@ -146,32 +146,37 @@ msgstr "beim Erzeugen des Rückgabewerts"
msgid "could not create new dictionary while building trigger arguments"
msgstr "konnte neues Dictionary nicht erzeugen, beim Aufbauen der Triggerargumente"
-#: plpy_exec.c:927
+#: plpy_exec.c:926
#, c-format
msgid "TD[\"new\"] deleted, cannot modify row"
msgstr "TD[\"new\"] wurde gelöscht, kann Zeile nicht ändern"
-#: plpy_exec.c:932
+#: plpy_exec.c:931
#, c-format
msgid "TD[\"new\"] is not a dictionary"
msgstr "TD[\"new\"] ist kein Dictionary"
-#: plpy_exec.c:957
+#: plpy_exec.c:958
#, c-format
msgid "TD[\"new\"] dictionary key at ordinal position %d is not a string"
msgstr "Dictionary-Schlüssel auf Position %d in TD[\"new\"] ist keine Zeichenkette"
-#: plpy_exec.c:964
+#: plpy_exec.c:965
#, c-format
msgid "key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row"
msgstr "in TD[\"new\"] gefundener Schlüssel »%s« existiert nicht als Spalte in der den Trigger auslösenden Zeile"
-#: plpy_exec.c:1044
+#: plpy_exec.c:970
+#, c-format
+msgid "cannot set system attribute \"%s\""
+msgstr "Systemattribut »%s« kann nicht gesetzt werden"
+
+#: plpy_exec.c:1041
#, c-format
msgid "while modifying trigger row"
msgstr "beim Ändern der Triggerzeile"
-#: plpy_exec.c:1105
+#: plpy_exec.c:1102
#, c-format
msgid "forcibly aborting a subtransaction that has not been exited"
msgstr "Abbruch einer Subtransaktion, die nicht beendet wurde, wird erzwungen"
@@ -206,86 +211,86 @@ msgstr "konnte globale Objekte nicht erzeugen"
msgid "could not initialize globals"
msgstr "konnte globale Objekte nicht initialisieren"
-#: plpy_main.c:389
+#: plpy_main.c:387
#, c-format
msgid "PL/Python function \"%s\""
msgstr "PL/Python-Funktion »%s«"
-#: plpy_main.c:396
+#: plpy_main.c:394
#, c-format
msgid "PL/Python anonymous code block"
msgstr "anonymer PL/Python-Codeblock"
-#: plpy_planobject.c:123
-#, c-format
-msgid "plan.status takes no arguments"
-msgstr "plan.status nimmt keine Argumente"
-
-#: plpy_plpymodule.c:178 plpy_plpymodule.c:181
+#: plpy_plpymodule.c:181 plpy_plpymodule.c:184
#, c-format
msgid "could not import \"plpy\" module"
msgstr "konnte Modul »plpy« nicht importieren"
-#: plpy_plpymodule.c:196
+#: plpy_plpymodule.c:199
+#, c-format
+msgid "could not create the spiexceptions module"
+msgstr "konnte das Modul »spiexceptions« nicht erzeugen"
+
+#: plpy_plpymodule.c:207
#, c-format
msgid "could not add the spiexceptions module"
msgstr "konnte das Modul »spiexceptions« nicht hinzufügen"
-#: plpy_plpymodule.c:217
+#: plpy_plpymodule.c:236
#, c-format
-msgid "could not create the base SPI exceptions"
-msgstr "konnte die SPI-Basisausnahmen nicht erzeugen"
+msgid "could not create exception \"%s\""
+msgstr "konnte Ausnahme »%s« nicht erzeugen"
-#: plpy_plpymodule.c:252 plpy_plpymodule.c:256
+#: plpy_plpymodule.c:271 plpy_plpymodule.c:275
#, c-format
msgid "could not generate SPI exceptions"
msgstr "konnte SPI-Ausnahmen nicht erzeugen"
-#: plpy_plpymodule.c:422
+#: plpy_plpymodule.c:443
#, c-format
msgid "could not unpack arguments in plpy.elog"
msgstr "konnte Argumente in plpy.elog nicht entpacken"
-#: plpy_plpymodule.c:431
+#: plpy_plpymodule.c:452
msgid "could not parse error message in plpy.elog"
msgstr "konnte Fehlermeldung in plpy.elog nicht parsen"
-#: plpy_plpymodule.c:448
+#: plpy_plpymodule.c:469
#, c-format
msgid "Argument 'message' given by name and position"
msgstr "Argument »message« wurde durch Namen und Position angegeben"
-#: plpy_plpymodule.c:475
+#: plpy_plpymodule.c:496
#, c-format
msgid "'%s' is an invalid keyword argument for this function"
msgstr "»%s« ist ein ungültiges Schlüsselwortargument für diese Funktion"
-#: plpy_plpymodule.c:486 plpy_plpymodule.c:492
+#: plpy_plpymodule.c:507 plpy_plpymodule.c:513
#, c-format
msgid "invalid SQLSTATE code"
msgstr "ungültiger SQLSTATE-Code"
-#: plpy_procedure.c:232
+#: plpy_procedure.c:230
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "Triggerfunktionen können nur als Trigger aufgerufen werden"
-#: plpy_procedure.c:237
+#: plpy_procedure.c:235
#, c-format
msgid "PL/Python functions cannot return type %s"
msgstr "PL/Python-Funktionen können keinen Rückgabetyp %s haben"
-#: plpy_procedure.c:318
+#: plpy_procedure.c:316
#, c-format
msgid "PL/Python functions cannot accept type %s"
msgstr "PL/Python-Funktionen können Typ %s nicht annehmen"
-#: plpy_procedure.c:414
+#: plpy_procedure.c:412
#, c-format
msgid "could not compile PL/Python function \"%s\""
msgstr "konnte PL/Python-Funktion »%s« nicht kompilieren"
-#: plpy_procedure.c:417
+#: plpy_procedure.c:415
#, c-format
msgid "could not compile anonymous PL/Python code block"
msgstr "konnte anonymen PL/Python-Codeblock nicht kompilieren"
@@ -300,52 +305,52 @@ msgstr "Befehl hat keine Ergebnismenge erzeugt"
msgid "second argument of plpy.prepare must be a sequence"
msgstr "zweites Argument von plpy.prepare muss eine Sequenz sein"
-#: plpy_spi.c:118
+#: plpy_spi.c:116
#, c-format
msgid "plpy.prepare: type name at ordinal position %d is not a string"
msgstr "plpy.prepare: Typname auf Position %d ist keine Zeichenkette"
-#: plpy_spi.c:194
+#: plpy_spi.c:192
#, c-format
msgid "plpy.execute expected a query or a plan"
msgstr "plpy.execute hat eine Anfrage oder einen Plan erwartet"
-#: plpy_spi.c:213
+#: plpy_spi.c:211
#, c-format
msgid "plpy.execute takes a sequence as its second argument"
msgstr "plpy.execute nimmt eine Sequenz als zweites Argument"
-#: plpy_spi.c:337
+#: plpy_spi.c:336
#, c-format
msgid "SPI_execute_plan failed: %s"
msgstr "SPI_execute_plan fehlgeschlagen: %s"
-#: plpy_spi.c:379
+#: plpy_spi.c:378
#, c-format
msgid "SPI_execute failed: %s"
msgstr "SPI_execute fehlgeschlagen: %s"
-#: plpy_subxactobject.c:123
+#: plpy_subxactobject.c:122
#, c-format
msgid "this subtransaction has already been entered"
msgstr "diese Subtransaktion wurde bereits begonnen"
-#: plpy_subxactobject.c:129 plpy_subxactobject.c:187
+#: plpy_subxactobject.c:128 plpy_subxactobject.c:186
#, c-format
msgid "this subtransaction has already been exited"
msgstr "diese Subtransaktion wurde bereits beendet"
-#: plpy_subxactobject.c:181
+#: plpy_subxactobject.c:180
#, c-format
msgid "this subtransaction has not been entered"
msgstr "diese Subtransaktion wurde nicht begonnen"
-#: plpy_subxactobject.c:193
+#: plpy_subxactobject.c:192
#, c-format
msgid "there is no subtransaction to exit from"
msgstr "es gibt keine Subtransaktion zu beenden"
-#: plpy_typeio.c:286
+#: plpy_typeio.c:292
#, c-format
msgid "could not create new dictionary"
msgstr "konnte neues Dictionary nicht erzeugen"
@@ -365,62 +370,85 @@ msgstr "kein Attribut »Decimal« im Modul"
msgid "conversion from numeric to Decimal failed"
msgstr "Umwandlung von numeric in Decimal fehlgeschlagen"
-#: plpy_typeio.c:645
-#, c-format
-msgid "cannot convert multidimensional array to Python list"
-msgstr "kann mehrdimensionales Array nicht in Python-Liste umwandeln"
-
-#: plpy_typeio.c:646
-#, c-format
-msgid "PL/Python only supports one-dimensional arrays."
-msgstr "PL/Python unterstützt nur eindimensionale Arrays."
-
-#: plpy_typeio.c:652
-#, c-format
-msgid "could not create new Python list"
-msgstr "konnte neue Python-Liste nicht erzeugen"
-
-#: plpy_typeio.c:711
+#: plpy_typeio.c:772
#, c-format
msgid "could not create bytes representation of Python object"
msgstr "konnte Bytes-Darstellung eines Python-Objektes nicht erzeugen"
-#: plpy_typeio.c:822
+#: plpy_typeio.c:881
#, c-format
msgid "could not create string representation of Python object"
msgstr "konnte Zeichenkettendarstellung eines Python-Objektes nicht erzeugen"
-#: plpy_typeio.c:833
+#: plpy_typeio.c:892
#, c-format
msgid "could not convert Python object into cstring: Python string representation appears to contain null bytes"
msgstr "konnte Python-Objekt nicht in cstring umwandeln: Python-Zeichenkettendarstellung enthält anscheinend Null-Bytes"
-#: plpy_typeio.c:879
+#: plpy_typeio.c:949
+#, c-format
+msgid "malformed record literal: \"%s\""
+msgstr "fehlerhafte Record-Konstante: »%s«"
+
+#: plpy_typeio.c:950
+#, c-format
+msgid "Missing left parenthesis."
+msgstr "Linke Klammer fehlt."
+
+#: plpy_typeio.c:951 plpy_typeio.c:1389
+#, c-format
+msgid "To return a composite type in an array, return the composite type as a Python tuple, e.g. \"[('foo')]\""
+msgstr ""
+
+#: plpy_typeio.c:1000
+#, c-format
+msgid "number of array dimensions exceeds the maximum allowed (%d)"
+msgstr "Anzahl der Arraydimensionen überschreitet erlaubtes Maximum (%d)"
+
+#: plpy_typeio.c:1004
+#, fuzzy, c-format
+#| msgid "cannot determine OID of function lo_truncate\n"
+msgid "cannot determine sequence length for function return value"
+msgstr "kann OID der Funktion lo_truncate nicht ermitteln\n"
+
+#: plpy_typeio.c:1007 plpy_typeio.c:1011
+#, fuzzy, c-format
+#| msgid "array size exceeds the maximum allowed (%d)"
+msgid "array size exceeds the maximum allowed"
+msgstr "Arraygröße überschreitet erlaubtes Maximum (%d)"
+
+#: plpy_typeio.c:1037
#, c-format
msgid "return value of function with array return type is not a Python sequence"
msgstr "Rückgabewert von Funktion mit Array-Rückgabetyp ist keine Python-Sequenz"
-#: plpy_typeio.c:1000
+#: plpy_typeio.c:1090
+#, fuzzy, c-format
+#| msgid "multidimensional arrays must have array expressions with matching dimensions"
+msgid "multidimensional arrays must have array expressions with matching dimensions. PL/Python function return value has sequence length %d while expected %d"
+msgstr "mehrdimensionale Arrays müssen Arraysausdrücke mit gleicher Anzahl Dimensionen haben"
+
+#: plpy_typeio.c:1212
#, c-format
msgid "key \"%s\" not found in mapping"
msgstr "Schlüssel »%s« nicht in Mapping gefunden"
-#: plpy_typeio.c:1001
+#: plpy_typeio.c:1213
#, c-format
msgid "To return null in a column, add the value None to the mapping with the key named after the column."
msgstr "Um einen NULL-Wert in einer Spalte zurückzugeben, muss der Wert None mit einem nach der Spalte benannten Schlüssel in das Mapping eingefügt werden."
-#: plpy_typeio.c:1052
+#: plpy_typeio.c:1264
#, c-format
msgid "length of returned sequence did not match number of columns in row"
msgstr "Länge der zurückgegebenen Sequenz hat nicht mit der Anzahl der Spalten in der Zeile übereingestimmt"
-#: plpy_typeio.c:1163
+#: plpy_typeio.c:1387
#, c-format
msgid "attribute \"%s\" does not exist in Python object"
msgstr "Attribut »%s« existiert nicht in Python-Objekt"
-#: plpy_typeio.c:1164
+#: plpy_typeio.c:1390
#, c-format
msgid "To return null in a column, let the returned object have an attribute named after column with value None."
msgstr "Um einen NULL-Wert in einer Spalte zurückzugeben, muss das zurückzugebende Objekt ein nach der Spalte benanntes Attribut mit dem Wert None haben."
@@ -434,3 +462,6 @@ msgstr "konnte Python-Unicode-Objekt nicht in Bytes umwandeln"
#, c-format
msgid "could not extract bytes from encoded string"
msgstr "konnte kodierte Zeichenkette nicht in Bytes umwandeln"
+
+#~ msgid "could not create new Python list"
+#~ msgstr "konnte neue Python-Liste nicht erzeugen"
diff --git a/src/pl/plpython/po/fr.po b/src/pl/plpython/po/fr.po
index 37c50119f2..2db977f6d6 100644
--- a/src/pl/plpython/po/fr.po
+++ b/src/pl/plpython/po/fr.po
@@ -1,72 +1,72 @@
# translation of plpython.po to fr_fr
# french message translation file for plpython
#
-# Use these quotes: %s
+# Use these quotes: « %s »
# Guillaume Lelarge <guillaume@lelarge.info>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: PostgreSQL 9.6\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2016-05-08 21:07+0000\n"
-"PO-Revision-Date: 2016-05-09 10:33+0200\n"
+"POT-Creation-Date: 2017-02-06 16:07+0000\n"
+"PO-Revision-Date: 2017-02-06 19:02+0100\n"
"Last-Translator: Guillaume Lelarge <guillaume@lelarge.info>\n"
"Language-Team: French <guillaume@lelarge.info>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-"X-Generator: Poedit 1.8.7.1\n"
+"X-Generator: Poedit 1.8.11\n"
#: plpy_cursorobject.c:101
#, c-format
msgid "plpy.cursor expected a query or a plan"
-msgstr "plpy.cursor attendait une requte ou un plan"
+msgstr "plpy.cursor attendait une requête ou un plan"
-#: plpy_cursorobject.c:179
+#: plpy_cursorobject.c:177
#, c-format
msgid "plpy.cursor takes a sequence as its second argument"
-msgstr "plpy.cursor prends une squence dans son second argument"
+msgstr "plpy.cursor prends une séquence dans son second argument"
-#: plpy_cursorobject.c:195 plpy_spi.c:229
+#: plpy_cursorobject.c:193 plpy_spi.c:227
#, c-format
msgid "could not execute plan"
-msgstr "n'a pas pu excuter le plan"
+msgstr "n'a pas pu exécuter le plan"
-#: plpy_cursorobject.c:198 plpy_spi.c:232
+#: plpy_cursorobject.c:196 plpy_spi.c:230
#, c-format
msgid "Expected sequence of %d argument, got %d: %s"
msgid_plural "Expected sequence of %d arguments, got %d: %s"
-msgstr[0] "Squence attendue de %d argument, %d obtenu : %s"
-msgstr[1] "Squence attendue de %d arguments, %d obtenus : %s"
+msgstr[0] "Séquence attendue de %d argument, %d obtenu : %s"
+msgstr[1] "Séquence attendue de %d arguments, %d obtenus : %s"
-#: plpy_cursorobject.c:354
+#: plpy_cursorobject.c:350
#, c-format
msgid "iterating a closed cursor"
-msgstr "itration d'un curseur ferm"
+msgstr "itération d'un curseur fermé"
-#: plpy_cursorobject.c:362 plpy_cursorobject.c:427
+#: plpy_cursorobject.c:358 plpy_cursorobject.c:423
#, c-format
msgid "iterating a cursor in an aborted subtransaction"
-msgstr "itration d'un curseur dans une sous-transaction annule"
+msgstr "itération d'un curseur dans une sous-transaction annulée"
-#: plpy_cursorobject.c:419
+#: plpy_cursorobject.c:415
#, c-format
msgid "fetch from a closed cursor"
-msgstr "rcuprer partir d'un curseur ferm"
+msgstr "récupérer à partir d'un curseur fermé"
-#: plpy_cursorobject.c:467 plpy_spi.c:438
+#: plpy_cursorobject.c:463 plpy_spi.c:434
#, c-format
msgid "query result has too many rows to fit in a Python list"
-msgstr "le rsultat de la requte contient trop de lignes pour tre intgr dans une liste Python"
+msgstr "le résultat de la requête contient trop de lignes pour être intégré dans une liste Python"
-#: plpy_cursorobject.c:508
+#: plpy_cursorobject.c:504
#, c-format
msgid "closing a cursor in an aborted subtransaction"
-msgstr "fermeture d'un curseur dans une sous-transaction annule"
+msgstr "fermeture d'un curseur dans une sous-transaction annulée"
-#: plpy_elog.c:127 plpy_elog.c:128 plpy_plpymodule.c:513
+#: plpy_elog.c:127 plpy_elog.c:128 plpy_plpymodule.c:548
#, c-format
msgid "%s"
msgstr "%s"
@@ -74,92 +74,92 @@ msgstr "%s"
#: plpy_exec.c:140
#, c-format
msgid "unsupported set function return mode"
-msgstr "mode de retour non support pour la fonction SET"
+msgstr "mode de retour non supporté pour la fonction SET"
#: plpy_exec.c:141
#, c-format
msgid "PL/Python set-returning functions only support returning one value per call."
msgstr ""
"les fonctions PL/python renvoyant des ensembles supportent seulement une\n"
-"valeur renvoye par appel."
+"valeur renvoyée par appel."
#: plpy_exec.c:154
#, c-format
msgid "returned object cannot be iterated"
-msgstr "l'objet renvoy ne supporte pas les itrations"
+msgstr "l'objet renvoyé ne supporte pas les itérations"
#: plpy_exec.c:155
#, c-format
msgid "PL/Python set-returning functions must return an iterable object."
msgstr ""
"les fonctions PL/python renvoyant des ensembles doivent renvoyer un objet\n"
-"itrable"
+"itérable"
#: plpy_exec.c:169
#, c-format
msgid "error fetching next item from iterator"
-msgstr "erreur lors de la rcupration du prochain lment de l'itrateur"
+msgstr "erreur lors de la récupération du prochain élément de l'itérateur"
#: plpy_exec.c:210
#, c-format
msgid "PL/Python function with return type \"void\" did not return None"
-msgstr "la fonction PL/python avec un code de retour void ne renvoyait pas None"
+msgstr "la fonction PL/python avec un code de retour « void » ne renvoyait pas None"
#: plpy_exec.c:374 plpy_exec.c:400
#, c-format
msgid "unexpected return value from trigger procedure"
-msgstr "valeur de retour inattendue de la procdure trigger"
+msgstr "valeur de retour inattendue de la procédure trigger"
#: plpy_exec.c:375
#, c-format
msgid "Expected None or a string."
-msgstr "Attendait None ou une chane de caractres."
+msgstr "Attendait None ou une chaîne de caractères."
#: plpy_exec.c:390
#, c-format
msgid "PL/Python trigger function returned \"MODIFY\" in a DELETE trigger -- ignored"
msgstr ""
-"la fonction trigger PL/python a renvoy MODIFY dans un trigger DELETE\n"
-"-- ignor"
+"la fonction trigger PL/python a renvoyé « MODIFY » dans un trigger DELETE\n"
+"-- ignoré"
#: plpy_exec.c:401
#, c-format
msgid "Expected None, \"OK\", \"SKIP\", or \"MODIFY\"."
-msgstr "Attendait None, OK , SKIP ou MODIFY ."
+msgstr "Attendait None, « OK », « SKIP » ou « MODIFY »."
#: plpy_exec.c:482
#, c-format
msgid "PyList_SetItem() failed, while setting up arguments"
-msgstr "chec de PyList_SetItem() lors de l'initialisation des arguments"
+msgstr "échec de PyList_SetItem() lors de l'initialisation des arguments"
#: plpy_exec.c:486
#, c-format
msgid "PyDict_SetItemString() failed, while setting up arguments"
-msgstr "chec de PyDict_SetItemString() lors de l'initialisation des arguments"
+msgstr "échec de PyDict_SetItemString() lors de l'initialisation des arguments"
#: plpy_exec.c:498
#, c-format
msgid "function returning record called in context that cannot accept type record"
msgstr ""
-"fonction renvoyant le type record appele dans un contexte qui ne peut pas\n"
+"fonction renvoyant le type record appelée dans un contexte qui ne peut pas\n"
"accepter le type record"
#: plpy_exec.c:714
#, c-format
msgid "while creating return value"
-msgstr "lors de la cration de la valeur de retour"
+msgstr "lors de la création de la valeur de retour"
#: plpy_exec.c:738
#, c-format
msgid "could not create new dictionary while building trigger arguments"
msgstr ""
-"n'a pas pu crer un nouveau dictionnaire lors de la construction des\n"
+"n'a pas pu créer un nouveau dictionnaire lors de la construction des\n"
"arguments du trigger"
#: plpy_exec.c:927
#, c-format
msgid "TD[\"new\"] deleted, cannot modify row"
-msgstr "TD[\"new\"] supprim, ne peut pas modifier la ligne"
+msgstr "TD[\"new\"] supprimé, ne peut pas modifier la ligne"
#: plpy_exec.c:932
#, c-format
@@ -169,14 +169,14 @@ msgstr "TD[\"new\"] n'est pas un dictionnaire"
#: plpy_exec.c:957
#, c-format
msgid "TD[\"new\"] dictionary key at ordinal position %d is not a string"
-msgstr "la cl TD[\"new\"] la position ordinale %d n'est pas une chane"
+msgstr "la clé TD[\"new\"] à la position ordinale %d n'est pas une chaîne"
#: plpy_exec.c:964
#, c-format
msgid "key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row"
msgstr ""
-"la cl %s trouve dans TD[\"new\"] n'existe pas comme colonne\n"
-"de la ligne impacte par le trigger"
+"la clé « %s » trouvée dans TD[\"new\"] n'existe pas comme colonne\n"
+"de la ligne impactée par le trigger"
#: plpy_exec.c:1044
#, c-format
@@ -186,44 +186,44 @@ msgstr "lors de la modification de la ligne du trigger"
#: plpy_exec.c:1105
#, c-format
msgid "forcibly aborting a subtransaction that has not been exited"
-msgstr "annulation force d'une sous-transaction qui n'a jamais t quitte"
+msgstr "annulation forcée d'une sous-transaction qui n'a jamais été quittée"
#: plpy_main.c:125
#, c-format
msgid "multiple Python libraries are present in session"
-msgstr "plusieurs bibliothques Python sont prsentes dans la session"
+msgstr "plusieurs bibliothèques Python sont présentes dans la session"
#: plpy_main.c:126
#, c-format
msgid "Only one Python major version can be used in one session."
-msgstr "Seule une version majeure de Python peut tre utilise dans une session."
+msgstr "Seule une version majeure de Python peut être utilisée dans une session."
#: plpy_main.c:142
#, c-format
msgid "untrapped error in initialization"
-msgstr "erreur non rcupre dans l'initialisation"
+msgstr "erreur non récupérée dans l'initialisation"
#: plpy_main.c:165
#, c-format
msgid "could not import \"__main__\" module"
-msgstr "n'a pas pu importer le module __main__ "
+msgstr "n'a pas pu importer le module « __main__ »"
#: plpy_main.c:170
#, c-format
msgid "could not create globals"
-msgstr "n'a pas pu crer les globales"
+msgstr "n'a pas pu créer les globales"
#: plpy_main.c:174
#, c-format
msgid "could not initialize globals"
msgstr "n'a pas pu initialiser les variables globales"
-#: plpy_main.c:389
+#: plpy_main.c:387
#, c-format
msgid "PL/Python function \"%s\""
-msgstr "fonction PL/python %s "
+msgstr "fonction PL/python « %s »"
-#: plpy_main.c:396
+#: plpy_main.c:394
#, c-format
msgid "PL/Python anonymous code block"
msgstr "bloc de code PL/Python anonyme"
@@ -233,71 +233,76 @@ msgstr "bloc de code PL/Python anonyme"
msgid "plan.status takes no arguments"
msgstr "plan.status ne prends pas d'arguments"
-#: plpy_plpymodule.c:178 plpy_plpymodule.c:181
+#: plpy_plpymodule.c:181 plpy_plpymodule.c:184
#, c-format
msgid "could not import \"plpy\" module"
-msgstr "n'a pas pu importer le module plpy "
+msgstr "n'a pas pu importer le module « plpy »"
-#: plpy_plpymodule.c:196
+#: plpy_plpymodule.c:199
+#, c-format
+msgid "could not create the spiexceptions module"
+msgstr "n'a pas pu créer le module « spiexceptions »"
+
+#: plpy_plpymodule.c:207
#, c-format
msgid "could not add the spiexceptions module"
-msgstr "n'a pas pu ajouter le module spiexceptions "
+msgstr "n'a pas pu ajouter le module « spiexceptions »"
-#: plpy_plpymodule.c:217
+#: plpy_plpymodule.c:236
#, c-format
-msgid "could not create the base SPI exceptions"
-msgstr "n'a pas pu crer les exceptions SPI de base"
+msgid "could not create exception \"%s\""
+msgstr "n'a pas pu créer l'exception « %s »"
-#: plpy_plpymodule.c:252 plpy_plpymodule.c:256
+#: plpy_plpymodule.c:271 plpy_plpymodule.c:275
#, c-format
msgid "could not generate SPI exceptions"
-msgstr "n'a pas pu gnrer les exceptions SPI"
+msgstr "n'a pas pu générer les exceptions SPI"
-#: plpy_plpymodule.c:421
+#: plpy_plpymodule.c:443
#, c-format
msgid "could not unpack arguments in plpy.elog"
-msgstr "n'a pas pu dballer les arguments dans plpy.elog"
+msgstr "n'a pas pu déballer les arguments dans plpy.elog"
-#: plpy_plpymodule.c:430
+#: plpy_plpymodule.c:452
msgid "could not parse error message in plpy.elog"
msgstr "n'a pas pu analyser le message d'erreur dans plpy.elog"
-#: plpy_plpymodule.c:446
+#: plpy_plpymodule.c:469
#, c-format
-msgid "the message is already specified"
-msgstr "le message est dj spcifi"
+msgid "Argument 'message' given by name and position"
+msgstr "Argument 'message' donn\" par nom et position"
-#: plpy_plpymodule.c:469
+#: plpy_plpymodule.c:496
#, c-format
msgid "'%s' is an invalid keyword argument for this function"
-msgstr "'%s' est une argument mot-cl invalide pour cette fonction"
+msgstr "'%s' est une argument mot-clé invalide pour cette fonction"
-#: plpy_plpymodule.c:477 plpy_plpymodule.c:480
+#: plpy_plpymodule.c:507 plpy_plpymodule.c:513
#, c-format
msgid "invalid SQLSTATE code"
msgstr "code SQLSTATE invalide"
-#: plpy_procedure.c:232
+#: plpy_procedure.c:230
#, c-format
msgid "trigger functions can only be called as triggers"
-msgstr "les fonctions trigger peuvent seulement tre appeles par des triggers"
+msgstr "les fonctions trigger peuvent seulement être appelées par des triggers"
-#: plpy_procedure.c:237
+#: plpy_procedure.c:235
#, c-format
msgid "PL/Python functions cannot return type %s"
msgstr "les fonctions PL/python ne peuvent pas renvoyer le type %s"
-#: plpy_procedure.c:318
+#: plpy_procedure.c:316
#, c-format
msgid "PL/Python functions cannot accept type %s"
msgstr "les fonctions PL/python ne peuvent pas accepter le type %s"
-#: plpy_procedure.c:414
+#: plpy_procedure.c:412
#, c-format
msgid "could not compile PL/Python function \"%s\""
-msgstr "n'a pas pu compiler la fonction PL/python %s "
+msgstr "n'a pas pu compiler la fonction PL/python « %s »"
-#: plpy_procedure.c:417
+#: plpy_procedure.c:415
#, c-format
msgid "could not compile anonymous PL/Python code block"
msgstr "n'a pas pu compiler le bloc de code anonyme PL/python"
@@ -305,37 +310,37 @@ msgstr "n'a pas pu compiler le bloc de code anonyme PL/python"
#: plpy_resultobject.c:145 plpy_resultobject.c:165 plpy_resultobject.c:185
#, c-format
msgid "command did not produce a result set"
-msgstr "la commande n'a pas fourni d'ensemble de rsultats"
+msgstr "la commande n'a pas fourni d'ensemble de résultats"
#: plpy_spi.c:60
#, c-format
msgid "second argument of plpy.prepare must be a sequence"
-msgstr "le second argument de plpy.prepare doit tre une squence"
+msgstr "le second argument de plpy.prepare doit être une séquence"
-#: plpy_spi.c:118
+#: plpy_spi.c:116
#, c-format
msgid "plpy.prepare: type name at ordinal position %d is not a string"
-msgstr "plpy.prepare : le nom du type sur la position ordinale %d n'est pas une chane"
+msgstr "plpy.prepare : le nom du type sur la position ordinale %d n'est pas une chaîne"
-#: plpy_spi.c:194
+#: plpy_spi.c:192
#, c-format
msgid "plpy.execute expected a query or a plan"
-msgstr "plpy.prepare attendait une requte ou un plan"
+msgstr "plpy.prepare attendait une requête ou un plan"
-#: plpy_spi.c:213
+#: plpy_spi.c:211
#, c-format
msgid "plpy.execute takes a sequence as its second argument"
-msgstr "plpy.execute prends une squence dans son second argument"
+msgstr "plpy.execute prends une séquence dans son second argument"
-#: plpy_spi.c:337
+#: plpy_spi.c:335
#, c-format
msgid "SPI_execute_plan failed: %s"
-msgstr "chec de SPI_execute_plan : %s"
+msgstr "échec de SPI_execute_plan : %s"
-#: plpy_spi.c:379
+#: plpy_spi.c:377
#, c-format
msgid "SPI_execute failed: %s"
-msgstr "chec de SPI_execute : %s"
+msgstr "échec de SPI_execute : %s"
#: plpy_subxactobject.c:123
#, c-format
@@ -345,22 +350,22 @@ msgstr "cette sous-transaction est en cours d'utilisation"
#: plpy_subxactobject.c:129 plpy_subxactobject.c:187
#, c-format
msgid "this subtransaction has already been exited"
-msgstr "dj sorti de cette sous-transaction"
+msgstr "déjà sorti de cette sous-transaction"
#: plpy_subxactobject.c:181
#, c-format
msgid "this subtransaction has not been entered"
-msgstr "cette sous-transaction n'a jamais t utilise"
+msgstr "cette sous-transaction n'a jamais été utilisée"
#: plpy_subxactobject.c:193
#, c-format
msgid "there is no subtransaction to exit from"
-msgstr "il n'y a pas de transaction quitter"
+msgstr "il n'y a pas de transaction à quitter"
#: plpy_typeio.c:286
#, c-format
msgid "could not create new dictionary"
-msgstr "n'a pas pu crer le nouveau dictionnaire"
+msgstr "n'a pas pu créer le nouveau dictionnaire"
#: plpy_typeio.c:560
#, c-format
@@ -375,7 +380,7 @@ msgstr "pas d'attribut Decimal dans le module"
#: plpy_typeio.c:570
#, c-format
msgid "conversion from numeric to Decimal failed"
-msgstr "chec de la conversion numeric vers Decimal"
+msgstr "échec de la conversion numeric vers Decimal"
#: plpy_typeio.c:645
#, c-format
@@ -390,58 +395,58 @@ msgstr "PL/Python supporte seulement les tableaux uni-dimensionnels."
#: plpy_typeio.c:652
#, c-format
msgid "could not create new Python list"
-msgstr "n'a pas pu crer la nouvelle liste Python"
+msgstr "n'a pas pu créer la nouvelle liste Python"
#: plpy_typeio.c:711
#, c-format
msgid "could not create bytes representation of Python object"
-msgstr "n'a pas pu crer une reprsentation octets de l'objet Python"
+msgstr "n'a pas pu créer une représentation octets de l'objet Python"
-#: plpy_typeio.c:822
+#: plpy_typeio.c:820
#, c-format
msgid "could not create string representation of Python object"
-msgstr "n'a pas pu crer une reprsentation chane de caractres de l'objet Python"
+msgstr "n'a pas pu créer une représentation chaîne de caractères de l'objet Python"
-#: plpy_typeio.c:833
+#: plpy_typeio.c:831
#, c-format
msgid "could not convert Python object into cstring: Python string representation appears to contain null bytes"
-msgstr "n'a pas pu convertir l'objet Python en csting : la reprsentation de la chane Python contient des octets nuls"
+msgstr "n'a pas pu convertir l'objet Python en csting : la représentation de la chaîne Python contient des octets nuls"
-#: plpy_typeio.c:879
+#: plpy_typeio.c:877
#, c-format
msgid "return value of function with array return type is not a Python sequence"
-msgstr "la valeur de retour de la fonction de type tableau n'est pas une squence Python"
+msgstr "la valeur de retour de la fonction de type tableau n'est pas une séquence Python"
-#: plpy_typeio.c:1000
+#: plpy_typeio.c:996
#, c-format
msgid "key \"%s\" not found in mapping"
-msgstr "la cl %s introuvable dans la correspondance"
+msgstr "la clé « %s » introuvable dans la correspondance"
-#: plpy_typeio.c:1001
+#: plpy_typeio.c:997
#, c-format
msgid "To return null in a column, add the value None to the mapping with the key named after the column."
msgstr ""
-"Pour renvoyer NULL dans une colonne, ajoutez la valeur None la\n"
-"correspondance de la cl nomme d'aprs la colonne."
+"Pour renvoyer NULL dans une colonne, ajoutez la valeur None à la\n"
+"correspondance de la clé nommée d'après la colonne."
-#: plpy_typeio.c:1052
+#: plpy_typeio.c:1048
#, c-format
msgid "length of returned sequence did not match number of columns in row"
msgstr ""
-"la longueur de la squence renvoye ne correspondait pas au nombre de\n"
+"la longueur de la séquence renvoyée ne correspondait pas au nombre de\n"
"colonnes dans la ligne"
-#: plpy_typeio.c:1163
+#: plpy_typeio.c:1159
#, c-format
msgid "attribute \"%s\" does not exist in Python object"
-msgstr "l'attribut %s n'existe pas dans l'objet Python"
+msgstr "l'attribut « %s » n'existe pas dans l'objet Python"
-#: plpy_typeio.c:1164
+#: plpy_typeio.c:1160
#, c-format
msgid "To return null in a column, let the returned object have an attribute named after column with value None."
msgstr ""
-"Pour renvoyer NULL dans une colonne, faites en sorte que l'objet renvoy ait\n"
-"un attribut nomm suivant la colonne de valeur None."
+"Pour renvoyer NULL dans une colonne, faites en sorte que l'objet renvoyé ait\n"
+"un attribut nommé suivant la colonne de valeur None."
#: plpy_util.c:36
#, c-format
@@ -451,7 +456,10 @@ msgstr "n'a pas pu convertir l'objet Unicode Python en octets"
#: plpy_util.c:42
#, c-format
msgid "could not extract bytes from encoded string"
-msgstr "n'a pas pu extraire les octets de la chane encode"
+msgstr "n'a pas pu extraire les octets de la chaîne encodée"
+
+#~ msgid "the message is already specified"
+#~ msgstr "le message est déjà spécifié"
#~ msgid "plpy.prepare does not support composite types"
#~ msgstr "plpy.prepare ne supporte pas les types composites"
@@ -463,13 +471,13 @@ msgstr "n'a pas pu extraire les octets de la chane encode"
#~ msgstr "erreur inconnue dans PLy_spi_execute_fetch_result"
#~ msgid "PyCObject_AsVoidPtr() failed"
-#~ msgstr "chec de PyCObject_AsVoidPtr()"
+#~ msgstr "échec de PyCObject_AsVoidPtr()"
#~ msgid "PyCObject_FromVoidPtr() failed"
-#~ msgstr "chec de PyCObject_FromVoidPtr()"
+#~ msgstr "échec de PyCObject_FromVoidPtr()"
#~ msgid "transaction aborted"
-#~ msgstr "transaction annule"
+#~ msgstr "transaction annulée"
#~ msgid "invalid arguments for plpy.prepare"
#~ msgstr "arguments invalides pour plpy.prepare"
@@ -484,40 +492,43 @@ msgstr "n'a pas pu extraire les octets de la chane encode"
#~ msgstr "erreur inconnue dans PLy_spi_execute_query"
#~ msgid "could not create procedure cache"
-#~ msgstr "n'a pas pu crer le cache de procdure"
+#~ msgstr "n'a pas pu créer le cache de procédure"
#~ msgid "PL/Python: %s"
#~ msgstr "PL/python : %s"
#~ msgid "out of memory"
-#~ msgstr "mmoire puise"
+#~ msgstr "mémoire épuisée"
#~ msgid "PL/Python function \"%s\" failed"
-#~ msgstr "chec de la fonction PL/python %s "
+#~ msgstr "échec de la fonction PL/python « %s »"
#~ msgid "could not compute string representation of Python object in PL/Python function \"%s\" while modifying trigger row"
#~ msgstr ""
-#~ "n'a pas pu traiter la reprsentation de la chane d'un objet Python dans\n"
-#~ "la fonction PL/Python %s lors de la modification de la ligne du trigger"
+#~ "n'a pas pu traiter la représentation de la chaîne d'un objet Python dans\n"
+#~ "la fonction PL/Python « %s » lors de la modification de la ligne du trigger"
#~ msgid "could not create string representation of Python object in PL/Python function \"%s\" while creating return value"
#~ msgstr ""
-#~ "n'a pas pu crer la reprsentation en chane de caractre de l'objet\n"
-#~ "Python dans la fonction PL/python %s lors de la cration de la valeur\n"
+#~ "n'a pas pu créer la représentation en chaîne de caractère de l'objet\n"
+#~ "Python dans la fonction PL/python « %s » lors de la création de la valeur\n"
#~ "de retour"
#~ msgid "PL/Python function \"%s\" could not execute plan"
-#~ msgstr "la fonction PL/python %s n'a pas pu excuter un plan"
+#~ msgstr "la fonction PL/python « %s » n'a pas pu exécuter un plan"
#~ msgid "Start a new session to use a different Python major version."
#~ msgstr ""
-#~ "Lancez une nouvelle session pour utiliser une version majeure diffrente de\n"
+#~ "Lancez une nouvelle session pour utiliser une version majeure différente de\n"
#~ "Python."
#~ msgid "This session has previously used Python major version %d, and it is now attempting to use Python major version %d."
#~ msgstr ""
-#~ "Cette session a auparavant utilis la version majeure %d de Python et elle\n"
+#~ "Cette session a auparavant utilisé la version majeure %d de Python et elle\n"
#~ "essaie maintenant d'utiliser la version majeure %d."
#~ msgid "Python major version mismatch in session"
-#~ msgstr "Diffrence de version majeure de Python dans la session"
+#~ msgstr "Différence de version majeure de Python dans la session"
+
+#~ msgid "could not create the base SPI exceptions"
+#~ msgstr "n'a pas pu créer les exceptions SPI de base"
diff --git a/src/pl/plpython/po/ko.po b/src/pl/plpython/po/ko.po
index 3b388e24e9..8608d1ba94 100644
--- a/src/pl/plpython/po/ko.po
+++ b/src/pl/plpython/po/ko.po
@@ -5,161 +5,169 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: plpython (PostgreSQL) 9.5\n"
+"Project-Id-Version: plpython (PostgreSQL) 9.6\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2015-12-22 15:41+0900\n"
-"PO-Revision-Date: 2015-12-22 17:57+0900\n"
+"POT-Creation-Date: 2016-09-26 14:02+0900\n"
+"PO-Revision-Date: 2016-09-26 19:31+0900\n"
"Last-Translator: Ioseph Kim <ioseph@uri.sarang.net>\n"
"Language-Team: Korean <pgsql-kr@postgresql.kr>\n"
+"Language: ko\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: ko\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-#: plpy_cursorobject.c:98
+#: plpy_cursorobject.c:101
#, c-format
msgid "plpy.cursor expected a query or a plan"
msgstr "plpy.cursor 객체는 쿼리나 plpy.prepare 객체를 인자로 사용합니다"
-#: plpy_cursorobject.c:171
+#: plpy_cursorobject.c:177
#, c-format
msgid "plpy.cursor takes a sequence as its second argument"
-msgstr "plpy.cursor 객체의 인자로 plpy.prepare 객체를 사용한 경우 두번째 인자는 prepare 객체의 매개변수가 있어야 합니다."
+msgstr ""
+"plpy.cursor 객체의 인자로 plpy.prepare 객체를 사용한 경우 두번째 인자는 "
+"prepare 객체의 매개변수가 있어야 합니다."
-#: plpy_cursorobject.c:187 plpy_spi.c:217
+#: plpy_cursorobject.c:193 plpy_spi.c:227
#, c-format
msgid "could not execute plan"
msgstr "plpy.prepare 객체를 실행할 수 없음"
-#: plpy_cursorobject.c:190 plpy_spi.c:220
+#: plpy_cursorobject.c:196 plpy_spi.c:230
#, c-format
msgid "Expected sequence of %d argument, got %d: %s"
msgid_plural "Expected sequence of %d arguments, got %d: %s"
msgstr[0] "%d 개의 인자가 필요한데, %d개의 인자를 지정했음: %s"
-#: plpy_cursorobject.c:340
+#: plpy_cursorobject.c:350
#, c-format
msgid "iterating a closed cursor"
msgstr "이미 닫긴 커서에서 다음 자료를 요구하고 있음"
-#: plpy_cursorobject.c:348 plpy_cursorobject.c:413
+#: plpy_cursorobject.c:358 plpy_cursorobject.c:423
#, c-format
msgid "iterating a cursor in an aborted subtransaction"
msgstr "중지된 서브 트랜잭션에 있는 커서에서 다음 자료를 요구하고 있음"
-#: plpy_cursorobject.c:405
+#: plpy_cursorobject.c:415
#, c-format
msgid "fetch from a closed cursor"
msgstr "닫긴 커서에서 fetch"
-#: plpy_cursorobject.c:482
+#: plpy_cursorobject.c:463 plpy_spi.c:434
+#, c-format
+msgid "query result has too many rows to fit in a Python list"
+msgstr "쿼리 결과가 Python 리스트로 담기에는 너무 많습니다"
+
+#: plpy_cursorobject.c:504
#, c-format
msgid "closing a cursor in an aborted subtransaction"
msgstr "중지된 서브트랜잭션에서 커서를 닫고 있음"
-#: plpy_elog.c:103 plpy_elog.c:104 plpy_plpymodule.c:419
+#: plpy_elog.c:127 plpy_elog.c:128 plpy_plpymodule.c:527
#, c-format
msgid "%s"
msgstr "%s"
-#: plpy_exec.c:91
+#: plpy_exec.c:140
#, c-format
msgid "unsupported set function return mode"
msgstr "지원하지 않는 집합 함수 리턴 모드"
-#: plpy_exec.c:92
+#: plpy_exec.c:141
#, c-format
msgid ""
"PL/Python set-returning functions only support returning one value per call."
msgstr ""
-"PL/Python 집합-반환 함수는 한번의 호출에 대해서 하나의 값만 반환할 수 있습니다."
+"PL/Python 집합-반환 함수는 한번의 호출에 대해서 하나의 값만 반환할 수 있습니"
+"다."
-#: plpy_exec.c:104
+#: plpy_exec.c:154
#, c-format
msgid "returned object cannot be iterated"
msgstr "반환하는 객체가 iterable 형이 아님"
-#: plpy_exec.c:105
+#: plpy_exec.c:155
#, c-format
msgid "PL/Python set-returning functions must return an iterable object."
msgstr "PL/Python 집합-반환 함수는 iterable 객체를 반환해야 합니다."
-#: plpy_exec.c:130
+#: plpy_exec.c:169
#, c-format
msgid "error fetching next item from iterator"
msgstr "iterator에서 다음 아이템을 가져올 수 없음"
-#: plpy_exec.c:165
+#: plpy_exec.c:210
#, c-format
msgid "PL/Python function with return type \"void\" did not return None"
-msgstr "반환 자료형이 \"void\"인 PL/Python 함수가 return None으로 끝나지 않았음"
+msgstr ""
+"반환 자료형이 \"void\"인 PL/Python 함수가 return None으로 끝나지 않았음"
-#: plpy_exec.c:291 plpy_exec.c:317
+#: plpy_exec.c:374 plpy_exec.c:400
#, c-format
msgid "unexpected return value from trigger procedure"
msgstr "트리거 프로시져가 예상치 못한 값을 반환했습니다"
-#: plpy_exec.c:292
+#: plpy_exec.c:375
#, c-format
msgid "Expected None or a string."
msgstr "None 이나 문자열이 있어야합니다."
-#: plpy_exec.c:307
+#: plpy_exec.c:390
#, c-format
msgid ""
"PL/Python trigger function returned \"MODIFY\" in a DELETE trigger -- ignored"
msgstr ""
"PL/Python 트리거 함수가 DELETE 트리거에서 \"MODIFY\"를 반환했음 -- 무시함"
-#: plpy_exec.c:318
+#: plpy_exec.c:401
#, c-format
msgid "Expected None, \"OK\", \"SKIP\", or \"MODIFY\"."
msgstr "None, \"OK\", \"SKIP\", 또는 \"MODIFY\"를 사용해야 함."
-#: plpy_exec.c:399
+#: plpy_exec.c:482
#, c-format
msgid "PyList_SetItem() failed, while setting up arguments"
msgstr "PyList_SetItem() 함수가 인자 설정하는 중 실패"
-#: plpy_exec.c:403
+#: plpy_exec.c:486
#, c-format
msgid "PyDict_SetItemString() failed, while setting up arguments"
msgstr "PyDict_SetItemString() 함수가 인자 설정하는 중 실패"
-#: plpy_exec.c:415
+#: plpy_exec.c:498
#, c-format
msgid ""
"function returning record called in context that cannot accept type record"
-msgstr ""
-"반환 자료형이 record인데 함수가 그 자료형으로 반환하지 않음"
+msgstr "반환 자료형이 record인데 함수가 그 자료형으로 반환하지 않음"
-#: plpy_exec.c:453
+#: plpy_exec.c:714
#, c-format
msgid "while creating return value"
msgstr "반환값을 만들고 있은 중"
-#: plpy_exec.c:477
+#: plpy_exec.c:738
#, c-format
msgid "could not create new dictionary while building trigger arguments"
msgstr "트리거 인자를 구성하는 중 새 딕션너리를 만들 수 없음"
-#: plpy_exec.c:666
+#: plpy_exec.c:927
#, c-format
msgid "TD[\"new\"] deleted, cannot modify row"
msgstr "TD[\"new\"] 변수가 삭제되었음, 로우를 수정할 수 없음"
-#: plpy_exec.c:671
+#: plpy_exec.c:932
#, c-format
msgid "TD[\"new\"] is not a dictionary"
msgstr "TD[\"new\"] 변수가 딕션너리 형태가 아님"
-#: plpy_exec.c:696
+#: plpy_exec.c:957
#, c-format
msgid "TD[\"new\"] dictionary key at ordinal position %d is not a string"
msgstr "%d 번째 TD[\"new\"] 딕션너리 키가 문자열이 아님"
-#: plpy_exec.c:703
+#: plpy_exec.c:964
#, c-format
msgid ""
"key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering "
@@ -167,65 +175,57 @@ msgid ""
msgstr ""
"로우 트리거 작업에서 칼럼으로 사용되는 \"%s\" 키가 TD[\"new\"] 변수에 없음."
-#: plpy_exec.c:783
+#: plpy_exec.c:1044
#, c-format
msgid "while modifying trigger row"
msgstr "로우 변경 트리거 작업 도중"
-#: plpy_exec.c:844
+#: plpy_exec.c:1105
#, c-format
msgid "forcibly aborting a subtransaction that has not been exited"
msgstr "서브트랜잭션이 중지됨으로 강제로 중지됨"
-#: plpy_main.c:93
-#, c-format
-msgid "Python major version mismatch in session"
-msgstr "세션에서 Python 메이져 버전이 일치하지 않습니다"
-
-#: plpy_main.c:94
+#: plpy_main.c:125
#, c-format
-msgid ""
-"This session has previously used Python major version %d, and it is now "
-"attempting to use Python major version %d."
-msgstr ""
-"이 세션은 이전에 %d 버전을 사용했는데, 지금은 %d 버전을 사용하려고 합니다."
+msgid "multiple Python libraries are present in session"
+msgstr "세션에서 여러 Python 라이브러리가 사용되고 있습니다"
-#: plpy_main.c:96
+#: plpy_main.c:126
#, c-format
-msgid "Start a new session to use a different Python major version."
-msgstr "Python 메이져 버전을 달리 사용하려면 새 세션으로 시작하세요."
+msgid "Only one Python major version can be used in one session."
+msgstr "하나의 세션에서는 하나의 Python 메이져 버전만 사용할 수 있습니다."
-#: plpy_main.c:111
+#: plpy_main.c:142
#, c-format
msgid "untrapped error in initialization"
msgstr "plpy 모듈 초기화 실패"
-#: plpy_main.c:134
+#: plpy_main.c:165
#, c-format
msgid "could not import \"__main__\" module"
msgstr "\"__main__\" 모듈은 임포트 할 수 없음"
-#: plpy_main.c:139
+#: plpy_main.c:170
#, c-format
msgid "could not create globals"
msgstr "전역변수들을 만들 수 없음"
-#: plpy_main.c:143
+#: plpy_main.c:174
#, c-format
msgid "could not initialize globals"
msgstr "전역변수들을 초기화 할 수 없음"
-#: plpy_main.c:348
+#: plpy_main.c:387
#, c-format
msgid "PL/Python function \"%s\""
msgstr "\"%s\" PL/Python 함수"
-#: plpy_main.c:355
+#: plpy_main.c:394
#, c-format
msgid "PL/Python anonymous code block"
-msgstr ""
+msgstr "PL/Python 익명 코드 블럭"
-#: plpy_planobject.c:126
+#: plpy_planobject.c:123
#, c-format
msgid "plan.status takes no arguments"
msgstr "plan.status의 인자가 없습니다."
@@ -250,36 +250,51 @@ msgstr "기본 SPI 예외처리를 만들 수 없음"
msgid "could not generate SPI exceptions"
msgstr "SPI 예외처리를 생성할 수 없음"
-#: plpy_plpymodule.c:387
+#: plpy_plpymodule.c:422
#, c-format
msgid "could not unpack arguments in plpy.elog"
msgstr "잘못된 인자로 구성된 plpy.elog"
-#: plpy_plpymodule.c:395
+#: plpy_plpymodule.c:431
msgid "could not parse error message in plpy.elog"
msgstr "plpy.elog 에서 오류 메시지를 분석할 수 없음"
-#: plpy_procedure.c:213
+#: plpy_plpymodule.c:448
+#, c-format
+msgid "Argument 'message' given by name and position"
+msgstr "'message' 인자는 이름과 위치가 있어야 함"
+
+#: plpy_plpymodule.c:475
+#, c-format
+msgid "'%s' is an invalid keyword argument for this function"
+msgstr "'%s' 값은 이 함수에서 잘못된 예약어 인자입니다"
+
+#: plpy_plpymodule.c:486 plpy_plpymodule.c:492
+#, c-format
+msgid "invalid SQLSTATE code"
+msgstr "잘못된 SQLSTATE 코드"
+
+#: plpy_procedure.c:230
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "트리거 함수는 트리거로만 호출될 수 있음"
-#: plpy_procedure.c:218
+#: plpy_procedure.c:235
#, c-format
msgid "PL/Python functions cannot return type %s"
msgstr "PL/Python 함수는 %s 자료형을 반환할 수 없음"
-#: plpy_procedure.c:300
+#: plpy_procedure.c:316
#, c-format
msgid "PL/Python functions cannot accept type %s"
msgstr "PL/Python 함수는 %s 자료형을 사용할 수 없음"
-#: plpy_procedure.c:398
+#: plpy_procedure.c:412
#, c-format
msgid "could not compile PL/Python function \"%s\""
msgstr "\"%s\" PL/Python 함수를 컴파일 할 수 없음"
-#: plpy_procedure.c:401
+#: plpy_procedure.c:415
#, c-format
msgid "could not compile anonymous PL/Python code block"
msgstr "anonymous PL/Python 코드 블록을 컴파일 할 수 없음"
@@ -289,92 +304,92 @@ msgstr "anonymous PL/Python 코드 블록을 컴파일 할 수 없음"
msgid "command did not produce a result set"
msgstr "명령의 결과값이 없음"
-#: plpy_spi.c:57
+#: plpy_spi.c:60
#, c-format
msgid "second argument of plpy.prepare must be a sequence"
msgstr "plpy.prepare 함수의 두번째 인자는 Python 시퀀스형이어야 함"
-#: plpy_spi.c:106
+#: plpy_spi.c:116
#, c-format
msgid "plpy.prepare: type name at ordinal position %d is not a string"
msgstr "plpy.prepare: %d 번째 인자의 자료형이 문자열이 아님"
-#: plpy_spi.c:182
+#: plpy_spi.c:192
#, c-format
msgid "plpy.execute expected a query or a plan"
msgstr "plpy.execute 함수의 인자는 쿼리문이나 plpy.prepare 객체여야 함"
-#: plpy_spi.c:201
+#: plpy_spi.c:211
#, c-format
msgid "plpy.execute takes a sequence as its second argument"
msgstr "plpy.execut 함수의 두번째 인자는 python 시퀀스형이 와야함"
-#: plpy_spi.c:325
+#: plpy_spi.c:335
#, c-format
msgid "SPI_execute_plan failed: %s"
msgstr "SPI_execute_plan 실패: %s"
-#: plpy_spi.c:367
+#: plpy_spi.c:377
#, c-format
msgid "SPI_execute failed: %s"
msgstr "SPI_execute 실패: %s"
-#: plpy_subxactobject.c:122
+#: plpy_subxactobject.c:123
#, c-format
msgid "this subtransaction has already been entered"
msgstr "이 서브트랜잭션은 이미 시작되었음"
-#: plpy_subxactobject.c:128 plpy_subxactobject.c:180
+#: plpy_subxactobject.c:129 plpy_subxactobject.c:187
#, c-format
msgid "this subtransaction has already been exited"
msgstr "이 서브트랜잭션은 이미 끝났음"
-#: plpy_subxactobject.c:174
+#: plpy_subxactobject.c:181
#, c-format
msgid "this subtransaction has not been entered"
msgstr "이 서브트랜잭션이 시작되지 않았음"
-#: plpy_subxactobject.c:186
+#: plpy_subxactobject.c:193
#, c-format
msgid "there is no subtransaction to exit from"
msgstr "종료할 서브트랜잭션이 없음, 위치:"
-#: plpy_typeio.c:302
+#: plpy_typeio.c:286
#, c-format
msgid "could not create new dictionary"
msgstr "새 디렉터리를 만들 수 없음"
-#: plpy_typeio.c:564
+#: plpy_typeio.c:560
#, c-format
msgid "could not import a module for Decimal constructor"
msgstr "Decimal 자료형 처리를 위해 모듈을 임포트 할 수 없음"
-#: plpy_typeio.c:568
+#: plpy_typeio.c:564
#, c-format
msgid "no Decimal attribute in module"
msgstr "모듈안에 Decimal 속성이 없음"
-#: plpy_typeio.c:574
+#: plpy_typeio.c:570
#, c-format
msgid "conversion from numeric to Decimal failed"
msgstr "numeric 형을 Decimal 형으로 변환할 수 없음"
-#: plpy_typeio.c:649
+#: plpy_typeio.c:645
#, c-format
msgid "cannot convert multidimensional array to Python list"
msgstr "다중 차원 배열은 Python 리스트로 변환할 수 없음"
-#: plpy_typeio.c:650
+#: plpy_typeio.c:646
#, c-format
msgid "PL/Python only supports one-dimensional arrays."
msgstr "PL/Python에서는 1차원 배열만 지원함"
-#: plpy_typeio.c:656
+#: plpy_typeio.c:652
#, c-format
msgid "could not create new Python list"
msgstr "새 Python 리스트를 만들 수 없음"
-#: plpy_typeio.c:715
+#: plpy_typeio.c:711
#, c-format
msgid "could not create bytes representation of Python object"
msgstr "Python 객체를 bytea 자료형으로 변환할 수 없음"
@@ -390,51 +405,62 @@ msgid ""
"could not convert Python object into cstring: Python string representation "
"appears to contain null bytes"
msgstr ""
-"Python 객체를 cstring 형으로 변환할 수 없음: Python string 변수에 null"
-"문자열이 포함되어 있음"
+"Python 객체를 cstring 형으로 변환할 수 없음: Python string 변수에 null문자열"
+"이 포함되어 있음"
#: plpy_typeio.c:877
#, c-format
msgid ""
"return value of function with array return type is not a Python sequence"
-msgstr ""
-"배열형으로 넘길 자료형이 Python 시퀀스형이 아님"
+msgstr "배열형으로 넘길 자료형이 Python 시퀀스형이 아님"
-#: plpy_typeio.c:992
+#: plpy_typeio.c:996
#, c-format
msgid "key \"%s\" not found in mapping"
msgstr "맵 안에 \"%s\" 키가 없음"
-#: plpy_typeio.c:993
+#: plpy_typeio.c:997
#, c-format
msgid ""
"To return null in a column, add the value None to the mapping with the key "
"named after the column."
msgstr ""
+"칼럼값으로 null을 반환하려면, 칼럼 다음에 해당 키 이름과 맵핑 되는 None값을 지정하세요"
-#: plpy_typeio.c:1044
+#: plpy_typeio.c:1048
#, c-format
msgid "length of returned sequence did not match number of columns in row"
msgstr "반환되는 시퀀스형 변수의 길이가 로우의 칼럼수와 일치하지 않음"
-#: plpy_typeio.c:1155
+#: plpy_typeio.c:1159
#, c-format
msgid "attribute \"%s\" does not exist in Python object"
msgstr "Python 객체 가운데 \"%s\" 속성이 없음"
-#: plpy_typeio.c:1156
+#: plpy_typeio.c:1160
#, c-format
msgid ""
"To return null in a column, let the returned object have an attribute named "
"after column with value None."
msgstr ""
+"칼럼 값으로 null 을 반환하려면, 값으로 None 값을 가지는 칼럼 뒤에, "
+"속성 이름이 있는 객체를 반환하세요"
-#: plpy_util.c:72
+#: plpy_util.c:36
#, c-format
msgid "could not convert Python Unicode object to bytes"
msgstr "Python 유니코드 객체를 UTF-8 문자열로 변환할 수 없음"
-#: plpy_util.c:78
+#: plpy_util.c:42
#, c-format
msgid "could not extract bytes from encoded string"
msgstr "해당 인코드 문자열을 Python에서 사용할 수 없음"
+
+#~ msgid "Start a new session to use a different Python major version."
+#~ msgstr "Python 메이져 버전을 달리 사용하려면 새 세션으로 시작하세요."
+
+#~ msgid ""
+#~ "This session has previously used Python major version %d, and it is now "
+#~ "attempting to use Python major version %d."
+#~ msgstr ""
+#~ "이 세션은 이전에 %d 버전을 사용했는데, 지금은 %d 버전을 사용하려고 합니다."
diff --git a/src/pl/plpython/po/pl.po b/src/pl/plpython/po/pl.po
index be5d69af91..4abd6f502b 100644
--- a/src/pl/plpython/po/pl.po
+++ b/src/pl/plpython/po/pl.po
@@ -2,13 +2,13 @@
# Copyright (C) 2011 PostgreSQL Global Development Group
# This file is distributed under the same license as the PostgreSQL package.
# Begina Felicysym <begina.felicysym@wp.eu>, 2011, 2012.
-# grzegorz <begina.felicysym@wp.eu>, 2014, 2015, 2016.
+# grzegorz <begina.felicysym@wp.eu>, 2014, 2015, 2016, 2017.
msgid ""
msgstr ""
"Project-Id-Version: plpython (PostgreSQL 9.1)\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2016-07-18 17:37+0000\n"
-"PO-Revision-Date: 2016-07-18 23:19+0200\n"
+"POT-Creation-Date: 2017-04-09 21:07+0000\n"
+"PO-Revision-Date: 2017-04-11 23:15+0200\n"
"Last-Translator: grzegorz <begina.felicysym@wp.eu>\n"
"Language-Team: begina.felicysym@wp.eu\n"
"Language: pl\n"
@@ -19,22 +19,22 @@ msgstr ""
"|| n%100>=20) ? 1 : 2);\n"
"X-Generator: Virtaal 0.7.1\n"
-#: plpy_cursorobject.c:101
+#: plpy_cursorobject.c:100
#, c-format
msgid "plpy.cursor expected a query or a plan"
msgstr "plpy.cursor oczekuje kwerendy lub planu"
-#: plpy_cursorobject.c:179
+#: plpy_cursorobject.c:176
#, c-format
msgid "plpy.cursor takes a sequence as its second argument"
msgstr "plpy.cursor przyjmuje sekwencję jako drugi argument"
-#: plpy_cursorobject.c:195 plpy_spi.c:229
+#: plpy_cursorobject.c:192 plpy_spi.c:226
#, c-format
msgid "could not execute plan"
msgstr "nie można wykonać planu"
-#: plpy_cursorobject.c:198 plpy_spi.c:232
+#: plpy_cursorobject.c:195 plpy_spi.c:229
#, c-format
msgid "Expected sequence of %d argument, got %d: %s"
msgid_plural "Expected sequence of %d arguments, got %d: %s"
@@ -42,33 +42,32 @@ msgstr[0] "Oczekiwano sekwencji z %d argumentem, mamy %d: %s"
msgstr[1] "Oczekiwano sekwencji z %d argumentami, mamy %d: %s"
msgstr[2] "Oczekiwano sekwencji z %d argumentami, mamy %d: %s"
-#: plpy_cursorobject.c:354
+#: plpy_cursorobject.c:350
#, c-format
msgid "iterating a closed cursor"
msgstr "iteracja zamkniętego kursora"
-#: plpy_cursorobject.c:362 plpy_cursorobject.c:427
+#: plpy_cursorobject.c:358 plpy_cursorobject.c:423
#, c-format
msgid "iterating a cursor in an aborted subtransaction"
msgstr "iteracja kursora w przerwanej podtransakcji"
-#: plpy_cursorobject.c:419
+#: plpy_cursorobject.c:415
#, c-format
msgid "fetch from a closed cursor"
msgstr "pobranie z zamkniętego kursora"
-#: plpy_cursorobject.c:467 plpy_spi.c:438
+#: plpy_cursorobject.c:463 plpy_spi.c:434
#, c-format
-#| msgid "query result has too many rows to fit in a Perl array"
msgid "query result has too many rows to fit in a Python list"
msgstr "wynik zapytania ma za dużo wierszy by pomieścić w liście Python"
-#: plpy_cursorobject.c:508
+#: plpy_cursorobject.c:504
#, c-format
msgid "closing a cursor in an aborted subtransaction"
msgstr "zamknięcie kursora w przerwanej podtransakcji"
-#: plpy_elog.c:127 plpy_elog.c:128 plpy_plpymodule.c:527
+#: plpy_elog.c:127 plpy_elog.c:128 plpy_plpymodule.c:548
#, c-format
msgid "%s"
msgstr "%s"
@@ -103,77 +102,82 @@ msgstr "błąd pobierania następnego elementu z iteratora"
msgid "PL/Python function with return type \"void\" did not return None"
msgstr "funkcja PL/Python zwracająca typ \"void\" nie zwróciła wartości None"
-#: plpy_exec.c:374 plpy_exec.c:400
+#: plpy_exec.c:379 plpy_exec.c:405
#, c-format
msgid "unexpected return value from trigger procedure"
msgstr "nieoczekiwana wartość zwracana przez procedury wyzwalacza"
-#: plpy_exec.c:375
+#: plpy_exec.c:380
#, c-format
msgid "Expected None or a string."
msgstr "Oczekiwano None lub ciąg znaków."
-#: plpy_exec.c:390
+#: plpy_exec.c:395
#, c-format
msgid "PL/Python trigger function returned \"MODIFY\" in a DELETE trigger -- ignored"
msgstr "funkcja wyzwalacza PL/Python zwróciła \"MODIFY\" w wyzwalaczu DELETE -- zignorowano"
-#: plpy_exec.c:401
+#: plpy_exec.c:406
#, c-format
msgid "Expected None, \"OK\", \"SKIP\", or \"MODIFY\"."
msgstr "Oczekiwano None, \"OK\", \"SKIP\", lub \"MODIFY\"."
-#: plpy_exec.c:482
+#: plpy_exec.c:487
#, c-format
msgid "PyList_SetItem() failed, while setting up arguments"
msgstr "nie powiodło się PyList_SetItem() podczas ustawiania argumentów"
-#: plpy_exec.c:486
+#: plpy_exec.c:491
#, c-format
msgid "PyDict_SetItemString() failed, while setting up arguments"
msgstr "nie powiodło się PyDict_SetItemString() podczas ustawiania argumentów"
-#: plpy_exec.c:498
+#: plpy_exec.c:503
#, c-format
msgid "function returning record called in context that cannot accept type record"
msgstr "funkcja zwracająca rekord w wywołaniu, które nie akceptuje typów złożonych"
-#: plpy_exec.c:714
+#: plpy_exec.c:719
#, c-format
msgid "while creating return value"
msgstr "podczas tworzenia wartości zwracanej"
-#: plpy_exec.c:738
+#: plpy_exec.c:743
#, c-format
msgid "could not create new dictionary while building trigger arguments"
msgstr "nie można utworzyć nowego słownika w czasie tworzenia argumentów wyzwalacza"
-#: plpy_exec.c:927
+#: plpy_exec.c:931
#, c-format
msgid "TD[\"new\"] deleted, cannot modify row"
msgstr "usunięto TD[\"new\"], nie można zmienić wiersza"
-#: plpy_exec.c:932
+#: plpy_exec.c:936
#, c-format
msgid "TD[\"new\"] is not a dictionary"
msgstr "TD[\"new\"] nie jest słownikiem"
-#: plpy_exec.c:957
+#: plpy_exec.c:963
#, c-format
msgid "TD[\"new\"] dictionary key at ordinal position %d is not a string"
msgstr "klucz słownika TD[\"new\"] na pozycji porządkowej %d nie jest ciągiem znaków"
-#: plpy_exec.c:964
+#: plpy_exec.c:970
#, c-format
msgid "key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row"
msgstr "klucz \"%s\" znaleziony w TD[\"new\"] nie istnieje jako kolumna w wierszu obsługiwanym przez wyzwalacz"
-#: plpy_exec.c:1044
+#: plpy_exec.c:975
+#, c-format
+msgid "cannot set system attribute \"%s\""
+msgstr "nie można ustawić atrybutu systemowego \"%s\""
+
+#: plpy_exec.c:1046
#, c-format
msgid "while modifying trigger row"
msgstr "podczas modyfikowania wiersza wyzwalacza"
-#: plpy_exec.c:1105
+#: plpy_exec.c:1107
#, c-format
msgid "forcibly aborting a subtransaction that has not been exited"
msgstr "wymuszone przerywanie podtransakcji, która nie została zakończona"
@@ -208,87 +212,88 @@ msgstr "nie można utworzyć zmiennych globalnych"
msgid "could not initialize globals"
msgstr "nie można zainicjować zmiennych globalnych"
-#: plpy_main.c:389
+#: plpy_main.c:387
#, c-format
msgid "PL/Python function \"%s\""
msgstr "funkcja PL/Python \"%s\""
-#: plpy_main.c:396
+#: plpy_main.c:394
#, c-format
msgid "PL/Python anonymous code block"
msgstr "anonimowy blok kodu PL/Python"
-#: plpy_planobject.c:123
-#, c-format
-msgid "plan.status takes no arguments"
-msgstr "plan.status nie przyjmuje żadnych argumentów"
-
-#: plpy_plpymodule.c:178 plpy_plpymodule.c:181
+#: plpy_plpymodule.c:181 plpy_plpymodule.c:184
#, c-format
msgid "could not import \"plpy\" module"
msgstr "nie można zaimportować modułu \"plpy\""
-#: plpy_plpymodule.c:196
+#: plpy_plpymodule.c:199
+#, c-format
+#| msgid "could not add the spiexceptions module"
+msgid "could not create the spiexceptions module"
+msgstr "nie udało się utworzyć modułu spiexceptions"
+
+#: plpy_plpymodule.c:207
#, c-format
msgid "could not add the spiexceptions module"
msgstr "nie udało się dodać modułu spiexceptions"
-#: plpy_plpymodule.c:217
+#: plpy_plpymodule.c:236
#, c-format
-msgid "could not create the base SPI exceptions"
-msgstr "nie można stworzyć bazowych wyjątków SPI"
+#| msgid "could not create directory \"%s\": %m"
+msgid "could not create exception \"%s\""
+msgstr "nie można utworzyć wyjątku \"%s\""
-#: plpy_plpymodule.c:252 plpy_plpymodule.c:256
+#: plpy_plpymodule.c:271 plpy_plpymodule.c:275
#, c-format
msgid "could not generate SPI exceptions"
msgstr "nie można wygenerować wyjątków SPI"
-#: plpy_plpymodule.c:422
+#: plpy_plpymodule.c:443
#, c-format
msgid "could not unpack arguments in plpy.elog"
msgstr "nie można rozpakować argumentów w plpy.elog"
-#: plpy_plpymodule.c:431
+#: plpy_plpymodule.c:452
msgid "could not parse error message in plpy.elog"
msgstr "nie można przetworzyć komunikatu błędu w plpy.elog"
-#: plpy_plpymodule.c:448
+#: plpy_plpymodule.c:469
#, c-format
msgid "Argument 'message' given by name and position"
msgstr "Argument 'message' przekazany przez nazwę i pozycję"
-#: plpy_plpymodule.c:475
+#: plpy_plpymodule.c:496
#, c-format
-#| msgid "%s: invalid argument for option %s\n"
msgid "'%s' is an invalid keyword argument for this function"
msgstr "'%s' jest niepoprawnym słowem kluczowym argumentu dla tej funkcji"
-#: plpy_plpymodule.c:486 plpy_plpymodule.c:492
+#: plpy_plpymodule.c:507 plpy_plpymodule.c:513
#, c-format
msgid "invalid SQLSTATE code"
msgstr "błędny kod SQLSTATE"
-#: plpy_procedure.c:232
+#: plpy_procedure.c:230
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "procedury wyzwalaczy mogą być wywoływane jedynie przez wyzwalacze"
-#: plpy_procedure.c:237
+#: plpy_procedure.c:235
#, c-format
msgid "PL/Python functions cannot return type %s"
msgstr "funkcje PL/Python nie mogą zwracać wartości typu %s"
-#: plpy_procedure.c:318
+#: plpy_procedure.c:316
#, c-format
msgid "PL/Python functions cannot accept type %s"
msgstr "funkcje PL/Python nie obsługują typu %s"
-#: plpy_procedure.c:414
+#: plpy_procedure.c:412
#, c-format
msgid "could not compile PL/Python function \"%s\""
msgstr "nie powiodła się kompilacja funkcji PL/Python \"%s\""
-#: plpy_procedure.c:417
+#: plpy_procedure.c:415
#, c-format
msgid "could not compile anonymous PL/Python code block"
msgstr "nie udało się skompilować anonimowego bloku kodu PL/Python"
@@ -298,57 +303,57 @@ msgstr "nie udało się skompilować anonimowego bloku kodu PL/Python"
msgid "command did not produce a result set"
msgstr "polecenie nie utworzyło zbioru wynikowego"
-#: plpy_spi.c:60
+#: plpy_spi.c:59
#, c-format
msgid "second argument of plpy.prepare must be a sequence"
msgstr "drugi argument plpy.prepare musi być sekwencją"
-#: plpy_spi.c:118
+#: plpy_spi.c:115
#, c-format
msgid "plpy.prepare: type name at ordinal position %d is not a string"
msgstr "plpy.prepare: nazwa typu na pozycji porządkowej %d nie jest ciągiem znaków"
-#: plpy_spi.c:194
+#: plpy_spi.c:191
#, c-format
msgid "plpy.execute expected a query or a plan"
msgstr "plpy.execute oczekuje kwerendy lub planu"
-#: plpy_spi.c:213
+#: plpy_spi.c:210
#, c-format
msgid "plpy.execute takes a sequence as its second argument"
msgstr "plpy.execute przyjmuje sekwencję jako drugi argument"
-#: plpy_spi.c:337
+#: plpy_spi.c:335
#, c-format
msgid "SPI_execute_plan failed: %s"
msgstr "nie powiódł się SPI_execute_plan: %s"
-#: plpy_spi.c:379
+#: plpy_spi.c:377
#, c-format
msgid "SPI_execute failed: %s"
msgstr "nie powiódł się SPI_execute: %s"
-#: plpy_subxactobject.c:123
+#: plpy_subxactobject.c:122
#, c-format
msgid "this subtransaction has already been entered"
msgstr "ta podtransakcja już została wprowadzona"
-#: plpy_subxactobject.c:129 plpy_subxactobject.c:187
+#: plpy_subxactobject.c:128 plpy_subxactobject.c:186
#, c-format
msgid "this subtransaction has already been exited"
msgstr "ta podtransakcja już została zakończona"
-#: plpy_subxactobject.c:181
+#: plpy_subxactobject.c:180
#, c-format
msgid "this subtransaction has not been entered"
msgstr "ta podtransakcja nie została wprowadzona"
-#: plpy_subxactobject.c:193
+#: plpy_subxactobject.c:192
#, c-format
msgid "there is no subtransaction to exit from"
msgstr "brak podtransakcji by z niej wyjść"
-#: plpy_typeio.c:286
+#: plpy_typeio.c:292
#, c-format
msgid "could not create new dictionary"
msgstr "nie można utworzyć nowego słownika"
@@ -368,62 +373,92 @@ msgstr "brak atrybutu Decimal w module"
msgid "conversion from numeric to Decimal failed"
msgstr "konwersja z numeric na Decimal nie powiodła się"
-#: plpy_typeio.c:645
-#, c-format
-msgid "cannot convert multidimensional array to Python list"
-msgstr "nie można skonwertować tablicy wielowymiarowej na listę Python"
-
-#: plpy_typeio.c:646
-#, c-format
-msgid "PL/Python only supports one-dimensional arrays."
-msgstr "PL/Python obsługuje tylko jednowymiarowe tablice."
-
-#: plpy_typeio.c:652
-#, c-format
-msgid "could not create new Python list"
-msgstr "nie można utworzyć nowej listy Python"
-
-#: plpy_typeio.c:711
+#: plpy_typeio.c:772
#, c-format
msgid "could not create bytes representation of Python object"
msgstr "nie można utworzyć reprezentacji bajtowej obiektu Python"
-#: plpy_typeio.c:822
+#: plpy_typeio.c:881
#, c-format
msgid "could not create string representation of Python object"
msgstr "nie można utworzyć reprezentacji znakowej obiektu Python"
-#: plpy_typeio.c:833
+#: plpy_typeio.c:892
#, c-format
msgid "could not convert Python object into cstring: Python string representation appears to contain null bytes"
msgstr "nie można zmienić obiektu Python na cstring: reprezentacja ciągu znaków Python wydaje się zawierać puste bajty"
-#: plpy_typeio.c:879
+#: plpy_typeio.c:949
+#, c-format
+msgid "malformed record literal: \"%s\""
+msgstr "nieprawidłowy literał rekordu: \"%s\""
+
+#: plpy_typeio.c:950
+#, c-format
+msgid "Missing left parenthesis."
+msgstr "Brak lewego nawiasu."
+
+#: plpy_typeio.c:951 plpy_typeio.c:1389
+#, c-format
+msgid "To return a composite type in an array, return the composite type as a Python tuple, e.g. \"[('foo')]\""
+msgstr ""
+"By zwrócić typ złożony w tablicy, zwracaj typ złożony jako krotkę Pythona, "
+"np. \"[('foo')]\""
+
+#: plpy_typeio.c:1000
+#, c-format
+#| msgid "number of array dimensions (%d) exceeds the maximum allowed (%d)"
+msgid "number of array dimensions exceeds the maximum allowed (%d)"
+msgstr "liczba wymiarów tablicy przekracza dozwolone maksimum (%d)"
+
+#: plpy_typeio.c:1004
+#, c-format
+#| msgid "cannot determine OID of function lo_truncate\n"
+msgid "cannot determine sequence length for function return value"
+msgstr ""
+"nie można ustalić długości sekwencji dla zwracanej wartości przez funkcję"
+
+#: plpy_typeio.c:1007 plpy_typeio.c:1011
+#, c-format
+#| msgid "array size exceeds the maximum allowed (%d)"
+msgid "array size exceeds the maximum allowed"
+msgstr "rozmiar tablicy przekracza dozwolone maksimum"
+
+#: plpy_typeio.c:1037
#, c-format
msgid "return value of function with array return type is not a Python sequence"
msgstr "wartość zwrócona przez funkcję zwracającą tablicę nie jest sekwencją Python"
-#: plpy_typeio.c:1000
+#: plpy_typeio.c:1090
+#, c-format
+#| msgid "multidimensional arrays must have array expressions with matching dimensions"
+msgid "multidimensional arrays must have array expressions with matching dimensions. PL/Python function return value has sequence length %d while expected %d"
+msgstr ""
+"wielowymiarowe tablice muszą mieć wyrażenia tablicowe z pasującymi "
+"wymiarami. Wartość zwracana przez funkcję PL/Python ma długość sekwencji %d "
+"gdy oczekiwano %d"
+
+#: plpy_typeio.c:1212
#, c-format
msgid "key \"%s\" not found in mapping"
msgstr "nie odnaleziono klucza \"%s\" w mapowaniu"
-#: plpy_typeio.c:1001
+#: plpy_typeio.c:1213
#, c-format
msgid "To return null in a column, add the value None to the mapping with the key named after the column."
msgstr "Aby zwrócić null w kolumnie, dodaj wartość None do mapowania z kluczem nazwanym wedle kolumny."
-#: plpy_typeio.c:1052
+#: plpy_typeio.c:1264
#, c-format
msgid "length of returned sequence did not match number of columns in row"
msgstr "długość zwróconej sekwencji nie jest równa liczbie kolumn w wierszu"
-#: plpy_typeio.c:1163
+#: plpy_typeio.c:1387
#, c-format
msgid "attribute \"%s\" does not exist in Python object"
msgstr "atrybut \"%s\" nie istnieje w obiekcie Python"
-#: plpy_typeio.c:1164
+#: plpy_typeio.c:1390
#, c-format
msgid "To return null in a column, let the returned object have an attribute named after column with value None."
msgstr "Aby zwrócić null w kolumnie, niech zwrócony obiekt posiada atrybut nazwany wedle kolumny z wartością None."
@@ -438,20 +473,35 @@ msgstr "nie można zmienić obiektu unikodowego Python na bajty"
msgid "could not extract bytes from encoded string"
msgstr "nie można wyciągnąć bajtów z kodowanego ciągu znaków"
-#~ msgid "unrecognized error in PLy_spi_execute_fetch_result"
-#~ msgstr "nierozpoznany błąd w PLy_spi_execute_fetch_result"
+#~ msgid "Python major version mismatch in session"
+#~ msgstr "niezgodna wersja główna Python w sesji"
-#~ msgid "PL/Python does not support conversion to arrays of row types."
-#~ msgstr "PL/Python nie obsługuje konwersji typów wierszowych na tablice."
+#~ msgid "This session has previously used Python major version %d, and it is now attempting to use Python major version %d."
+#~ msgstr "Ta sesja używała poprzednio Python w głównej wersji %d, teraz próbuje użyć Python w głównej wersji %d."
+
+#~ msgid "Start a new session to use a different Python major version."
+#~ msgstr "Uruchom nową sesję aby użyć innej głównej wersji Python."
#~ msgid "plpy.prepare does not support composite types"
#~ msgstr "plpy.prepare nie obsługuje typów złożonych"
-#~ msgid "Start a new session to use a different Python major version."
-#~ msgstr "Uruchom nową sesję aby użyć innej głównej wersji Python."
+#~ msgid "PL/Python does not support conversion to arrays of row types."
+#~ msgstr "PL/Python nie obsługuje konwersji typów wierszowych na tablice."
-#~ msgid "This session has previously used Python major version %d, and it is now attempting to use Python major version %d."
-#~ msgstr "Ta sesja używała poprzednio Python w głównej wersji %d, teraz próbuje użyć Python w głównej wersji %d."
+#~ msgid "unrecognized error in PLy_spi_execute_fetch_result"
+#~ msgstr "nierozpoznany błąd w PLy_spi_execute_fetch_result"
-#~ msgid "Python major version mismatch in session"
-#~ msgstr "niezgodna wersja główna Python w sesji"
+#~ msgid "could not create new Python list"
+#~ msgstr "nie można utworzyć nowej listy Python"
+
+#~ msgid "PL/Python only supports one-dimensional arrays."
+#~ msgstr "PL/Python obsługuje tylko jednowymiarowe tablice."
+
+#~ msgid "cannot convert multidimensional array to Python list"
+#~ msgstr "nie można skonwertować tablicy wielowymiarowej na listę Python"
+
+#~ msgid "could not create the base SPI exceptions"
+#~ msgstr "nie można stworzyć bazowych wyjątków SPI"
+
+#~ msgid "plan.status takes no arguments"
+#~ msgstr "plan.status nie przyjmuje żadnych argumentów"
diff --git a/src/pl/plpython/po/pt_BR.po b/src/pl/plpython/po/pt_BR.po
index 973cee5fdc..5ea66785f3 100644
--- a/src/pl/plpython/po/pt_BR.po
+++ b/src/pl/plpython/po/pt_BR.po
@@ -5,9 +5,9 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: PostgreSQL 9.5\n"
+"Project-Id-Version: PostgreSQL 9.6\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2016-02-13 17:22-0300\n"
+"POT-Creation-Date: 2016-08-09 22:53-0300\n"
"PO-Revision-Date: 2009-05-10 01:15-0300\n"
"Last-Translator: Euler Taveira de Oliveira <euler@timbira.com>\n"
"Language-Team: Brazilian Portuguese <pgbr-dev@listas.postgresql.org.br>\n"
@@ -17,214 +17,204 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n>1);\n"
-#: plpy_cursorobject.c:98
+#: plpy_cursorobject.c:101
#, c-format
msgid "plpy.cursor expected a query or a plan"
msgstr "plpy.cursor esperava uma consulta ou um plano"
-#: plpy_cursorobject.c:171
+#: plpy_cursorobject.c:179
#, c-format
msgid "plpy.cursor takes a sequence as its second argument"
msgstr "plpy.cursor tem uma sequência como seu segundo argumento"
-#: plpy_cursorobject.c:187 plpy_spi.c:217
+#: plpy_cursorobject.c:195 plpy_spi.c:229
#, c-format
msgid "could not execute plan"
msgstr "não pôde executar plano"
-#: plpy_cursorobject.c:190 plpy_spi.c:220
+#: plpy_cursorobject.c:198 plpy_spi.c:232
#, c-format
msgid "Expected sequence of %d argument, got %d: %s"
msgid_plural "Expected sequence of %d arguments, got %d: %s"
msgstr[0] "Sequência esperada de %d argumento, recebeu %d: %s"
msgstr[1] "Sequência esperada de %d argumentos, recebeu %d: %s"
-#: plpy_cursorobject.c:340
+#: plpy_cursorobject.c:354
#, c-format
msgid "iterating a closed cursor"
msgstr "iterando um cursor fechado"
-#: plpy_cursorobject.c:348 plpy_cursorobject.c:413
+#: plpy_cursorobject.c:362 plpy_cursorobject.c:427
#, c-format
msgid "iterating a cursor in an aborted subtransaction"
msgstr "iterando um cursor em uma subtransação abortada"
-#: plpy_cursorobject.c:405
+#: plpy_cursorobject.c:419
#, c-format
msgid "fetch from a closed cursor"
msgstr "busca em um cursor fechado"
-#: plpy_cursorobject.c:482
+#: plpy_cursorobject.c:467 plpy_spi.c:438
+#, c-format
+msgid "query result has too many rows to fit in a Python list"
+msgstr "resultado da consulta tem muitos registros para caber em uma lista Python"
+
+#: plpy_cursorobject.c:508
#, c-format
msgid "closing a cursor in an aborted subtransaction"
msgstr "fechando um cursor em uma subtransação abortada"
-#: plpy_elog.c:103 plpy_elog.c:104 plpy_plpymodule.c:419
+#: plpy_elog.c:127 plpy_elog.c:128 plpy_plpymodule.c:527
#, c-format
msgid "%s"
msgstr "%s"
-#: plpy_exec.c:91
+#: plpy_exec.c:140
#, c-format
msgid "unsupported set function return mode"
msgstr "modo de retorno da função que retorna conjunto não é suportado"
-#: plpy_exec.c:92
+#: plpy_exec.c:141
#, c-format
msgid "PL/Python set-returning functions only support returning one value per call."
msgstr "funções PL/Python que retornam conjunto só suportam retornar um valor por chamada."
-#: plpy_exec.c:104
+#: plpy_exec.c:154
#, c-format
msgid "returned object cannot be iterated"
msgstr "objeto retornado não pode ser iterado"
-#: plpy_exec.c:105
+#: plpy_exec.c:155
#, c-format
msgid "PL/Python set-returning functions must return an iterable object."
msgstr "funções PL/Python que retornam conjunto devem retornar um objeto iterável."
-#: plpy_exec.c:130
+#: plpy_exec.c:169
#, c-format
msgid "error fetching next item from iterator"
msgstr "erro ao buscar próximo item do iterador"
-#: plpy_exec.c:165
+#: plpy_exec.c:210
#, c-format
msgid "PL/Python function with return type \"void\" did not return None"
msgstr "função PL/Python com tipo de retorno \"void\" não retornou None"
-#: plpy_exec.c:291 plpy_exec.c:317
+#: plpy_exec.c:374 plpy_exec.c:400
#, c-format
msgid "unexpected return value from trigger procedure"
msgstr "função de gatilho retornou valor inesperado"
-#: plpy_exec.c:292
+#: plpy_exec.c:375
#, c-format
msgid "Expected None or a string."
msgstr "None ou uma cadeia de caracteres era esperado."
-#: plpy_exec.c:307
+#: plpy_exec.c:390
#, c-format
msgid "PL/Python trigger function returned \"MODIFY\" in a DELETE trigger -- ignored"
msgstr "função de gatilho PL/Python retornou \"MODIFY\" em um gatilho DELETE -- ignorado"
-#: plpy_exec.c:318
+#: plpy_exec.c:401
#, c-format
msgid "Expected None, \"OK\", \"SKIP\", or \"MODIFY\"."
msgstr "Era esperado None, \"OK\", \"SKIP\" ou \"MODIFY\"."
-#: plpy_exec.c:399
+#: plpy_exec.c:482
#, c-format
msgid "PyList_SetItem() failed, while setting up arguments"
msgstr "PyList_SetItem() falhou ao definir argumentos"
-#: plpy_exec.c:403
+#: plpy_exec.c:486
#, c-format
msgid "PyDict_SetItemString() failed, while setting up arguments"
msgstr "PyDict_SetItemString() falhou ao definir argumentos"
-#: plpy_exec.c:415
+#: plpy_exec.c:498
#, c-format
msgid "function returning record called in context that cannot accept type record"
msgstr "função que retorna record foi chamada em um contexto que não pode aceitar tipo record"
-#: plpy_exec.c:453
+#: plpy_exec.c:714
#, c-format
msgid "while creating return value"
msgstr "ao criar valor de retorno"
-#: plpy_exec.c:477
+#: plpy_exec.c:738
#, c-format
msgid "could not create new dictionary while building trigger arguments"
msgstr "não pode criar novo dicionário ao construir argumentos do gatilho"
-#: plpy_exec.c:666
+#: plpy_exec.c:927
#, c-format
msgid "TD[\"new\"] deleted, cannot modify row"
msgstr "TD[\"new\"] removido, não pode modificar registro"
-#: plpy_exec.c:671
+#: plpy_exec.c:932
#, c-format
msgid "TD[\"new\"] is not a dictionary"
msgstr "TD[\"new\"] não é um dicionário"
-#: plpy_exec.c:696
+#: plpy_exec.c:957
#, c-format
msgid "TD[\"new\"] dictionary key at ordinal position %d is not a string"
msgstr "chave do dicionário TD[\"new\"] na posição %d não é uma cadeia de caracteres"
-#: plpy_exec.c:703
+#: plpy_exec.c:964
#, c-format
msgid "key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row"
msgstr "chave \"%s\" encontrada em TD[\"new\"] não existe como uma coluna no registro do gatilho"
-#: plpy_exec.c:783
+#: plpy_exec.c:1044
#, c-format
msgid "while modifying trigger row"
msgstr "ao modificar registro de gatilho"
-#: plpy_exec.c:844
+#: plpy_exec.c:1105
#, c-format
msgid "forcibly aborting a subtransaction that has not been exited"
msgstr "forçado a abortar subtransação que não foi concluída"
-#: plpy_main.c:124
-#, c-format
-msgid "Python major version mismatch in session"
-msgstr "versão do Python não corresponde na sessão"
-
#: plpy_main.c:125
#, c-format
-msgid "This session has previously used Python major version %d, and it is now attempting to use Python major version %d."
-msgstr "Esta sessão utilizou Python versão %d, e agora está tentando utilizar Python versão %d."
-
-#: plpy_main.c:127
-#, c-format
-msgid "Start a new session to use a different Python major version."
-msgstr "Inicie uma nova sessão para utilizar uma versão diferente do Python."
-
-#: plpy_main.c:150
-#, c-format
msgid "multiple Python libraries are present in session"
msgstr "múltiplas bibliotecas do Python estão presentes na sessão"
-#: plpy_main.c:151
+#: plpy_main.c:126
#, c-format
msgid "Only one Python major version can be used in one session."
msgstr "Apenas uma versão do Python pode ser utilizada na sessão."
-#: plpy_main.c:167
+#: plpy_main.c:142
#, c-format
msgid "untrapped error in initialization"
msgstr "erro não interceptado na inicialização"
-#: plpy_main.c:190
+#: plpy_main.c:165
#, c-format
msgid "could not import \"__main__\" module"
msgstr "não pôde importar módulo \"__main__\""
-#: plpy_main.c:195
+#: plpy_main.c:170
#, c-format
msgid "could not create globals"
msgstr "não pôde criar globais"
-#: plpy_main.c:199
+#: plpy_main.c:174
#, c-format
msgid "could not initialize globals"
msgstr "não pôde inicializar globais"
-#: plpy_main.c:409
+#: plpy_main.c:389
#, c-format
msgid "PL/Python function \"%s\""
msgstr "função PL/Python \"%s\""
-#: plpy_main.c:416
+#: plpy_main.c:396
#, c-format
msgid "PL/Python anonymous code block"
msgstr "bloco de código PL/Python anônimo"
-#: plpy_planobject.c:126
+#: plpy_planobject.c:123
#, c-format
msgid "plan.status takes no arguments"
msgstr "plan.status não contém argumentos"
@@ -249,36 +239,51 @@ msgstr "não pôde criar as exceções base da SPI"
msgid "could not generate SPI exceptions"
msgstr "não pôde gerar exceções da SPI"
-#: plpy_plpymodule.c:387
+#: plpy_plpymodule.c:422
#, c-format
msgid "could not unpack arguments in plpy.elog"
msgstr "não pode desempacotar argumentos em plpy.elog"
-#: plpy_plpymodule.c:395
+#: plpy_plpymodule.c:431
msgid "could not parse error message in plpy.elog"
msgstr "não pode analisar mensagem de erro em plpy.elog"
-#: plpy_procedure.c:213
+#: plpy_plpymodule.c:448
+#, c-format
+msgid "Argument 'message' given by name and position"
+msgstr "Argumento 'message' informado por nome e posição"
+
+#: plpy_plpymodule.c:475
+#, c-format
+msgid "'%s' is an invalid keyword argument for this function"
+msgstr "'%s' é um argumento inválido para esta função"
+
+#: plpy_plpymodule.c:486 plpy_plpymodule.c:492
+#, c-format
+msgid "invalid SQLSTATE code"
+msgstr "código SQLSTATE inválido"
+
+#: plpy_procedure.c:232
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "funções de gatilho só podem ser chamadas como gatilhos"
-#: plpy_procedure.c:218
+#: plpy_procedure.c:237
#, c-format
msgid "PL/Python functions cannot return type %s"
msgstr "funções PL/Python não podem retornar tipo %s"
-#: plpy_procedure.c:300
+#: plpy_procedure.c:318
#, c-format
msgid "PL/Python functions cannot accept type %s"
msgstr "funções PL/Python não podem aceitar tipo %s"
-#: plpy_procedure.c:398
+#: plpy_procedure.c:414
#, c-format
msgid "could not compile PL/Python function \"%s\""
msgstr "não pôde compilar função PL/Python \"%s\""
-#: plpy_procedure.c:401
+#: plpy_procedure.c:417
#, c-format
msgid "could not compile anonymous PL/Python code block"
msgstr "não pôde compilar bloco de código PL/Python anônimo"
@@ -288,142 +293,142 @@ msgstr "não pôde compilar bloco de código PL/Python anônimo"
msgid "command did not produce a result set"
msgstr "comando não produziu um conjunto de resultados"
-#: plpy_spi.c:57
+#: plpy_spi.c:60
#, c-format
msgid "second argument of plpy.prepare must be a sequence"
msgstr "segundo argumento de plpy.prepare deve ser uma sequência"
-#: plpy_spi.c:106
+#: plpy_spi.c:118
#, c-format
msgid "plpy.prepare: type name at ordinal position %d is not a string"
msgstr "plpy.prepare: nome do tipo na posição %d não é uma cadeia de caracteres"
-#: plpy_spi.c:182
+#: plpy_spi.c:194
#, c-format
msgid "plpy.execute expected a query or a plan"
msgstr "plpy.execute espera uma consulta ou um plano"
-#: plpy_spi.c:201
+#: plpy_spi.c:213
#, c-format
msgid "plpy.execute takes a sequence as its second argument"
msgstr "plpy.execute recebe uma sequência como segundo argumento"
-#: plpy_spi.c:325
+#: plpy_spi.c:337
#, c-format
msgid "SPI_execute_plan failed: %s"
msgstr "SPI_execute_plan falhou: %s"
-#: plpy_spi.c:367
+#: plpy_spi.c:379
#, c-format
msgid "SPI_execute failed: %s"
msgstr "SPI_execute falhou: %s"
-#: plpy_subxactobject.c:122
+#: plpy_subxactobject.c:123
#, c-format
msgid "this subtransaction has already been entered"
msgstr "essa subtransação já foi iniciada"
-#: plpy_subxactobject.c:128 plpy_subxactobject.c:180
+#: plpy_subxactobject.c:129 plpy_subxactobject.c:187
#, c-format
msgid "this subtransaction has already been exited"
msgstr "essa subtransação já foi concluída"
-#: plpy_subxactobject.c:174
+#: plpy_subxactobject.c:181
#, c-format
msgid "this subtransaction has not been entered"
msgstr "essa subtransação não foi iniciada"
-#: plpy_subxactobject.c:186
+#: plpy_subxactobject.c:193
#, c-format
msgid "there is no subtransaction to exit from"
msgstr "não há uma subtransação a ser concluída"
-#: plpy_typeio.c:302
+#: plpy_typeio.c:286
#, c-format
msgid "could not create new dictionary"
msgstr "não pôde criar novo dicionário"
-#: plpy_typeio.c:564
+#: plpy_typeio.c:560
#, c-format
msgid "could not import a module for Decimal constructor"
msgstr "não pôde importar módulo para construtor Decimal"
-#: plpy_typeio.c:568
+#: plpy_typeio.c:564
#, c-format
msgid "no Decimal attribute in module"
msgstr "nenhum atributo Decimal no módulo"
-#: plpy_typeio.c:574
+#: plpy_typeio.c:570
#, c-format
msgid "conversion from numeric to Decimal failed"
msgstr "conversão de numeric para Decimal falhou"
-#: plpy_typeio.c:649
+#: plpy_typeio.c:645
#, c-format
msgid "cannot convert multidimensional array to Python list"
msgstr "não pode converter matriz multidimensional para lista Python"
-#: plpy_typeio.c:650
+#: plpy_typeio.c:646
#, c-format
msgid "PL/Python only supports one-dimensional arrays."
msgstr "PL/Python só suporta matrizes unidimensionais."
-#: plpy_typeio.c:656
+#: plpy_typeio.c:652
#, c-format
msgid "could not create new Python list"
msgstr "não pôde criar nova lista Python"
-#: plpy_typeio.c:715
+#: plpy_typeio.c:711
#, c-format
msgid "could not create bytes representation of Python object"
msgstr "não pôde criar representação de bytes de um objeto Python"
-#: plpy_typeio.c:820
+#: plpy_typeio.c:822
#, c-format
msgid "could not create string representation of Python object"
msgstr "não pôde criar representação de cadeia de caracteres de um objeto Python"
-#: plpy_typeio.c:831
+#: plpy_typeio.c:833
#, c-format
msgid "could not convert Python object into cstring: Python string representation appears to contain null bytes"
msgstr "não pôde converter objeto Python em cstring: representação de cadeia de caracteres Python parece conter bytes nulos"
-#: plpy_typeio.c:877
+#: plpy_typeio.c:879
#, c-format
msgid "return value of function with array return type is not a Python sequence"
msgstr "valor de retorno da função do tipo matriz retorna tipo que não é uma sequência Python"
-#: plpy_typeio.c:992
+#: plpy_typeio.c:1000
#, c-format
msgid "key \"%s\" not found in mapping"
msgstr "chave \"%s\" não foi encontrada no mapeamento"
-#: plpy_typeio.c:993
+#: plpy_typeio.c:1001
#, c-format
msgid "To return null in a column, add the value None to the mapping with the key named after the column."
msgstr "Para retornar nulo em uma coluna, adicionar o valor None no mapeamento cuja chave é o nome da coluna."
-#: plpy_typeio.c:1044
+#: plpy_typeio.c:1052
#, c-format
msgid "length of returned sequence did not match number of columns in row"
msgstr "tamanho da sequência retornada não combina com número de colunas no registro"
-#: plpy_typeio.c:1155
+#: plpy_typeio.c:1163
#, c-format
msgid "attribute \"%s\" does not exist in Python object"
msgstr "atributo \"%s\" não existe no objeto Python"
-#: plpy_typeio.c:1156
+#: plpy_typeio.c:1164
#, c-format
msgid "To return null in a column, let the returned object have an attribute named after column with value None."
msgstr "Para retornar nulo na coluna, deixe o objeto retornado ter um atributo cuja chave é o nome do coluna e o valor é None."
-#: plpy_util.c:72
+#: plpy_util.c:36
#, c-format
msgid "could not convert Python Unicode object to bytes"
msgstr "não pôde converter objeto Unicode Python para bytes"
-#: plpy_util.c:78
+#: plpy_util.c:42
#, c-format
msgid "could not extract bytes from encoded string"
msgstr "não pôde extrair bytes de cadeia de caracteres codificada"
diff --git a/src/pl/plpython/po/ru.po b/src/pl/plpython/po/ru.po
index e377e1b723..cb3a2ed3cb 100644
--- a/src/pl/plpython/po/ru.po
+++ b/src/pl/plpython/po/ru.po
@@ -1,45 +1,39 @@
# Russian message translation file for plpython
-# Copyright (C) 2012 PostgreSQL Global Development Group
+# Copyright (C) 2012-2016 PostgreSQL Global Development Group
# This file is distributed under the same license as the PostgreSQL package.
-# Alexander Lakhin <exclusion@gmail.com>, 2012.
+# Alexander Lakhin <exclusion@gmail.com>, 2012-2017.
#
-# ChangeLog:
-# - August 6, 2012: Alexander Lakhin <exclusion@gmail.com>.
-# - June 27, 2012: Updates for 9.2. Alexander Lakhin <exclusion@gmail.com>.
-# - April 3, 2012: Bug fixes. Alexander Lakhin <exclusion@gmail.com>.
-# - February 18, 2012: Complete translation for 9.1. Alexander Lakhin <exclusion@gmail.com>.
msgid ""
msgstr ""
-"Project-Id-Version: PostgreSQL 9.2\n"
+"Project-Id-Version: plpython (PostgreSQL current)\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2016-05-27 16:07+0000\n"
-"PO-Revision-Date: 2016-01-17 08:04+0300\n"
-"Last-Translator: Alexander Lakhin <exclusion@gmail.com>\n"
-"Language-Team: Russian <pgsql-translators@postgresql.org>\n"
+"POT-Creation-Date: 2017-04-02 23:37+0000\n"
+"PO-Revision-Date: 2017-03-29 13:53+0300\n"
+"Language-Team: Russian <pgsql-ru-general@postgresql.org>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-"X-Generator: Lokalize 2.0\n"
+"Last-Translator: Alexander Lakhin <exclusion@gmail.com>\n"
-#: plpy_cursorobject.c:101
+#: plpy_cursorobject.c:100
#, c-format
msgid "plpy.cursor expected a query or a plan"
msgstr "plpy.cursor ожидает запрос или план"
-#: plpy_cursorobject.c:179
+#: plpy_cursorobject.c:176
#, c-format
msgid "plpy.cursor takes a sequence as its second argument"
msgstr "plpy.cursor принимает в качестве второго аргумента последовательность"
-#: plpy_cursorobject.c:195 plpy_spi.c:229
+#: plpy_cursorobject.c:192 plpy_spi.c:226
#, c-format
msgid "could not execute plan"
msgstr "нельзя выполнить план"
-#: plpy_cursorobject.c:198 plpy_spi.c:232
+#: plpy_cursorobject.c:195 plpy_spi.c:229
#, c-format
msgid "Expected sequence of %d argument, got %d: %s"
msgid_plural "Expected sequence of %d arguments, got %d: %s"
@@ -47,33 +41,33 @@ msgstr[0] "Ожидалась последовательность из %d ар
msgstr[1] "Ожидалась последовательность из %d аргументов, получено %d: %s"
msgstr[2] "Ожидалась последовательность из %d аргументов, получено %d: %s"
-#: plpy_cursorobject.c:354
+#: plpy_cursorobject.c:350
#, c-format
msgid "iterating a closed cursor"
msgstr "перемещение закрытого курсора"
-#: plpy_cursorobject.c:362 plpy_cursorobject.c:427
+#: plpy_cursorobject.c:358 plpy_cursorobject.c:423
#, c-format
msgid "iterating a cursor in an aborted subtransaction"
msgstr "перемещение курсора в прерванной подтранзакции"
-#: plpy_cursorobject.c:419
+#: plpy_cursorobject.c:415
#, c-format
msgid "fetch from a closed cursor"
msgstr "выборка из закрытого курсора"
-#: plpy_cursorobject.c:467 plpy_spi.c:438
+#: plpy_cursorobject.c:463 plpy_spi.c:434
#, c-format
msgid "query result has too many rows to fit in a Python list"
msgstr ""
"результат запроса содержит слишком много строк для передачи в списке Python"
-#: plpy_cursorobject.c:508
+#: plpy_cursorobject.c:504
#, c-format
msgid "closing a cursor in an aborted subtransaction"
msgstr "закрытие курсора в прерванной подтранзакции"
-#: plpy_elog.c:127 plpy_elog.c:128 plpy_plpymodule.c:513
+#: plpy_elog.c:127 plpy_elog.c:128 plpy_plpymodule.c:548
#, c-format
msgid "%s"
msgstr "%s"
@@ -163,22 +157,22 @@ msgstr "при создании возвращаемого значения"
msgid "could not create new dictionary while building trigger arguments"
msgstr "не удалось создать словарь для передачи аргументов триггера"
-#: plpy_exec.c:927
+#: plpy_exec.c:926
#, c-format
msgid "TD[\"new\"] deleted, cannot modify row"
msgstr "элемент TD[\"new\"] удалён -- изменить строку нельзя"
-#: plpy_exec.c:932
+#: plpy_exec.c:931
#, c-format
msgid "TD[\"new\"] is not a dictionary"
msgstr "TD[\"new\"] - не словарь"
-#: plpy_exec.c:957
+#: plpy_exec.c:958
#, c-format
msgid "TD[\"new\"] dictionary key at ordinal position %d is not a string"
msgstr "ключ словаря TD[\"new\"] с порядковым номером %d не является строкой"
-#: plpy_exec.c:964
+#: plpy_exec.c:965
#, c-format
msgid ""
"key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering "
@@ -187,12 +181,17 @@ msgstr ""
"ключу \"%s\", найденному в TD[\"new\"], не соответствует столбец в строке, "
"обрабатываемой триггером"
-#: plpy_exec.c:1044
+#: plpy_exec.c:970
+#, c-format
+msgid "cannot set system attribute \"%s\""
+msgstr "установить системный атрибут \"%s\" нельзя"
+
+#: plpy_exec.c:1041
#, c-format
msgid "while modifying trigger row"
msgstr "при изменении строки в триггере"
-#: plpy_exec.c:1105
+#: plpy_exec.c:1102
#, c-format
msgid "forcibly aborting a subtransaction that has not been exited"
msgstr "принудительное прерывание незавершённой подтранзакции"
@@ -227,86 +226,86 @@ msgstr "не удалось создать глобальные данные"
msgid "could not initialize globals"
msgstr "не удалось инициализировать глобальные данные"
-#: plpy_main.c:389
+#: plpy_main.c:387
#, c-format
msgid "PL/Python function \"%s\""
msgstr "функция PL/Python \"%s\""
-#: plpy_main.c:396
+#: plpy_main.c:394
#, c-format
msgid "PL/Python anonymous code block"
msgstr "анонимный блок кода PL/Python"
-#: plpy_planobject.c:123
-#, c-format
-msgid "plan.status takes no arguments"
-msgstr "plan.status не принимает аргументы"
-
-#: plpy_plpymodule.c:178 plpy_plpymodule.c:181
+#: plpy_plpymodule.c:181 plpy_plpymodule.c:184
#, c-format
msgid "could not import \"plpy\" module"
msgstr "не удалось импортировать модуль \"plpy\""
-#: plpy_plpymodule.c:196
+#: plpy_plpymodule.c:199
+#, c-format
+msgid "could not create the spiexceptions module"
+msgstr "не удалось создать модуль spiexceptions"
+
+#: plpy_plpymodule.c:207
#, c-format
msgid "could not add the spiexceptions module"
msgstr "не удалось добавить модуль spiexceptions"
-#: plpy_plpymodule.c:217
+#: plpy_plpymodule.c:236
#, c-format
-msgid "could not create the base SPI exceptions"
-msgstr "не удалось создать базовые объекты исключений SPI"
+msgid "could not create exception \"%s\""
+msgstr "не удалось сгенерировать исключение \"%s\""
-#: plpy_plpymodule.c:252 plpy_plpymodule.c:256
+#: plpy_plpymodule.c:271 plpy_plpymodule.c:275
#, c-format
msgid "could not generate SPI exceptions"
msgstr "не удалось сгенерировать исключения SPI"
-#: plpy_plpymodule.c:421
+#: plpy_plpymodule.c:443
#, c-format
msgid "could not unpack arguments in plpy.elog"
msgstr "не удалось распаковать аргументы в plpy.elog"
-#: plpy_plpymodule.c:430
+#: plpy_plpymodule.c:452
msgid "could not parse error message in plpy.elog"
msgstr "не удалось разобрать сообщение об ошибке в plpy.elog"
-#: plpy_plpymodule.c:446
+#: plpy_plpymodule.c:469
#, c-format
-msgid "the message is already specified"
-msgstr "сообщение уже указано"
+msgid "Argument 'message' given by name and position"
+msgstr "Аргумент 'message' задан и по имени, и по позиции"
-#: plpy_plpymodule.c:469
+#: plpy_plpymodule.c:496
#, c-format
msgid "'%s' is an invalid keyword argument for this function"
msgstr "'%s' - недопустимое ключевое слово (аргумент) для этой функции"
-#: plpy_plpymodule.c:477 plpy_plpymodule.c:480
+#: plpy_plpymodule.c:507 plpy_plpymodule.c:513
#, c-format
msgid "invalid SQLSTATE code"
msgstr "неверный код SQLSTATE"
-#: plpy_procedure.c:232
+#: plpy_procedure.c:230
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "триггерные функции могут вызываться только в триггерах"
-#: plpy_procedure.c:237
+#: plpy_procedure.c:235
#, c-format
msgid "PL/Python functions cannot return type %s"
msgstr "функции PL/Python не могут возвращать тип %s"
-#: plpy_procedure.c:318
+#: plpy_procedure.c:316
#, c-format
msgid "PL/Python functions cannot accept type %s"
msgstr "функции PL/Python не могут принимать тип %s"
-#: plpy_procedure.c:414
+#: plpy_procedure.c:412
#, c-format
msgid "could not compile PL/Python function \"%s\""
msgstr "не удалось скомпилировать функцию PL/Python \"%s\""
-#: plpy_procedure.c:417
+#: plpy_procedure.c:415
#, c-format
msgid "could not compile anonymous PL/Python code block"
msgstr "не удалось скомпилировать анонимный блок кода PL/Python"
@@ -316,57 +315,57 @@ msgstr "не удалось скомпилировать анонимный бл
msgid "command did not produce a result set"
msgstr "команда не выдала результирующий набор"
-#: plpy_spi.c:60
+#: plpy_spi.c:59
#, c-format
msgid "second argument of plpy.prepare must be a sequence"
msgstr "вторым аргументом plpy.prepare должна быть последовательность"
-#: plpy_spi.c:118
+#: plpy_spi.c:115
#, c-format
msgid "plpy.prepare: type name at ordinal position %d is not a string"
msgstr "plpy.prepare: имя типа с порядковым номером %d не является строкой"
-#: plpy_spi.c:194
+#: plpy_spi.c:191
#, c-format
msgid "plpy.execute expected a query or a plan"
msgstr "plpy.execute ожидает запрос или план"
-#: plpy_spi.c:213
+#: plpy_spi.c:210
#, c-format
msgid "plpy.execute takes a sequence as its second argument"
msgstr "plpy.execute принимает в качестве второго аргумента последовательность"
-#: plpy_spi.c:337
+#: plpy_spi.c:335
#, c-format
msgid "SPI_execute_plan failed: %s"
msgstr "ошибка в SPI_execute_plan: %s"
-#: plpy_spi.c:379
+#: plpy_spi.c:377
#, c-format
msgid "SPI_execute failed: %s"
msgstr "ошибка в SPI_execute: %s"
-#: plpy_subxactobject.c:123
+#: plpy_subxactobject.c:122
#, c-format
msgid "this subtransaction has already been entered"
msgstr "эта подтранзакция уже начата"
-#: plpy_subxactobject.c:129 plpy_subxactobject.c:187
+#: plpy_subxactobject.c:128 plpy_subxactobject.c:186
#, c-format
msgid "this subtransaction has already been exited"
msgstr "эта подтранзакция уже закончена"
-#: plpy_subxactobject.c:181
+#: plpy_subxactobject.c:180
#, c-format
msgid "this subtransaction has not been entered"
msgstr "эта подтранзакция ещё не начата"
-#: plpy_subxactobject.c:193
+#: plpy_subxactobject.c:192
#, c-format
msgid "there is no subtransaction to exit from"
msgstr "нет подтранзакции, которую нужно закончить"
-#: plpy_typeio.c:286
+#: plpy_typeio.c:292
#, c-format
msgid "could not create new dictionary"
msgstr "не удалось создать словарь"
@@ -386,32 +385,17 @@ msgstr "в модуле нет атрибута Decimal"
msgid "conversion from numeric to Decimal failed"
msgstr "не удалось преобразовать numeric в Decimal"
-#: plpy_typeio.c:645
-#, c-format
-msgid "cannot convert multidimensional array to Python list"
-msgstr "преобразовать многомерный массив в список Python нельзя"
-
-#: plpy_typeio.c:646
-#, c-format
-msgid "PL/Python only supports one-dimensional arrays."
-msgstr "PL/Python поддерживает только одномерные массивы."
-
-#: plpy_typeio.c:652
-#, c-format
-msgid "could not create new Python list"
-msgstr "не удалось создать список Python"
-
-#: plpy_typeio.c:711
+#: plpy_typeio.c:772
#, c-format
msgid "could not create bytes representation of Python object"
msgstr "не удалось создать байтовое представление объекта Python"
-#: plpy_typeio.c:822
+#: plpy_typeio.c:881
#, c-format
msgid "could not create string representation of Python object"
msgstr "не удалось создать строковое представление объекта Python"
-#: plpy_typeio.c:833
+#: plpy_typeio.c:892
#, c-format
msgid ""
"could not convert Python object into cstring: Python string representation "
@@ -420,7 +404,43 @@ msgstr ""
"не удалось преобразовать объект Python в cstring: похоже, представление "
"строки Python содержит нулевые байты"
-#: plpy_typeio.c:879
+#: plpy_typeio.c:949
+#, c-format
+msgid "malformed record literal: \"%s\""
+msgstr "ошибка в литерале записи: \"%s\""
+
+#: plpy_typeio.c:950
+#, c-format
+msgid "Missing left parenthesis."
+msgstr "Отсутствует левая скобка."
+
+#: plpy_typeio.c:951 plpy_typeio.c:1389
+#, c-format
+msgid ""
+"To return a composite type in an array, return the composite type as a "
+"Python tuple, e.g. \"[('foo')]\""
+msgstr ""
+"Чтобы возвратить составной тип в массиве, нужно возвратить составное "
+"значение в виде кортежа Python, например: \"[('foo')]\""
+
+#: plpy_typeio.c:1000
+#, c-format
+msgid "number of array dimensions exceeds the maximum allowed (%d)"
+msgstr "число размерностей массива превышает предел (%d)"
+
+#: plpy_typeio.c:1004
+#, c-format
+msgid "cannot determine sequence length for function return value"
+msgstr ""
+"не удалось определить длину последовательности в возвращаемом функцией "
+"значении"
+
+#: plpy_typeio.c:1007 plpy_typeio.c:1011
+#, c-format
+msgid "array size exceeds the maximum allowed"
+msgstr "размер массива превышает предел"
+
+#: plpy_typeio.c:1037
#, c-format
msgid ""
"return value of function with array return type is not a Python sequence"
@@ -428,12 +448,23 @@ msgstr ""
"возвращаемое значение функции с результатом-массивом не является "
"последовательностью"
-#: plpy_typeio.c:1000
+#: plpy_typeio.c:1090
+#, c-format
+msgid ""
+"multidimensional arrays must have array expressions with matching "
+"dimensions. PL/Python function return value has sequence length %d while "
+"expected %d"
+msgstr ""
+"для многомерных массивов должны задаваться выражения с соответствующими "
+"размерностями. В возвращаемом функцией на PL/Python значении "
+"последовательность имеет длину %d (а ожидалось %d)"
+
+#: plpy_typeio.c:1212
#, c-format
msgid "key \"%s\" not found in mapping"
msgstr "ключ \"%s\" не найден в сопоставлении"
-#: plpy_typeio.c:1001
+#: plpy_typeio.c:1213
#, c-format
msgid ""
"To return null in a column, add the value None to the mapping with the key "
@@ -442,17 +473,17 @@ msgstr ""
"Чтобы присвоить столбцу NULL, добавьте в сопоставление значение None с "
"ключом-именем столбца."
-#: plpy_typeio.c:1052
+#: plpy_typeio.c:1264
#, c-format
msgid "length of returned sequence did not match number of columns in row"
msgstr "длина возвращённой последовательности не равна числу столбцов в строке"
-#: plpy_typeio.c:1163
+#: plpy_typeio.c:1387
#, c-format
msgid "attribute \"%s\" does not exist in Python object"
msgstr "в объекте Python не существует атрибут \"%s\""
-#: plpy_typeio.c:1164
+#: plpy_typeio.c:1390
#, c-format
msgid ""
"To return null in a column, let the returned object have an attribute named "
@@ -471,6 +502,24 @@ msgstr "не удалось преобразовать объект Python Unico
msgid "could not extract bytes from encoded string"
msgstr "не удалось извлечь байты из кодированной строки"
+#~ msgid "plan.status takes no arguments"
+#~ msgstr "plan.status не принимает аргументы"
+
+#~ msgid "cannot convert multidimensional array to Python list"
+#~ msgstr "преобразовать многомерный массив в список Python нельзя"
+
+#~ msgid "PL/Python only supports one-dimensional arrays."
+#~ msgstr "PL/Python поддерживает только одномерные массивы."
+
+#~ msgid "could not create new Python list"
+#~ msgstr "не удалось создать список Python"
+
+#~ msgid "could not create the base SPI exceptions"
+#~ msgstr "не удалось создать базовые объекты исключений SPI"
+
+#~ msgid "the message is already specified"
+#~ msgstr "сообщение уже указано"
+
#~ msgid "Python major version mismatch in session"
#~ msgstr "несовпадение базовой версии Python в сеансе"
diff --git a/src/pl/plpython/sql/plpython_composite.sql b/src/pl/plpython/sql/plpython_composite.sql
index 473342c2f4..0fd2f5d5e3 100644
--- a/src/pl/plpython/sql/plpython_composite.sql
+++ b/src/pl/plpython/sql/plpython_composite.sql
@@ -169,13 +169,13 @@ SELECT * FROM changing_test();
-- tables of composite types
CREATE FUNCTION composite_types_table(OUT tab table_record[], OUT typ type_record[] ) RETURNS SETOF record AS $$
-yield {'tab': [['first', 1], ['second', 2]],
+yield {'tab': [('first', 1), ('second', 2)],
'typ': [{'first': 'third', 'second': 3},
{'first': 'fourth', 'second': 4}]}
-yield {'tab': [['first', 1], ['second', 2]],
+yield {'tab': [('first', 1), ('second', 2)],
'typ': [{'first': 'third', 'second': 3},
{'first': 'fourth', 'second': 4}]}
-yield {'tab': [['first', 1], ['second', 2]],
+yield {'tab': [('first', 1), ('second', 2)],
'typ': [{'first': 'third', 'second': 3},
{'first': 'fourth', 'second': 4}]}
$$ LANGUAGE plpythonu;
@@ -207,3 +207,18 @@ SELECT * FROM return_record_2('v4') AS (v1 int, v3 int, v2 int);
-- works
SELECT * FROM return_record_2('v3') AS (v1 int, v3 int, v2 int);
SELECT * FROM return_record_2('v3') AS (v1 int, v2 int, v3 int);
+
+-- multi-dimensional array of composite types.
+CREATE FUNCTION composite_type_as_list() RETURNS type_record[] AS $$
+ return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]];
+$$ LANGUAGE plpythonu;
+SELECT * FROM composite_type_as_list();
+
+-- Starting with PostgreSQL 10, a composite type in an array cannot be
+-- represented as a Python list, because it's ambiguous with multi-dimensional
+-- arrays. So this throws an error now. The error should contain a useful hint
+-- on the issue.
+CREATE FUNCTION composite_type_as_list_broken() RETURNS type_record[] AS $$
+ return [['first', 1]];
+$$ LANGUAGE plpythonu;
+SELECT * FROM composite_type_as_list_broken();
diff --git a/src/pl/plpython/sql/plpython_ereport.sql b/src/pl/plpython/sql/plpython_ereport.sql
index 2612e93387..889293d33c 100644
--- a/src/pl/plpython/sql/plpython_ereport.sql
+++ b/src/pl/plpython/sql/plpython_ereport.sql
@@ -55,12 +55,8 @@ kwargs = {
"column_name": _column_name, "datatype_name": _datatype_name,
"constraint_name": _constraint_name
}
-# ignore None values - should work on Python2.3
-dict = {}
-for k in kwargs:
- if kwargs[k] is not None:
- dict[k] = kwargs[k]
-plpy.error(**dict)
+# ignore None values
+plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
$$ LANGUAGE plpythonu;
SELECT raise_exception('hello', 'world');
diff --git a/src/pl/plpython/sql/plpython_spi.sql b/src/pl/plpython/sql/plpython_spi.sql
index 87170609da..fcf049cb66 100644
--- a/src/pl/plpython/sql/plpython_spi.sql
+++ b/src/pl/plpython/sql/plpython_spi.sql
@@ -37,6 +37,20 @@ return None
'
LANGUAGE plpythonu;
+CREATE FUNCTION spi_prepared_plan_test_two(a text) RETURNS text
+ AS
+'if "myplan" not in SD:
+ q = "SELECT count(*) FROM users WHERE lname = $1"
+ SD["myplan"] = plpy.prepare(q, [ "text" ])
+try:
+ rv = SD["myplan"].execute([a])
+ return "there are " + str(rv[0]["count"]) + " " + str(a) + "s"
+except Exception, ex:
+ plpy.error(str(ex))
+return None
+'
+ LANGUAGE plpythonu;
+
CREATE FUNCTION spi_prepared_plan_test_nested(a text) RETURNS text
AS
'if "myplan" not in SD:
@@ -79,7 +93,7 @@ return a + r
--
select nested_call_one('pass this along');
select spi_prepared_plan_test_one('doe');
-select spi_prepared_plan_test_one('smith');
+select spi_prepared_plan_test_two('smith');
select spi_prepared_plan_test_nested('smith');
SELECT join_sequences(sequences) FROM sequences;
@@ -135,8 +149,8 @@ SELECT result_len_test($$UPDATE foo3 SET b= '' WHERE a = 2$$);
CREATE FUNCTION result_subscript_test() RETURNS void
AS $$
-result = plpy.execute("SELECT 1 AS c UNION SELECT 2 "
- "UNION SELECT 3 UNION SELECT 4")
+result = plpy.execute("SELECT 1 AS c UNION ALL SELECT 2 "
+ "UNION ALL SELECT 3 UNION ALL SELECT 4")
plpy.info(result[1]['c'])
plpy.info(result[-1]['c'])
@@ -275,7 +289,7 @@ plan = plpy.prepare(
["text"])
for row in plpy.cursor(plan, ["w"]):
yield row['fname']
-for row in plpy.cursor(plan, ["j"]):
+for row in plan.cursor(["j"]):
yield row['fname']
$$ LANGUAGE plpythonu;
diff --git a/src/pl/plpython/sql/plpython_test.sql b/src/pl/plpython/sql/plpython_test.sql
index fa3c465ef8..5f1be9c94a 100644
--- a/src/pl/plpython/sql/plpython_test.sql
+++ b/src/pl/plpython/sql/plpython_test.sql
@@ -27,11 +27,11 @@ select "Argument test #1"(users, fname, lname) from users where lname = 'doe' or
-- check module contents
-CREATE FUNCTION module_contents() RETURNS text AS
+CREATE FUNCTION module_contents() RETURNS SETOF text AS
$$
contents = list(filter(lambda x: not x.startswith("__"), dir(plpy)))
contents.sort()
-return ", ".join(contents)
+return contents
$$ LANGUAGE plpythonu;
select module_contents();
diff --git a/src/pl/plpython/sql/plpython_trigger.sql b/src/pl/plpython/sql/plpython_trigger.sql
index a054fe729b..79c24b714b 100644
--- a/src/pl/plpython/sql/plpython_trigger.sql
+++ b/src/pl/plpython/sql/plpython_trigger.sql
@@ -406,3 +406,27 @@ SELECT * FROM a;
DROP TABLE a;
INSERT INTO b DEFAULT VALUES;
SELECT * FROM b;
+
+-- check that SQL run in trigger code can see transition tables
+
+CREATE TABLE transition_table_test (id int, name text);
+INSERT INTO transition_table_test VALUES (1, 'a');
+
+CREATE FUNCTION transition_table_test_f() RETURNS trigger LANGUAGE plpythonu AS
+$$
+ rv = plpy.execute("SELECT * FROM old_table")
+ assert(rv.nrows() == 1)
+ plpy.info("old: " + str(rv[0]["id"]) + " -> " + rv[0]["name"])
+ rv = plpy.execute("SELECT * FROM new_table")
+ assert(rv.nrows() == 1)
+ plpy.info("new: " + str(rv[0]["id"]) + " -> " + rv[0]["name"])
+ return None
+$$;
+
+CREATE TRIGGER a_t AFTER UPDATE ON transition_table_test
+ REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
+ FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_test_f();
+UPDATE transition_table_test SET name = 'b';
+
+DROP TABLE transition_table_test;
+DROP FUNCTION transition_table_test_f();
diff --git a/src/pl/plpython/sql/plpython_types.sql b/src/pl/plpython/sql/plpython_types.sql
index 19d920d3c7..8c57297c24 100644
--- a/src/pl/plpython/sql/plpython_types.sql
+++ b/src/pl/plpython/sql/plpython_types.sql
@@ -237,14 +237,74 @@ SELECT * FROM test_type_conversion_array_int4(ARRAY[NULL,1]);
SELECT * FROM test_type_conversion_array_int4(ARRAY[]::integer[]);
SELECT * FROM test_type_conversion_array_int4(NULL);
SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
+SELECT * FROM test_type_conversion_array_int4(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]);
+SELECT * FROM test_type_conversion_array_int4('[2:4]={1,2,3}');
+
+CREATE FUNCTION test_type_conversion_array_int8(x int8[]) RETURNS int8[] AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+
+SELECT * FROM test_type_conversion_array_int8(ARRAY[[[1,2,NULL],[NULL,5,6]],[[NULL,8,9],[10,11,12]]]::int8[]);
+
+CREATE FUNCTION test_type_conversion_array_date(x date[]) RETURNS date[] AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+
+SELECT * FROM test_type_conversion_array_date(ARRAY[[['2016-09-21','2016-09-22',NULL],[NULL,'2016-10-21','2016-10-22']],
+ [[NULL,'2016-11-21','2016-10-21'],['2015-09-21','2015-09-22','2014-09-21']]]::date[]);
+
+CREATE FUNCTION test_type_conversion_array_timestamp(x timestamp[]) RETURNS timestamp[] AS $$
+plpy.info(x, type(x))
+return x
+$$ LANGUAGE plpythonu;
+
+SELECT * FROM test_type_conversion_array_timestamp(ARRAY[[['2016-09-21 15:34:24.078792-04','2016-10-22 11:34:24.078795-04',NULL],
+ [NULL,'2016-10-21 11:34:25.078792-04','2016-10-21 11:34:24.098792-04']],
+ [[NULL,'2016-01-21 11:34:24.078792-04','2016-11-21 11:34:24.108792-04'],
+ ['2015-09-21 11:34:24.079792-04','2014-09-21 11:34:24.078792-04','2013-09-21 11:34:24.078792-04']]]::timestamp[]);
+CREATE OR REPLACE FUNCTION pyreturnmultidemint4(h int4, i int4, j int4, k int4 ) RETURNS int4[] AS $BODY$
+m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
+plpy.info(m, type(m))
+return m
+$BODY$ LANGUAGE plpythonu;
+
+select pyreturnmultidemint4(8,5,3,2);
+
+CREATE OR REPLACE FUNCTION pyreturnmultidemint8(h int4, i int4, j int4, k int4 ) RETURNS int8[] AS $BODY$
+m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
+plpy.info(m, type(m))
+return m
+$BODY$ LANGUAGE plpythonu;
+
+select pyreturnmultidemint8(5,5,3,2);
+
+CREATE OR REPLACE FUNCTION pyreturnmultidemfloat4(h int4, i int4, j int4, k int4 ) RETURNS float4[] AS $BODY$
+m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
+plpy.info(m, type(m))
+return m
+$BODY$ LANGUAGE plpythonu;
+
+select pyreturnmultidemfloat4(6,5,3,2);
+
+CREATE OR REPLACE FUNCTION pyreturnmultidemfloat8(h int4, i int4, j int4, k int4 ) RETURNS float8[] AS $BODY$
+m = [[[[x for x in range(h)] for y in range(i)] for z in range(j)] for w in range(k)]
+plpy.info(m, type(m))
+return m
+$BODY$ LANGUAGE plpythonu;
+
+select pyreturnmultidemfloat8(7,5,3,2);
+
CREATE FUNCTION test_type_conversion_array_text(x text[]) RETURNS text[] AS $$
plpy.info(x, type(x))
return x
$$ LANGUAGE plpythonu;
SELECT * FROM test_type_conversion_array_text(ARRAY['foo', 'bar']);
+SELECT * FROM test_type_conversion_array_text(ARRAY[['foo', 'bar'],['foo2', 'bar2']]);
CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
@@ -268,6 +328,18 @@ $$ LANGUAGE plpythonu;
SELECT * FROM test_type_conversion_array_mixed2();
+CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
+return [[1,2,3],[4,5]]
+$$ LANGUAGE plpythonu;
+
+SELECT * FROM test_type_conversion_mdarray_malformed();
+
+CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
+return [[[[[[[1]]]]]]]
+$$ LANGUAGE plpythonu;
+
+SELECT * FROM test_type_conversion_mdarray_toodeep();
+
CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}]
diff --git a/src/pl/tcl/Makefile b/src/pl/tcl/Makefile
index 25082ec504..b8971d3cc8 100644
--- a/src/pl/tcl/Makefile
+++ b/src/pl/tcl/Makefile
@@ -1,6 +1,6 @@
#-------------------------------------------------------------------------
#
-# Makefile for the pl/tcl procedural language
+# Makefile for the PL/Tcl procedural language
#
# src/pl/tcl/Makefile
#
@@ -28,7 +28,7 @@ DATA = pltcl.control pltcl--1.0.sql pltcl--unpackaged--1.0.sql \
pltclu.control pltclu--1.0.sql pltclu--unpackaged--1.0.sql
REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-extension=pltcl
-REGRESS = pltcl_setup pltcl_queries pltcl_unicode
+REGRESS = pltcl_setup pltcl_queries pltcl_start_proc pltcl_subxact pltcl_unicode
# Tcl on win32 ships with import libraries only for Microsoft Visual C++,
# which are not compatible with mingw gcc. Therefore we need to build a
@@ -53,7 +53,6 @@ include $(top_srcdir)/src/Makefile.shlib
all: all-lib
- $(MAKE) -C modules $@
# Force this dependency to be known even without dependency info built:
pltcl.o: pltclerrcodes.h
@@ -65,14 +64,11 @@ pltclerrcodes.h: $(top_srcdir)/src/backend/utils/errcodes.txt generate-pltclerrc
distprep: pltclerrcodes.h
install: all install-lib install-data
- $(MAKE) -C modules $@
installdirs: installdirs-lib
$(MKDIR_P) '$(DESTDIR)$(datadir)/extension'
- $(MAKE) -C modules $@
uninstall: uninstall-lib uninstall-data
- $(MAKE) -C modules $@
install-data: installdirs
$(INSTALL_DATA) $(addprefix $(srcdir)/, $(DATA)) '$(DESTDIR)$(datadir)/extension/'
@@ -100,7 +96,6 @@ clean distclean: clean-lib
ifeq ($(PORTNAME), win32)
rm -f $(tclwithver).def
endif
- $(MAKE) -C modules $@
maintainer-clean: distclean
rm -f pltclerrcodes.h
diff --git a/src/pl/tcl/expected/pltcl_queries.out b/src/pl/tcl/expected/pltcl_queries.out
index 6cb1fdbb61..5f50f46887 100644
--- a/src/pl/tcl/expected/pltcl_queries.out
+++ b/src/pl/tcl/expected/pltcl_queries.out
@@ -185,12 +185,23 @@ select * from T_pkey2 order by key1 using @<, key2 collate "C";
-- show dump of trigger data
insert into trigger_test values(1,'insert');
-NOTICE: NEW: {i: 1, v: insert}
+NOTICE: NEW: {}
+NOTICE: OLD: {}
+NOTICE: TG_level: STATEMENT
+NOTICE: TG_name: statement_trigger
+NOTICE: TG_op: INSERT
+NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull}
+NOTICE: TG_relid: bogus:12345
+NOTICE: TG_table_name: trigger_test
+NOTICE: TG_table_schema: public
+NOTICE: TG_when: BEFORE
+NOTICE: args: {42 {statement trigger}}
+NOTICE: NEW: {i: 1, test_argisnull: f, test_return_null: f, test_skip: f, v: insert}
NOTICE: OLD: {}
NOTICE: TG_level: ROW
NOTICE: TG_name: show_trigger_data_trig
NOTICE: TG_op: INSERT
-NOTICE: TG_relatts: {{} i v}
+NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull}
NOTICE: TG_relid: bogus:12345
NOTICE: TG_table_name: trigger_test
NOTICE: TG_table_schema: public
@@ -232,13 +243,37 @@ NOTICE: TG_table_name: trigger_test_view
NOTICE: TG_table_schema: public
NOTICE: TG_when: {INSTEAD OF}
NOTICE: args: {24 {skidoo view}}
+update trigger_test set v = 'update', test_skip=true where i = 1;
+NOTICE: NEW: {}
+NOTICE: OLD: {}
+NOTICE: TG_level: STATEMENT
+NOTICE: TG_name: statement_trigger
+NOTICE: TG_op: UPDATE
+NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull}
+NOTICE: TG_relid: bogus:12345
+NOTICE: TG_table_name: trigger_test
+NOTICE: TG_table_schema: public
+NOTICE: TG_when: BEFORE
+NOTICE: args: {42 {statement trigger}}
+NOTICE: SKIPPING OPERATION UPDATE
update trigger_test set v = 'update' where i = 1;
-NOTICE: NEW: {i: 1, v: update}
-NOTICE: OLD: {i: 1, v: insert}
+NOTICE: NEW: {}
+NOTICE: OLD: {}
+NOTICE: TG_level: STATEMENT
+NOTICE: TG_name: statement_trigger
+NOTICE: TG_op: UPDATE
+NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull}
+NOTICE: TG_relid: bogus:12345
+NOTICE: TG_table_name: trigger_test
+NOTICE: TG_table_schema: public
+NOTICE: TG_when: BEFORE
+NOTICE: args: {42 {statement trigger}}
+NOTICE: NEW: {i: 1, test_argisnull: f, test_return_null: f, test_skip: f, v: update}
+NOTICE: OLD: {i: 1, test_argisnull: f, test_return_null: f, test_skip: f, v: insert}
NOTICE: TG_level: ROW
NOTICE: TG_name: show_trigger_data_trig
NOTICE: TG_op: UPDATE
-NOTICE: TG_relatts: {{} i v}
+NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull}
NOTICE: TG_relid: bogus:12345
NOTICE: TG_table_name: trigger_test
NOTICE: TG_table_schema: public
@@ -246,16 +281,39 @@ NOTICE: TG_when: BEFORE
NOTICE: args: {23 skidoo}
delete from trigger_test;
NOTICE: NEW: {}
-NOTICE: OLD: {i: 1, v: update}
+NOTICE: OLD: {}
+NOTICE: TG_level: STATEMENT
+NOTICE: TG_name: statement_trigger
+NOTICE: TG_op: DELETE
+NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull}
+NOTICE: TG_relid: bogus:12345
+NOTICE: TG_table_name: trigger_test
+NOTICE: TG_table_schema: public
+NOTICE: TG_when: BEFORE
+NOTICE: args: {42 {statement trigger}}
+NOTICE: NEW: {}
+NOTICE: OLD: {i: 1, test_argisnull: f, test_return_null: f, test_skip: f, v: update}
NOTICE: TG_level: ROW
NOTICE: TG_name: show_trigger_data_trig
NOTICE: TG_op: DELETE
-NOTICE: TG_relatts: {{} i v}
+NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull}
NOTICE: TG_relid: bogus:12345
NOTICE: TG_table_name: trigger_test
NOTICE: TG_table_schema: public
NOTICE: TG_when: BEFORE
NOTICE: args: {23 skidoo}
+truncate trigger_test;
+NOTICE: NEW: {}
+NOTICE: OLD: {}
+NOTICE: TG_level: STATEMENT
+NOTICE: TG_name: statement_trigger
+NOTICE: TG_op: TRUNCATE
+NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull}
+NOTICE: TG_relid: bogus:12345
+NOTICE: TG_table_name: trigger_test
+NOTICE: TG_table_schema: public
+NOTICE: TG_when: BEFORE
+NOTICE: args: {42 {statement trigger}}
-- Test composite-type arguments
select tcl_composite_arg_ref1(row('tkey', 42, 'ref2'));
tcl_composite_arg_ref1
@@ -288,6 +346,22 @@ select tcl_argisnull(null);
t
(1 row)
+-- should error
+insert into trigger_test(test_argisnull) values(true);
+NOTICE: NEW: {}
+NOTICE: OLD: {}
+NOTICE: TG_level: STATEMENT
+NOTICE: TG_name: statement_trigger
+NOTICE: TG_op: INSERT
+NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull}
+NOTICE: TG_relid: bogus:12345
+NOTICE: TG_table_name: trigger_test
+NOTICE: TG_table_schema: public
+NOTICE: TG_when: BEFORE
+NOTICE: args: {42 {statement trigger}}
+ERROR: argisnull cannot be used in triggers
+select trigger_data();
+ERROR: trigger functions can only be called as triggers
-- Test spi_lastoid primitive
create temp table t1 (f1 int);
select tcl_lastoid('t1');
@@ -303,3 +377,307 @@ select tcl_lastoid('t2') > 0;
t
(1 row)
+-- test some error cases
+create function tcl_error(out a int, out b int) as $$return {$$ language pltcl;
+select tcl_error();
+ERROR: missing close-brace
+create function bad_record(out a text, out b text) as $$return [list a]$$ language pltcl;
+select bad_record();
+ERROR: column name/value list must have even number of elements
+create function bad_field(out a text, out b text) as $$return [list a 1 b 2 cow 3]$$ language pltcl;
+select bad_field();
+ERROR: column name/value list contains nonexistent column name "cow"
+-- test compound return
+select * from tcl_test_cube_squared(5);
+ squared | cubed
+---------+-------
+ 25 | 125
+(1 row)
+
+-- test SRF
+select * from tcl_test_squared_rows(0,5);
+ x | y
+---+----
+ 0 | 0
+ 1 | 1
+ 2 | 4
+ 3 | 9
+ 4 | 16
+(5 rows)
+
+select * from tcl_test_sequence(0,5) as a;
+ a
+---
+ 0
+ 1
+ 2
+ 3
+ 4
+(5 rows)
+
+select 1, tcl_test_sequence(0,5);
+ ?column? | tcl_test_sequence
+----------+-------------------
+ 1 | 0
+ 1 | 1
+ 1 | 2
+ 1 | 3
+ 1 | 4
+(5 rows)
+
+create function non_srf() returns int as $$return_next 1$$ language pltcl;
+select non_srf();
+ERROR: return_next cannot be used in non-set-returning functions
+create function bad_record_srf(out a text, out b text) returns setof record as $$
+return_next [list a]
+$$ language pltcl;
+select bad_record_srf();
+ERROR: column name/value list must have even number of elements
+create function bad_field_srf(out a text, out b text) returns setof record as $$
+return_next [list a 1 b 2 cow 3]
+$$ language pltcl;
+select bad_field_srf();
+ERROR: column name/value list contains nonexistent column name "cow"
+-- test quote
+select tcl_eval('quote foo bar');
+ERROR: wrong # args: should be "quote string"
+select tcl_eval('quote [format %c 39]');
+ tcl_eval
+----------
+ ''
+(1 row)
+
+select tcl_eval('quote [format %c 92]');
+ tcl_eval
+----------
+ \\
+(1 row)
+
+-- Test argisnull
+select tcl_eval('argisnull');
+ERROR: wrong # args: should be "argisnull argno"
+select tcl_eval('argisnull 14');
+ERROR: argno out of range
+select tcl_eval('argisnull abc');
+ERROR: expected integer but got "abc"
+-- Test return_null
+select tcl_eval('return_null 14');
+ERROR: wrong # args: should be "return_null "
+-- should error
+insert into trigger_test(test_return_null) values(true);
+NOTICE: NEW: {}
+NOTICE: OLD: {}
+NOTICE: TG_level: STATEMENT
+NOTICE: TG_name: statement_trigger
+NOTICE: TG_op: INSERT
+NOTICE: TG_relatts: {{} i v {} test_skip test_return_null test_argisnull}
+NOTICE: TG_relid: bogus:12345
+NOTICE: TG_table_name: trigger_test
+NOTICE: TG_table_schema: public
+NOTICE: TG_when: BEFORE
+NOTICE: args: {42 {statement trigger}}
+ERROR: return_null cannot be used in triggers
+-- Test spi_exec
+select tcl_eval('spi_exec');
+ERROR: wrong # args: should be "spi_exec ?-count n? ?-array name? query ?loop body?"
+select tcl_eval('spi_exec -count');
+ERROR: missing argument to -count or -array
+select tcl_eval('spi_exec -array');
+ERROR: missing argument to -count or -array
+select tcl_eval('spi_exec -count abc');
+ERROR: expected integer but got "abc"
+select tcl_eval('spi_exec query loop body toomuch');
+ERROR: wrong # args: should be "query ?loop body?"
+select tcl_eval('spi_exec "begin; rollback;"');
+ERROR: pltcl: SPI_execute failed: SPI_ERROR_TRANSACTION
+-- Test spi_execp
+select tcl_eval('spi_execp');
+ERROR: missing argument to -count or -array
+select tcl_eval('spi_execp -count');
+ERROR: missing argument to -array, -count or -nulls
+select tcl_eval('spi_execp -array');
+ERROR: missing argument to -array, -count or -nulls
+select tcl_eval('spi_execp -count abc');
+ERROR: expected integer but got "abc"
+select tcl_eval('spi_execp -nulls');
+ERROR: missing argument to -array, -count or -nulls
+select tcl_eval('spi_execp ""');
+ERROR: invalid queryid ''
+-- test spi_prepare
+select tcl_eval('spi_prepare');
+ERROR: wrong # args: should be "spi_prepare query argtypes"
+select tcl_eval('spi_prepare a b');
+ERROR: type "b" does not exist
+select tcl_eval('spi_prepare a "b {"');
+ERROR: unmatched open brace in list
+select tcl_error_handling_test($tcl$spi_prepare "select moo" []$tcl$);
+ tcl_error_handling_test
+--------------------------------------
+ SQLSTATE: 42703 +
+ condition: undefined_column +
+ cursor_position: 8 +
+ message: column "moo" does not exist+
+ statement: select moo
+(1 row)
+
+-- test full error text
+select tcl_error_handling_test($tcl$
+spi_exec "DO $$
+BEGIN
+RAISE 'my message'
+ USING HINT = 'my hint'
+ , DETAIL = 'my detail'
+ , SCHEMA = 'my schema'
+ , TABLE = 'my table'
+ , COLUMN = 'my column'
+ , CONSTRAINT = 'my constraint'
+ , DATATYPE = 'my datatype'
+;
+END$$;"
+$tcl$);
+ tcl_error_handling_test
+--------------------------------------------------------------
+ SQLSTATE: P0001 +
+ column: my column +
+ condition: raise_exception +
+ constraint: my constraint +
+ context: PL/pgSQL function inline_code_block line 3 at RAISE+
+ SQL statement "DO $$ +
+ BEGIN +
+ RAISE 'my message' +
+ USING HINT = 'my hint' +
+ , DETAIL = 'my detail' +
+ , SCHEMA = 'my schema' +
+ , TABLE = 'my table' +
+ , COLUMN = 'my column' +
+ , CONSTRAINT = 'my constraint' +
+ , DATATYPE = 'my datatype' +
+ ; +
+ END$$;" +
+ datatype: my datatype +
+ detail: my detail +
+ hint: my hint +
+ message: my message +
+ schema: my schema +
+ table: my table
+(1 row)
+
+-- verify tcl_error_handling_test() properly reports non-postgres errors
+select tcl_error_handling_test('moo');
+ tcl_error_handling_test
+----------------------------
+ invalid command name "moo"
+(1 row)
+
+-- test elog
+select tcl_eval('elog');
+ERROR: wrong # args: should be "elog level msg"
+select tcl_eval('elog foo bar');
+ERROR: bad priority "foo": must be DEBUG, LOG, INFO, NOTICE, WARNING, ERROR, or FATAL
+-- test forced error
+select tcl_eval('error "forced error"');
+ERROR: forced error
+-- test loop control in spi_exec[p]
+select tcl_spi_exec(true, 'break');
+NOTICE: col1 1, col2 foo
+NOTICE: col1 2, col2 bar
+NOTICE: action: break
+NOTICE: end of function
+ tcl_spi_exec
+--------------
+
+(1 row)
+
+select tcl_spi_exec(true, 'continue');
+NOTICE: col1 1, col2 foo
+NOTICE: col1 2, col2 bar
+NOTICE: action: continue
+NOTICE: col1 3, col2 baz
+NOTICE: end of function
+ tcl_spi_exec
+--------------
+
+(1 row)
+
+select tcl_spi_exec(true, 'error');
+NOTICE: col1 1, col2 foo
+NOTICE: col1 2, col2 bar
+NOTICE: action: error
+ERROR: error message
+select tcl_spi_exec(true, 'return');
+NOTICE: col1 1, col2 foo
+NOTICE: col1 2, col2 bar
+NOTICE: action: return
+ tcl_spi_exec
+--------------
+
+(1 row)
+
+select tcl_spi_exec(false, 'break');
+NOTICE: col1 1, col2 foo
+NOTICE: col1 2, col2 bar
+NOTICE: action: break
+NOTICE: end of function
+ tcl_spi_exec
+--------------
+
+(1 row)
+
+select tcl_spi_exec(false, 'continue');
+NOTICE: col1 1, col2 foo
+NOTICE: col1 2, col2 bar
+NOTICE: action: continue
+NOTICE: col1 3, col2 baz
+NOTICE: end of function
+ tcl_spi_exec
+--------------
+
+(1 row)
+
+select tcl_spi_exec(false, 'error');
+NOTICE: col1 1, col2 foo
+NOTICE: col1 2, col2 bar
+NOTICE: action: error
+ERROR: error message
+select tcl_spi_exec(false, 'return');
+NOTICE: col1 1, col2 foo
+NOTICE: col1 2, col2 bar
+NOTICE: action: return
+ tcl_spi_exec
+--------------
+
+(1 row)
+
+-- forcibly run the Tcl event loop for awhile, to check that we have not
+-- messed things up too badly by disabling the Tcl notifier subsystem
+select tcl_eval($$
+ unset -nocomplain ::tcl_vwait
+ after 100 {set ::tcl_vwait 1}
+ vwait ::tcl_vwait
+ unset -nocomplain ::tcl_vwait$$);
+ tcl_eval
+----------
+
+(1 row)
+
+-- test transition table visibility
+create table transition_table_test (id int, name text);
+insert into transition_table_test values (1, 'a');
+create function transition_table_test_f() returns trigger language pltcl as
+$$
+ spi_exec -array C "SELECT id, name FROM old_table" {
+ elog INFO "old: $C(id) -> $C(name)"
+ }
+ spi_exec -array C "SELECT id, name FROM new_table" {
+ elog INFO "new: $C(id) -> $C(name)"
+ }
+ return OK
+$$;
+CREATE TRIGGER a_t AFTER UPDATE ON transition_table_test
+ REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
+ FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_test_f();
+update transition_table_test set name = 'b';
+INFO: old: 1 -> a
+INFO: new: 1 -> b
+drop table transition_table_test;
+drop function transition_table_test_f();
diff --git a/src/pl/tcl/expected/pltcl_setup.out b/src/pl/tcl/expected/pltcl_setup.out
index e65e9e3ff7..f1958c3a98 100644
--- a/src/pl/tcl/expected/pltcl_setup.out
+++ b/src/pl/tcl/expected/pltcl_setup.out
@@ -49,10 +49,31 @@ create function check_pkey1_exists(int4, bpchar) returns bool as E'
return "f"
' language pltcl;
-- dump trigger data
-CREATE TABLE trigger_test
- (i int, v text );
-CREATE VIEW trigger_test_view AS SELECT * FROM trigger_test;
+CREATE TABLE trigger_test (
+ i int,
+ v text,
+ dropme text,
+ test_skip boolean DEFAULT false,
+ test_return_null boolean DEFAULT false,
+ test_argisnull boolean DEFAULT false
+);
+-- Make certain dropped attributes are handled correctly
+ALTER TABLE trigger_test DROP dropme;
+CREATE VIEW trigger_test_view AS SELECT i, v FROM trigger_test;
CREATE FUNCTION trigger_data() returns trigger language pltcl as $_$
+ if {$TG_table_name eq "trigger_test" && $TG_level eq "ROW" && $TG_op ne "DELETE"} {
+ # Special case tests
+ if {$NEW(test_return_null) eq "t" } {
+ return_null
+ }
+ if {$NEW(test_argisnull) eq "t" } {
+ set should_error [argisnull 1]
+ }
+ if {$NEW(test_skip) eq "t" } {
+ elog NOTICE "SKIPPING OPERATION $TG_op"
+ return SKIP
+ }
+ }
if { [info exists TG_relid] } {
set TG_relid "bogus:12345"
@@ -86,6 +107,9 @@ $_$;
CREATE TRIGGER show_trigger_data_trig
BEFORE INSERT OR UPDATE OR DELETE ON trigger_test
FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
+CREATE TRIGGER statement_trigger
+BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE ON trigger_test
+FOR EACH STATEMENT EXECUTE PROCEDURE trigger_data(42,'statement trigger');
CREATE TRIGGER show_trigger_data_view_trig
INSTEAD OF INSERT OR UPDATE OR DELETE ON trigger_test_view
FOR EACH ROW EXECUTE PROCEDURE trigger_data(24,'skidoo view');
@@ -520,7 +544,7 @@ CREATE OPERATOR CLASS tcl_int4_ops
create function tcl_date_week(int4,int4,int4) returns text as $$
return [clock format [clock scan "$2/$3/$1"] -format "%U"]
$$ language pltcl immutable;
-select tcl_date_week(2010,1,24);
+select tcl_date_week(2010,1,26);
tcl_date_week
---------------
04
@@ -533,12 +557,12 @@ select tcl_date_week(2001,10,24);
(1 row)
-- test pltcl event triggers
-create or replace function tclsnitch() returns event_trigger language pltcl as $$
+create function tclsnitch() returns event_trigger language pltcl as $$
elog NOTICE "tclsnitch: $TG_event $TG_tag"
$$;
create event trigger tcl_a_snitch on ddl_command_start execute procedure tclsnitch();
create event trigger tcl_b_snitch on ddl_command_end execute procedure tclsnitch();
-create or replace function foobar() returns int language sql as $$select 1;$$;
+create function foobar() returns int language sql as $$select 1;$$;
NOTICE: tclsnitch: ddl_command_start CREATE FUNCTION
NOTICE: tclsnitch: ddl_command_end CREATE FUNCTION
alter function foobar() cost 77;
@@ -555,31 +579,108 @@ NOTICE: tclsnitch: ddl_command_start DROP TABLE
NOTICE: tclsnitch: ddl_command_end DROP TABLE
drop event trigger tcl_a_snitch;
drop event trigger tcl_b_snitch;
+create function tcl_test_cube_squared(in int, out squared int, out cubed int) as $$
+ return [list squared [expr {$1 * $1}] cubed [expr {$1 * $1 * $1}]]
+$$ language pltcl;
+create function tcl_test_squared_rows(int,int) returns table (x int, y int) as $$
+ for {set i $1} {$i < $2} {incr i} {
+ return_next [list y [expr {$i * $i}] x $i]
+ }
+$$ language pltcl;
+create function tcl_test_sequence(int,int) returns setof int as $$
+ for {set i $1} {$i < $2} {incr i} {
+ return_next $i
+ }
+$$ language pltcl;
+create function tcl_eval(string text) returns text as $$
+ eval $1
+$$ language pltcl;
-- test use of errorCode in error handling
-create function tcl_error_handling_test() returns text as $$
- global errorCode
- if {[catch { spi_exec "select no_such_column from foo;" }]} {
- array set errArray $errorCode
- if {$errArray(condition) == "undefined_table"} {
- return "expected error: $errArray(message)"
- } else {
- return "unexpected error: $errArray(condition) $errArray(message)"
+create function tcl_error_handling_test(text) returns text
+language pltcl
+as $function$
+ if {[catch $1 err]} {
+ # If not a Postgres error, just return the basic error message
+ if {[lindex $::errorCode 0] != "POSTGRES"} {
+ return $err
}
+
+ # Get rid of keys that can't be expected to remain constant
+ array set myArray $::errorCode
+ unset myArray(POSTGRES)
+ unset -nocomplain myArray(funcname)
+ unset -nocomplain myArray(filename)
+ unset -nocomplain myArray(lineno)
+
+ # Format into something nicer
+ set vals []
+ foreach {key} [lsort [array names myArray]] {
+ set value [string map {"\n" "\n\t"} $myArray($key)]
+ lappend vals "$key: $value"
+ }
+ return [join $vals "\n"]
} else {
return "no error"
}
-$$ language pltcl;
-select tcl_error_handling_test();
- tcl_error_handling_test
------------------------------------------------
- expected error: relation "foo" does not exist
-(1 row)
-
-create temp table foo(f1 int);
-select tcl_error_handling_test();
- tcl_error_handling_test
----------------------------------------------------------------------------
- unexpected error: undefined_column column "no_such_column" does not exist
-(1 row)
-
-drop table foo;
+$function$;
+-- test spi_exec and spi_execp with -array
+create function tcl_spi_exec(
+ prepare boolean,
+ action text
+)
+returns void language pltcl AS $function$
+set query "select * from (values (1,'foo'),(2,'bar'),(3,'baz')) v(col1,col2)"
+if {$1 == "t"} {
+ set prep [spi_prepare $query {}]
+ spi_execp -array A $prep {
+ elog NOTICE "col1 $A(col1), col2 $A(col2)"
+
+ switch $A(col1) {
+ 2 {
+ elog NOTICE "action: $2"
+ switch $2 {
+ break {
+ break
+ }
+ continue {
+ continue
+ }
+ return {
+ return
+ }
+ error {
+ error "error message"
+ }
+ }
+ error "should not get here"
+ }
+ }
+ }
+} else {
+ spi_exec -array A $query {
+ elog NOTICE "col1 $A(col1), col2 $A(col2)"
+
+ switch $A(col1) {
+ 2 {
+ elog NOTICE "action: $2"
+ switch $2 {
+ break {
+ break
+ }
+ continue {
+ continue
+ }
+ return {
+ return
+ }
+ error {
+ error "error message"
+ }
+ }
+ error "should not get here"
+ }
+ }
+ }
+}
+elog NOTICE "end of function"
+$function$;
diff --git a/src/pl/tcl/expected/pltcl_start_proc.out b/src/pl/tcl/expected/pltcl_start_proc.out
new file mode 100644
index 0000000000..9946cd9652
--- /dev/null
+++ b/src/pl/tcl/expected/pltcl_start_proc.out
@@ -0,0 +1,31 @@
+--
+-- Test start_proc execution
+--
+SET pltcl.start_proc = 'no_such_function';
+select tcl_int4add(1, 2);
+ERROR: function no_such_function() does not exist
+CONTEXT: processing pltcl.start_proc parameter
+select tcl_int4add(1, 2);
+ERROR: function no_such_function() does not exist
+CONTEXT: processing pltcl.start_proc parameter
+create function tcl_initialize() returns void as
+$$ elog NOTICE "in tcl_initialize" $$ language pltcl SECURITY DEFINER;
+SET pltcl.start_proc = 'public.tcl_initialize';
+select tcl_int4add(1, 2); -- fail
+ERROR: function "public.tcl_initialize" must not be SECURITY DEFINER
+CONTEXT: processing pltcl.start_proc parameter
+create or replace function tcl_initialize() returns void as
+$$ elog NOTICE "in tcl_initialize" $$ language pltcl;
+select tcl_int4add(1, 2);
+NOTICE: in tcl_initialize
+ tcl_int4add
+-------------
+ 3
+(1 row)
+
+select tcl_int4add(1, 2);
+ tcl_int4add
+-------------
+ 3
+(1 row)
+
diff --git a/src/pl/tcl/expected/pltcl_subxact.out b/src/pl/tcl/expected/pltcl_subxact.out
new file mode 100644
index 0000000000..4393f4acf6
--- /dev/null
+++ b/src/pl/tcl/expected/pltcl_subxact.out
@@ -0,0 +1,143 @@
+--
+-- Test explicit subtransactions
+--
+CREATE TABLE subtransaction_tbl (
+ i integer
+);
+--
+-- We use this wrapper to catch errors and return errormsg only,
+-- because values of $::errorinfo variable contain procedure name which
+-- includes OID, so it's not stable
+--
+CREATE FUNCTION pltcl_wrapper(statement text) RETURNS text
+AS $$
+ if [catch {spi_exec $1} msg] {
+ return "ERROR: $msg"
+ } else {
+ return "SUCCESS: $msg"
+ }
+$$ LANGUAGE pltcl;
+-- Test subtransaction successfully committed
+CREATE FUNCTION subtransaction_ctx_success() RETURNS void
+AS $$
+ spi_exec "INSERT INTO subtransaction_tbl VALUES(1)"
+ subtransaction {
+ spi_exec "INSERT INTO subtransaction_tbl VALUES(2)"
+ }
+$$ LANGUAGE pltcl;
+BEGIN;
+INSERT INTO subtransaction_tbl VALUES(0);
+SELECT subtransaction_ctx_success();
+ subtransaction_ctx_success
+----------------------------
+
+(1 row)
+
+COMMIT;
+SELECT * FROM subtransaction_tbl;
+ i
+---
+ 0
+ 1
+ 2
+(3 rows)
+
+TRUNCATE subtransaction_tbl;
+-- Test subtransaction rollback
+CREATE FUNCTION subtransaction_ctx_test(what_error text = NULL) RETURNS void
+AS $$
+ spi_exec "INSERT INTO subtransaction_tbl VALUES (1)"
+ subtransaction {
+ spi_exec "INSERT INTO subtransaction_tbl VALUES (2)"
+ if {$1 == "SPI"} {
+ spi_exec "INSERT INTO subtransaction_tbl VALUES ('oops')"
+ } elseif { $1 == "Tcl"} {
+ elog ERROR "Tcl error"
+ }
+ }
+$$ LANGUAGE pltcl;
+SELECT pltcl_wrapper('SELECT subtransaction_ctx_test()');
+ pltcl_wrapper
+---------------
+ SUCCESS: 1
+(1 row)
+
+SELECT * FROM subtransaction_tbl;
+ i
+---
+ 1
+ 2
+(2 rows)
+
+TRUNCATE subtransaction_tbl;
+SELECT pltcl_wrapper('SELECT subtransaction_ctx_test(''SPI'')');
+ pltcl_wrapper
+-------------------------------------------------
+ ERROR: invalid input syntax for integer: "oops"
+(1 row)
+
+SELECT * FROM subtransaction_tbl;
+ i
+---
+(0 rows)
+
+TRUNCATE subtransaction_tbl;
+SELECT pltcl_wrapper('SELECT subtransaction_ctx_test(''Tcl'')');
+ pltcl_wrapper
+------------------
+ ERROR: Tcl error
+(1 row)
+
+SELECT * FROM subtransaction_tbl;
+ i
+---
+(0 rows)
+
+TRUNCATE subtransaction_tbl;
+-- Nested subtransactions
+CREATE FUNCTION subtransaction_nested_test(swallow boolean = 'f') RETURNS text
+AS $$
+spi_exec "INSERT INTO subtransaction_tbl VALUES (1)"
+subtransaction {
+ spi_exec "INSERT INTO subtransaction_tbl VALUES (2)"
+ if [catch {
+ subtransaction {
+ spi_exec "INSERT INTO subtransaction_tbl VALUES (3)"
+ spi_exec "error"
+ }
+ } errormsg] {
+ if {$1 != "t"} {
+ error $errormsg $::errorInfo $::errorCode
+ }
+ elog NOTICE "Swallowed $errormsg"
+ }
+}
+return "ok"
+$$ LANGUAGE pltcl;
+SELECT pltcl_wrapper('SELECT subtransaction_nested_test()');
+ pltcl_wrapper
+----------------------------------------
+ ERROR: syntax error at or near "error"
+(1 row)
+
+SELECT * FROM subtransaction_tbl;
+ i
+---
+(0 rows)
+
+TRUNCATE subtransaction_tbl;
+SELECT pltcl_wrapper('SELECT subtransaction_nested_test(''t'')');
+NOTICE: Swallowed syntax error at or near "error"
+ pltcl_wrapper
+---------------
+ SUCCESS: 1
+(1 row)
+
+SELECT * FROM subtransaction_tbl;
+ i
+---
+ 1
+ 2
+(2 rows)
+
+TRUNCATE subtransaction_tbl;
diff --git a/src/pl/tcl/generate-pltclerrcodes.pl b/src/pl/tcl/generate-pltclerrcodes.pl
index 144e159909..b4e429a4fb 100644
--- a/src/pl/tcl/generate-pltclerrcodes.pl
+++ b/src/pl/tcl/generate-pltclerrcodes.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
#
# Generate the pltclerrcodes.h header from errcodes.txt
-# Copyright (c) 2000-2016, PostgreSQL Global Development Group
+# Copyright (c) 2000-2017, PostgreSQL Global Development Group
use warnings;
use strict;
@@ -10,7 +10,7 @@ print
"/* autogenerated from src/backend/utils/errcodes.txt, do not edit */\n";
print "/* there is deliberately not an #ifndef PLTCLERRCODES_H here */\n";
-open my $errcodes, $ARGV[0] or die;
+open my $errcodes, '<', $ARGV[0] or die;
while (<$errcodes>)
{
diff --git a/src/pl/tcl/modules/.gitignore b/src/pl/tcl/modules/.gitignore
deleted file mode 100644
index 89581887c4..0000000000
--- a/src/pl/tcl/modules/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-/pltcl_delmod
-/pltcl_listmod
-/pltcl_loadmod
diff --git a/src/pl/tcl/modules/Makefile b/src/pl/tcl/modules/Makefile
deleted file mode 100644
index 8055c61460..0000000000
--- a/src/pl/tcl/modules/Makefile
+++ /dev/null
@@ -1,28 +0,0 @@
-# src/pl/tcl/modules/Makefile
-
-subdir = src/pl/tcl/modules
-top_builddir = ../../../..
-include $(top_builddir)/src/Makefile.global
-
-MODULES = pltcl_loadmod pltcl_delmod pltcl_listmod
-
-all: $(MODULES)
-
-$(MODULES): %: %.in $(top_builddir)/src/Makefile.global
- sed 's,@TCLSH@,$(TCLSH),g' $< >$@
- chmod a+x $@
-
-install: all installdirs
- $(INSTALL_SCRIPT) pltcl_loadmod '$(DESTDIR)$(bindir)/pltcl_loadmod'
- $(INSTALL_SCRIPT) pltcl_delmod '$(DESTDIR)$(bindir)/pltcl_delmod'
- $(INSTALL_SCRIPT) pltcl_listmod '$(DESTDIR)$(bindir)/pltcl_listmod'
- $(INSTALL_DATA) $(srcdir)/unknown.pltcl '$(DESTDIR)$(datadir)/unknown.pltcl'
-
-installdirs:
- $(MKDIR_P) '$(DESTDIR)$(bindir)' '$(DESTDIR)$(datadir)'
-
-uninstall:
- rm -f '$(DESTDIR)$(bindir)/pltcl_loadmod' '$(DESTDIR)$(bindir)/pltcl_delmod' '$(DESTDIR)$(bindir)/pltcl_listmod' '$(DESTDIR)$(datadir)/unknown.pltcl'
-
-clean distclean maintainer-clean:
- rm -f $(MODULES)
diff --git a/src/pl/tcl/modules/README b/src/pl/tcl/modules/README
deleted file mode 100644
index 342742c04b..0000000000
--- a/src/pl/tcl/modules/README
+++ /dev/null
@@ -1,18 +0,0 @@
-src/pl/tcl/modules/README
-
-Regular Tcl scripts of any size (over 8K :-) can be loaded into
-the table pltcl_modules using the pltcl_loadmod script. The script
-checks the modules that the procedure names don't overwrite
-existing ones before doing anything. They also check for global
-variables created at load time.
-
-All procedures defined in the module files are automatically
-added to the table pltcl_modfuncs. This table is used by the
-unknown procedure to determine if an unknown command can be
-loaded by sourcing a module. In that case the unknown procedure
-will silently source in the module and reexecute the original
-command that invoked unknown.
-
-I know, this readme should be more explanatory - but time.
-
-Jan
diff --git a/src/pl/tcl/modules/pltcl_delmod.in b/src/pl/tcl/modules/pltcl_delmod.in
deleted file mode 100644
index daa4fac460..0000000000
--- a/src/pl/tcl/modules/pltcl_delmod.in
+++ /dev/null
@@ -1,117 +0,0 @@
-#! /bin/sh
-# src/pl/tcl/modules/pltcl_delmod.in
-#
-# Start tclsh \
-exec @TCLSH@ "$0" "$@"
-
-#
-# Code still has to be documented
-#
-
-#load /usr/local/pgsql/lib/libpgtcl.so
-package require Pgtcl
-
-
-#
-# Check for minimum arguments
-#
-if {$argc < 1} {
- puts stderr ""
- puts stderr "usage: pltcl_delmod dbname \[options\] modulename \[...\]"
- puts stderr ""
- puts stderr "options:"
- puts stderr " -host hostname"
- puts stderr " -port portnumber"
- puts stderr ""
- exit 1
-}
-
-#
-# Remember database name and initialize options
-#
-set dbname [lindex $argv 0]
-set options ""
-set errors 0
-set opt ""
-set val ""
-
-set i 1
-while {$i < $argc} {
- if {[string compare [string index [lindex $argv $i] 0] "-"] != 0} {
- break;
- }
-
- set opt [lindex $argv $i]
- incr i
- if {$i >= $argc} {
- puts stderr "no value given for option $opt"
- incr errors
- continue
- }
- set val [lindex $argv $i]
- incr i
-
- switch -- $opt {
- -host {
- append options "-host \"$val\" "
- }
- -port {
- append options "-port $val "
- }
- default {
- puts stderr "unknown option '$opt'"
- incr errors
- }
- }
-}
-
-#
-# Final syntax check
-#
-if {$i >= $argc || $errors > 0} {
- puts stderr ""
- puts stderr "usage: pltcl_delmod dbname \[options\] modulename \[...\]"
- puts stderr ""
- puts stderr "options:"
- puts stderr " -host hostname"
- puts stderr " -port portnumber"
- puts stderr ""
- exit 1
-}
-
-proc delmodule {conn modname} {
- set xname $modname
- regsub -all {\\} $xname {\\} xname
- regsub -all {'} $xname {''} xname
-
- set found 0
- pg_select $conn "select * from pltcl_modules where modname = '$xname'" \
- MOD {
- set found 1
- break;
- }
-
- if {!$found} {
- puts "Module $modname not found in pltcl_modules"
- puts ""
- return
- }
-
- pg_result \
- [pg_exec $conn "delete from pltcl_modules where modname = '$xname'"] \
- -clear
- pg_result \
- [pg_exec $conn "delete from pltcl_modfuncs where modname = '$xname'"] \
- -clear
-
- puts "Module $modname removed"
-}
-
-set conn [eval pg_connect $dbname $options]
-
-while {$i < $argc} {
- delmodule $conn [lindex $argv $i]
- incr i
-}
-
-pg_disconnect $conn
diff --git a/src/pl/tcl/modules/pltcl_listmod.in b/src/pl/tcl/modules/pltcl_listmod.in
deleted file mode 100644
index 7d930ff0ea..0000000000
--- a/src/pl/tcl/modules/pltcl_listmod.in
+++ /dev/null
@@ -1,123 +0,0 @@
-#! /bin/sh
-# src/pl/tcl/modules/pltcl_listmod.in
-#
-# Start tclsh \
-exec @TCLSH@ "$0" "$@"
-
-#
-# Code still has to be documented
-#
-
-#load /usr/local/pgsql/lib/libpgtcl.so
-package require Pgtcl
-
-
-#
-# Check for minimum arguments
-#
-if {$argc < 1} {
- puts stderr ""
- puts stderr "usage: pltcl_listmod dbname \[options\] \[modulename \[...\]\]"
- puts stderr ""
- puts stderr "options:"
- puts stderr " -host hostname"
- puts stderr " -port portnumber"
- puts stderr ""
- exit 1
-}
-
-#
-# Remember database name and initialize options
-#
-set dbname [lindex $argv 0]
-set options ""
-set errors 0
-set opt ""
-set val ""
-
-set i 1
-while {$i < $argc} {
- if {[string compare [string index [lindex $argv $i] 0] "-"] != 0} {
- break;
- }
-
- set opt [lindex $argv $i]
- incr i
- if {$i >= $argc} {
- puts stderr "no value given for option $opt"
- incr errors
- continue
- }
- set val [lindex $argv $i]
- incr i
-
- switch -- $opt {
- -host {
- append options "-host \"$val\" "
- }
- -port {
- append options "-port $val "
- }
- default {
- puts stderr "unknown option '$opt'"
- incr errors
- }
- }
-}
-
-#
-# Final syntax check
-#
-if {$errors > 0} {
- puts stderr ""
- puts stderr "usage: pltcl_listmod dbname \[options\] \[modulename \[...\]\]"
- puts stderr ""
- puts stderr "options:"
- puts stderr " -host hostname"
- puts stderr " -port portnumber"
- puts stderr ""
- exit 1
-}
-
-proc listmodule {conn modname} {
- set xname $modname
- regsub -all {\\} $xname {\\} xname
- regsub -all {'} $xname {''} xname
-
- set found 0
- pg_select $conn "select * from pltcl_modules where modname = '$xname'" \
- MOD {
- set found 1
- break;
- }
-
- if {!$found} {
- puts "Module $modname not found in pltcl_modules"
- puts ""
- return
- }
-
- puts "Module $modname defines procedures:"
- pg_select $conn "select funcname from pltcl_modfuncs \
- where modname = '$xname' order by funcname" FUNC {
- puts " $FUNC(funcname)"
- }
- puts ""
-}
-
-set conn [eval pg_connect $dbname $options]
-
-if {$i == $argc} {
- pg_select $conn "select distinct modname from pltcl_modules \
- order by modname" \
- MOD {
- listmodule $conn $MOD(modname)
- }
-} else {
- while {$i < $argc} {
- listmodule $conn [lindex $argv $i]
- incr i
- }
-}
-
-pg_disconnect $conn
diff --git a/src/pl/tcl/modules/pltcl_loadmod.in b/src/pl/tcl/modules/pltcl_loadmod.in
deleted file mode 100644
index 645c6bbd9c..0000000000
--- a/src/pl/tcl/modules/pltcl_loadmod.in
+++ /dev/null
@@ -1,501 +0,0 @@
-#! /bin/sh
-# Start tclsh \
-exec @TCLSH@ "$0" "$@"
-
-#
-# Code still has to be documented
-#
-
-#load /usr/local/pgsql/lib/libpgtcl.so
-package require Pgtcl
-
-
-#
-# Check for minimum arguments
-#
-if {$argc < 2} {
- puts stderr ""
- puts stderr "usage: pltcl_loadmod dbname \[options\] file \[...\]"
- puts stderr ""
- puts stderr "options:"
- puts stderr " -host hostname"
- puts stderr " -port portnumber"
- puts stderr ""
- exit 1
-}
-
-#
-# Remember database name and initialize options
-#
-set dbname [lindex $argv 0]
-set options ""
-set errors 0
-set opt ""
-set val ""
-
-set i 1
-while {$i < $argc} {
- if {[string compare [string index [lindex $argv $i] 0] "-"] != 0} {
- break;
- }
-
- set opt [lindex $argv $i]
- incr i
- if {$i >= $argc} {
- puts stderr "no value given for option $opt"
- incr errors
- continue
- }
- set val [lindex $argv $i]
- incr i
-
- switch -- $opt {
- -host {
- append options "-host \"$val\" "
- }
- -port {
- append options "-port $val "
- }
- default {
- puts stderr "unknown option '$opt'"
- incr errors
- }
- }
-}
-
-#
-# Final syntax check
-#
-if {$i >= $argc || $errors > 0} {
- puts stderr ""
- puts stderr "usage: pltcl_loadmod dbname \[options\] file \[...\]"
- puts stderr ""
- puts stderr "options:"
- puts stderr " -host hostname"
- puts stderr " -port portnumber"
- puts stderr ""
- exit 1
-}
-
-
-proc __PLTcl_loadmod_check_table {conn tabname expnames exptypes} {
- set attrs [expr [llength $expnames] - 1]
- set error 0
- set found 0
-
- pg_select $conn "select C.relname, A.attname, A.attnum, T.typname \
- from pg_catalog.pg_class C, pg_catalog.pg_attribute A, pg_catalog.pg_type T \
- where C.relname = '$tabname' \
- and A.attrelid = C.oid \
- and A.attnum > 0 \
- and T.oid = A.atttypid \
- order by attnum" tup {
-
- incr found
- set i $tup(attnum)
-
- if {$i > $attrs} {
- puts stderr "Table $tabname has extra field '$tup(attname)'"
- incr error
- continue
- }
-
- set xname [lindex $expnames $i]
- set xtype [lindex $exptypes $i]
-
- if {[string compare $tup(attname) $xname] != 0} {
- puts stderr "Attribute $i of $tabname has wrong name"
- puts stderr " got '$tup(attname)' expected '$xname'"
- incr error
- }
- if {[string compare $tup(typname) $xtype] != 0} {
- puts stderr "Attribute $i of $tabname has wrong type"
- puts stderr " got '$tup(typname)' expected '$xtype'"
- incr error
- }
- }
-
- if {$found == 0} {
- return 0
- }
-
- if {$found < $attrs} {
- incr found
- set miss [lrange $expnames $found end]
- puts "Table $tabname doesn't have field(s) $miss"
- incr error
- }
-
- if {$error > 0} {
- return 2
- }
-
- return 1
-}
-
-
-proc __PLTcl_loadmod_check_tables {conn} {
- upvar #0 __PLTcl_loadmod_status status
-
- set error 0
-
- set names {{} modname modseq modsrc}
- set types {{} name int2 text}
-
- switch [__PLTcl_loadmod_check_table $conn pltcl_modules $names $types] {
- 0 {
- set status(create_table_modules) 1
- }
- 1 {
- set status(create_table_modules) 0
- }
- 2 {
- puts "Error(s) in table pltcl_modules"
- incr error
- }
- }
-
- set names {{} funcname modname}
- set types {{} name name}
-
- switch [__PLTcl_loadmod_check_table $conn pltcl_modfuncs $names $types] {
- 0 {
- set status(create_table_modfuncs) 1
- }
- 1 {
- set status(create_table_modfuncs) 0
- }
- 2 {
- puts "Error(s) in table pltcl_modfuncs"
- incr error
- }
- }
-
- if {$status(create_table_modfuncs) && !$status(create_table_modules)} {
- puts stderr "Table pltcl_modfuncs doesn't exist but pltcl_modules does"
- puts stderr "Either both tables must be present or none."
- incr error
- }
-
- if {$status(create_table_modules) && !$status(create_table_modfuncs)} {
- puts stderr "Table pltcl_modules doesn't exist but pltcl_modfuncs does"
- puts stderr "Either both tables must be present or none."
- incr error
- }
-
- if {$error} {
- puts stderr ""
- puts stderr "Abort"
- exit 1
- }
-
- if {!$status(create_table_modules)} {
- __PLTcl_loadmod_read_current $conn
- }
-}
-
-
-proc __PLTcl_loadmod_read_current {conn} {
- upvar #0 __PLTcl_loadmod_status status
- upvar #0 __PLTcl_loadmod_modsrc modsrc
- upvar #0 __PLTcl_loadmod_funclist funcs
- upvar #0 __PLTcl_loadmod_globlist globs
-
- set errors 0
-
- set curmodlist ""
- pg_select $conn "select distinct modname from pltcl_modules" mtup {
- set mname $mtup(modname);
- lappend curmodlist $mname
- }
-
- foreach mname $curmodlist {
- set srctext ""
- pg_select $conn "select * from pltcl_modules \
- where modname = '$mname' \
- order by modseq" tup {
- append srctext $tup(modsrc)
- }
-
- if {[catch {
- __PLTcl_loadmod_analyze \
- "Current $mname" \
- $mname \
- $srctext new_globals new_functions
- }]} {
- incr errors
- }
- set modsrc($mname) $srctext
- set funcs($mname) $new_functions
- set globs($mname) $new_globals
- }
-
- if {$errors} {
- puts stderr ""
- puts stderr "Abort"
- exit 1
- }
-}
-
-
-proc __PLTcl_loadmod_analyze {modinfo modname srctext v_globals v_functions} {
- upvar 1 $v_globals new_g
- upvar 1 $v_functions new_f
- upvar #0 __PLTcl_loadmod_allfuncs allfuncs
- upvar #0 __PLTcl_loadmod_allglobs allglobs
-
- set errors 0
-
- set old_g [info globals]
- set old_f [info procs]
- set new_g ""
- set new_f ""
-
- if {[catch {
- uplevel #0 "$srctext"
- } msg]} {
- puts "$modinfo: $msg"
- incr errors
- }
-
- set cur_g [info globals]
- set cur_f [info procs]
-
- foreach glob $cur_g {
- if {[lsearch -exact $old_g $glob] >= 0} {
- continue
- }
- if {[info exists allglobs($glob)]} {
- puts stderr "$modinfo: Global $glob previously used in module $allglobs($glob)"
- incr errors
- } else {
- set allglobs($glob) $modname
- }
- lappend new_g $glob
- uplevel #0 unset $glob
- }
- foreach func $cur_f {
- if {[lsearch -exact $old_f $func] >= 0} {
- continue
- }
- if {[info exists allfuncs($func)]} {
- puts stderr "$modinfo: Function $func previously defined in module $allfuncs($func)"
- incr errors
- } else {
- set allfuncs($func) $modname
- }
- lappend new_f $func
- rename $func {}
- }
-
- if {$errors} {
- return -code error
- }
- #puts "globs in $modname: $new_g"
- #puts "funcs in $modname: $new_f"
-}
-
-
-proc __PLTcl_loadmod_create_tables {conn} {
- upvar #0 __PLTcl_loadmod_status status
-
- if {$status(create_table_modules)} {
- if {[catch {
- set res [pg_exec $conn \
- "create table pltcl_modules ( \
- modname name, \
- modseq int2, \
- modsrc text);"]
- } msg]} {
- puts stderr "Error creating table pltcl_modules"
- puts stderr " $msg"
- exit 1
- }
- if {[catch {
- set res [pg_exec $conn \
- "create index pltcl_modules_i \
- on pltcl_modules using btree \
- (modname name_ops);"]
- } msg]} {
- puts stderr "Error creating index pltcl_modules_i"
- puts stderr " $msg"
- exit 1
- }
- puts "Table pltcl_modules created"
- pg_result $res -clear
- }
-
- if {$status(create_table_modfuncs)} {
- if {[catch {
- set res [pg_exec $conn \
- "create table pltcl_modfuncs ( \
- funcname name, \
- modname name);"]
- } msg]} {
- puts stderr "Error creating table pltcl_modfuncs"
- puts stderr " $msg"
- exit 1
- }
- if {[catch {
- set res [pg_exec $conn \
- "create index pltcl_modfuncs_i \
- on pltcl_modfuncs using hash \
- (funcname name_ops);"]
- } msg]} {
- puts stderr "Error creating index pltcl_modfuncs_i"
- puts stderr " $msg"
- exit 1
- }
- puts "Table pltcl_modfuncs created"
- pg_result $res -clear
- }
-}
-
-
-proc __PLTcl_loadmod_read_new {conn} {
- upvar #0 __PLTcl_loadmod_status status
- upvar #0 __PLTcl_loadmod_modsrc modsrc
- upvar #0 __PLTcl_loadmod_funclist funcs
- upvar #0 __PLTcl_loadmod_globlist globs
- upvar #0 __PLTcl_loadmod_allfuncs allfuncs
- upvar #0 __PLTcl_loadmod_allglobs allglobs
- upvar #0 __PLTcl_loadmod_modlist modlist
-
- set errors 0
-
- set new_modlist ""
- foreach modfile $modlist {
- set modname [file rootname [file tail $modfile]]
- if {[catch {
- set fid [open $modfile "r"]
- } msg]} {
- puts stderr $msg
- incr errors
- continue
- }
- set srctext [read $fid]
- close $fid
-
- if {[info exists modsrc($modname)]} {
- if {[string compare $modsrc($modname) $srctext] == 0} {
- puts "Module $modname unchanged - ignored"
- continue
- }
- foreach func $funcs($modname) {
- unset allfuncs($func)
- }
- foreach glob $globs($modname) {
- unset allglobs($glob)
- }
- unset funcs($modname)
- unset globs($modname)
- set modsrc($modname) $srctext
- lappend new_modlist $modname
- } else {
- set modsrc($modname) $srctext
- lappend new_modlist $modname
- }
-
- if {[catch {
- __PLTcl_loadmod_analyze "New/updated $modname" \
- $modname $srctext new_globals new_funcs
- }]} {
- incr errors
- }
-
- set funcs($modname) $new_funcs
- set globs($modname) $new_globals
- }
-
- if {$errors} {
- puts stderr ""
- puts stderr "Abort"
- exit 1
- }
-
- set modlist $new_modlist
-}
-
-
-proc __PLTcl_loadmod_load_modules {conn} {
- upvar #0 __PLTcl_loadmod_modsrc modsrc
- upvar #0 __PLTcl_loadmod_funclist funcs
- upvar #0 __PLTcl_loadmod_modlist modlist
-
- set errors 0
-
- foreach modname $modlist {
- set xname [__PLTcl_loadmod_quote $modname]
-
- pg_result [pg_exec $conn "begin;"] -clear
-
- pg_result [pg_exec $conn \
- "delete from pltcl_modules where modname = '$xname'"] -clear
- pg_result [pg_exec $conn \
- "delete from pltcl_modfuncs where modname = '$xname'"] -clear
-
- foreach func $funcs($modname) {
- set xfunc [__PLTcl_loadmod_quote $func]
- pg_result [ \
- pg_exec $conn "insert into pltcl_modfuncs values ( \
- '$xfunc', '$xname')" \
- ] -clear
- }
- set i 0
- set srctext $modsrc($modname)
- while {[string compare $srctext ""] != 0} {
- set xpart [string range $srctext 0 3999]
- set xpart [__PLTcl_loadmod_quote $xpart]
- set srctext [string range $srctext 4000 end]
-
- pg_result [ \
- pg_exec $conn "insert into pltcl_modules values ( \
- '$xname', $i, '$xpart')" \
- ] -clear
- incr i
- }
-
- pg_result [pg_exec $conn "commit;"] -clear
-
- puts "Successfully loaded/updated module $modname"
- }
-}
-
-
-proc __PLTcl_loadmod_quote {s} {
- regsub -all {\\} $s {\\\\} s
- regsub -all {'} $s {''} s
- return $s
-}
-
-
-set __PLTcl_loadmod_modlist [lrange $argv $i end]
-set __PLTcl_loadmod_modsrc(dummy) ""
-set __PLTcl_loadmod_funclist(dummy) ""
-set __PLTcl_loadmod_globlist(dummy) ""
-set __PLTcl_loadmod_allfuncs(dummy) ""
-set __PLTcl_loadmod_allglobs(dummy) ""
-
-unset __PLTcl_loadmod_modsrc(dummy)
-unset __PLTcl_loadmod_funclist(dummy)
-unset __PLTcl_loadmod_globlist(dummy)
-unset __PLTcl_loadmod_allfuncs(dummy)
-unset __PLTcl_loadmod_allglobs(dummy)
-
-
-puts ""
-
-set __PLTcl_loadmod_conn [eval pg_connect $dbname $options]
-
-unset i dbname options errors opt val
-
-__PLTcl_loadmod_check_tables $__PLTcl_loadmod_conn
-
-__PLTcl_loadmod_read_new $__PLTcl_loadmod_conn
-
-__PLTcl_loadmod_create_tables $__PLTcl_loadmod_conn
-__PLTcl_loadmod_load_modules $__PLTcl_loadmod_conn
-
-pg_disconnect $__PLTcl_loadmod_conn
-
-puts ""
diff --git a/src/pl/tcl/modules/unknown.pltcl b/src/pl/tcl/modules/unknown.pltcl
deleted file mode 100644
index 0729ac1b70..0000000000
--- a/src/pl/tcl/modules/unknown.pltcl
+++ /dev/null
@@ -1,63 +0,0 @@
-#---------------------------------------------------------------------
-# Support for unknown command
-#---------------------------------------------------------------------
-
-proc unknown {proname args} {
- upvar #0 __PLTcl_unknown_support_plan_modname p_mod
- upvar #0 __PLTcl_unknown_support_plan_modsrc p_src
-
- #-----------------------------------------------------------
- # On first call prepare the plans
- #-----------------------------------------------------------
- if {![info exists p_mod]} {
- set p_mod [spi_prepare \
- "select modname from pltcl_modfuncs \
- where funcname = \$1" name]
- set p_src [spi_prepare \
- "select modseq, modsrc from pltcl_modules \
- where modname = \$1 \
- order by modseq" name]
- }
-
- #-----------------------------------------------------------
- # Lookup the requested function in pltcl_modfuncs
- #-----------------------------------------------------------
- set n [spi_execp -count 1 $p_mod [list [quote $proname]]]
- if {$n != 1} {
- #-----------------------------------------------------------
- # Not found there either - now it's really unknown
- #-----------------------------------------------------------
- return -code error "unknown command '$proname'"
- }
-
- #-----------------------------------------------------------
- # Collect the source pieces from pltcl_modules
- #-----------------------------------------------------------
- set src ""
- spi_execp $p_src [list [quote $modname]] {
- append src $modsrc
- }
-
- #-----------------------------------------------------------
- # Load the source into the interpreter
- #-----------------------------------------------------------
- if {[catch {
- uplevel #0 "$src"
- } msg]} {
- elog NOTICE "pltcl unknown: error while loading module $modname"
- elog WARN $msg
- }
-
- #-----------------------------------------------------------
- # This should never happen
- #-----------------------------------------------------------
- if {[catch {info args $proname}]} {
- return -code error \
- "unknown command '$proname' (still after loading module $modname)"
- }
-
- #-----------------------------------------------------------
- # Finally simulate the initial procedure call
- #-----------------------------------------------------------
- return [uplevel 1 $proname $args]
-}
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 6ee4153ae6..ae9ba80cf7 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -15,20 +15,25 @@
#include "access/htup_details.h"
#include "access/xact.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/event_trigger.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "fmgr.h"
+#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
+#include "parser/parse_func.h"
#include "parser/parse_type.h"
+#include "pgstat.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
+#include "utils/regproc.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
@@ -114,21 +119,38 @@ typedef struct pltcl_interp_desc
/**********************************************************************
* The information we cache about loaded procedures
+ *
+ * The pltcl_proc_desc struct itself, as well as all subsidiary data,
+ * is stored in the memory context identified by the fn_cxt field.
+ * We can reclaim all the data by deleting that context, and should do so
+ * when the fn_refcount goes to zero. (But note that we do not bother
+ * trying to clean up Tcl's copy of the procedure definition: it's Tcl's
+ * problem to manage its memory when we replace a proc definition. We do
+ * not clean up pltcl_proc_descs when a pg_proc row is deleted, only when
+ * it is updated, and the same policy applies to Tcl's copy as well.)
+ *
+ * Note that the data in this struct is shared across all active calls;
+ * nothing except the fn_refcount should be changed by a call instance.
**********************************************************************/
typedef struct pltcl_proc_desc
{
- char *user_proname;
- char *internal_proname;
- TransactionId fn_xmin;
- ItemPointerData fn_tid;
- bool fn_readonly;
- bool lanpltrusted;
- pltcl_interp_desc *interp_desc;
- FmgrInfo result_in_func;
- Oid result_typioparam;
- int nargs;
- FmgrInfo arg_out_func[FUNC_MAX_ARGS];
- bool arg_is_rowtype[FUNC_MAX_ARGS];
+ char *user_proname; /* user's name (from pg_proc.proname) */
+ char *internal_proname; /* Tcl name (based on function OID) */
+ MemoryContext fn_cxt; /* memory context for this procedure */
+ unsigned long fn_refcount; /* number of active references */
+ TransactionId fn_xmin; /* xmin of pg_proc row */
+ ItemPointerData fn_tid; /* TID of pg_proc row */
+ bool fn_readonly; /* is function readonly? */
+ bool lanpltrusted; /* is it pltcl (vs. pltclu)? */
+ pltcl_interp_desc *interp_desc; /* interpreter to use */
+ FmgrInfo result_in_func; /* input function for fn's result type */
+ Oid result_typioparam; /* param to pass to same */
+ bool fn_retisset; /* true if function returns a set */
+ bool fn_retistuple; /* true if function returns composite */
+ int nargs; /* number of arguments */
+ /* these arrays have nargs entries: */
+ FmgrInfo *arg_out_func; /* output fns for arg types */
+ bool *arg_is_rowtype; /* is each arg composite? */
} pltcl_proc_desc;
@@ -177,16 +199,46 @@ typedef struct pltcl_proc_ptr
/**********************************************************************
+ * Per-call state
+ **********************************************************************/
+typedef struct pltcl_call_state
+{
+ /* Call info struct, or NULL in a trigger */
+ FunctionCallInfo fcinfo;
+
+ /* Trigger data, if we're in a normal (not event) trigger; else NULL */
+ TriggerData *trigdata;
+
+ /* Function we're executing (NULL if not yet identified) */
+ pltcl_proc_desc *prodesc;
+
+ /*
+ * Information for SRFs and functions returning composite types.
+ * ret_tupdesc and attinmeta are set up if either fn_retistuple or
+ * fn_retisset, since even a scalar-returning SRF needs a tuplestore.
+ */
+ TupleDesc ret_tupdesc; /* return rowtype, if retistuple or retisset */
+ AttInMetadata *attinmeta; /* metadata for building tuples of that type */
+
+ ReturnSetInfo *rsi; /* passed-in ReturnSetInfo, if any */
+ Tuplestorestate *tuple_store; /* SRFs accumulate result here */
+ MemoryContext tuple_store_cxt; /* context and resowner for tuplestore */
+ ResourceOwner tuple_store_owner;
+} pltcl_call_state;
+
+
+/**********************************************************************
* Global data
**********************************************************************/
+static char *pltcl_start_proc = NULL;
+static char *pltclu_start_proc = NULL;
static bool pltcl_pm_init_done = false;
static Tcl_Interp *pltcl_hold_interp = NULL;
static HTAB *pltcl_interp_htab = NULL;
static HTAB *pltcl_proc_htab = NULL;
-/* these are saved and restored by pltcl_handler */
-static FunctionCallInfo pltcl_current_fcinfo = NULL;
-static pltcl_proc_desc *pltcl_current_prodesc = NULL;
+/* this is saved and restored by pltcl_handler */
+static pltcl_call_state *pltcl_current_call_state = NULL;
/**********************************************************************
* Lookup table for SQLSTATE condition names
@@ -207,16 +259,20 @@ static const TclExceptionNameMap exception_name_map[] = {
**********************************************************************/
void _PG_init(void);
-static void pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted);
-static pltcl_interp_desc *pltcl_fetch_interp(bool pltrusted);
-static void pltcl_init_load_unknown(Tcl_Interp *interp);
+static void pltcl_init_interp(pltcl_interp_desc *interp_desc,
+ Oid prolang, bool pltrusted);
+static pltcl_interp_desc *pltcl_fetch_interp(Oid prolang, bool pltrusted);
+static void call_pltcl_start_proc(Oid prolang, bool pltrusted);
+static void start_proc_error_callback(void *arg);
static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted);
-static Datum pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted);
-
-static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted);
-static void pltcl_event_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted);
+static Datum pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
+ bool pltrusted);
+static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
+ bool pltrusted);
+static void pltcl_event_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
+ bool pltrusted);
static void throw_tcl_error(Tcl_Interp *interp, const char *proname);
@@ -234,7 +290,8 @@ static int pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
static int pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
-
+static int pltcl_returnnext(ClientData cdata, Tcl_Interp *interp,
+ int objc, Tcl_Obj *const objv[]);
static int pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
static int pltcl_process_SPI_result(Tcl_Interp *interp,
@@ -249,10 +306,24 @@ static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
static int pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
+static int pltcl_subtransaction(ClientData cdata, Tcl_Interp *interp,
+ int objc, Tcl_Obj *const objv[]);
+
+static void pltcl_subtrans_begin(MemoryContext oldcontext,
+ ResourceOwner oldowner);
+static void pltcl_subtrans_commit(MemoryContext oldcontext,
+ ResourceOwner oldowner);
+static void pltcl_subtrans_abort(Tcl_Interp *interp,
+ MemoryContext oldcontext,
+ ResourceOwner oldowner);
static void pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
uint64 tupno, HeapTuple tuple, TupleDesc tupdesc);
static Tcl_Obj *pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc);
+static HeapTuple pltcl_build_tuple_result(Tcl_Interp *interp,
+ Tcl_Obj **kvObjv, int kvObjc,
+ pltcl_call_state *call_state);
+static void pltcl_init_tuple_store(pltcl_call_state *call_state);
/*
@@ -313,23 +384,6 @@ pltcl_WaitForEvent(CONST86 Tcl_Time *timePtr)
/*
- * This routine is a crock, and so is everyplace that calls it. The problem
- * is that the cached form of pltcl functions/queries is allocated permanently
- * (mostly via malloc()) and never released until backend exit. Subsidiary
- * data structures such as fmgr info records therefore must live forever
- * as well. A better implementation would store all this stuff in a per-
- * function memory context that could be reclaimed at need. In the meantime,
- * fmgr_info_cxt must be called specifying TopMemoryContext so that whatever
- * it might allocate, and whatever the eventual function might allocate using
- * fn_mcxt, will live forever too.
- */
-static void
-perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
-{
- fmgr_info_cxt(functionId, finfo, TopMemoryContext);
-}
-
-/*
* _PG_init() - library load-time initialization
*
* DO NOT make this static nor change its name!
@@ -398,6 +452,24 @@ _PG_init(void)
&hash_ctl,
HASH_ELEM | HASH_BLOBS);
+ /************************************************************
+ * Define PL/Tcl's custom GUCs
+ ************************************************************/
+ DefineCustomStringVariable("pltcl.start_proc",
+ gettext_noop("PL/Tcl function to call once when pltcl is first used."),
+ NULL,
+ &pltcl_start_proc,
+ NULL,
+ PGC_SUSET, 0,
+ NULL, NULL, NULL);
+ DefineCustomStringVariable("pltclu.start_proc",
+ gettext_noop("PL/TclU function to call once when pltclu is first used."),
+ NULL,
+ &pltclu_start_proc,
+ NULL,
+ PGC_SUSET, 0,
+ NULL, NULL, NULL);
+
pltcl_pm_init_done = true;
}
@@ -405,7 +477,7 @@ _PG_init(void)
* pltcl_init_interp() - initialize a new Tcl interpreter
**********************************************************************/
static void
-pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted)
+pltcl_init_interp(pltcl_interp_desc *interp_desc, Oid prolang, bool pltrusted)
{
Tcl_Interp *interp;
char interpname[32];
@@ -419,7 +491,6 @@ pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted)
if ((interp = Tcl_CreateSlave(pltcl_hold_interp, interpname,
pltrusted ? 1 : 0)) == NULL)
elog(ERROR, "could not create slave Tcl interpreter");
- interp_desc->interp = interp;
/************************************************************
* Initialize the query hash table associated with interpreter
@@ -437,7 +508,8 @@ pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted)
pltcl_argisnull, NULL, NULL);
Tcl_CreateObjCommand(interp, "return_null",
pltcl_returnnull, NULL, NULL);
-
+ Tcl_CreateObjCommand(interp, "return_next",
+ pltcl_returnnext, NULL, NULL);
Tcl_CreateObjCommand(interp, "spi_exec",
pltcl_SPI_execute, NULL, NULL);
Tcl_CreateObjCommand(interp, "spi_prepare",
@@ -446,21 +518,37 @@ pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted)
pltcl_SPI_execute_plan, NULL, NULL);
Tcl_CreateObjCommand(interp, "spi_lastoid",
pltcl_SPI_lastoid, NULL, NULL);
+ Tcl_CreateObjCommand(interp, "subtransaction",
+ pltcl_subtransaction, NULL, NULL);
/************************************************************
- * Try to load the unknown procedure from pltcl_modules
+ * Call the appropriate start_proc, if there is one.
+ *
+ * We must set interp_desc->interp before the call, else the start_proc
+ * won't find the interpreter it's supposed to use. But, if the
+ * start_proc fails, we want to abandon use of the interpreter.
************************************************************/
- pltcl_init_load_unknown(interp);
+ PG_TRY();
+ {
+ interp_desc->interp = interp;
+ call_pltcl_start_proc(prolang, pltrusted);
+ }
+ PG_CATCH();
+ {
+ interp_desc->interp = NULL;
+ Tcl_DeleteInterp(interp);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
}
/**********************************************************************
* pltcl_fetch_interp() - fetch the Tcl interpreter to use for a function
*
* This also takes care of any on-first-use initialization required.
- * Note: we assume caller has already connected to SPI.
**********************************************************************/
static pltcl_interp_desc *
-pltcl_fetch_interp(bool pltrusted)
+pltcl_fetch_interp(Oid prolang, bool pltrusted)
{
Oid user_id;
pltcl_interp_desc *interp_desc;
@@ -476,128 +564,114 @@ pltcl_fetch_interp(bool pltrusted)
HASH_ENTER,
&found);
if (!found)
- pltcl_init_interp(interp_desc, pltrusted);
+ interp_desc->interp = NULL;
+
+ /* If we haven't yet successfully made an interpreter, try to do that */
+ if (!interp_desc->interp)
+ pltcl_init_interp(interp_desc, prolang, pltrusted);
return interp_desc;
}
+
/**********************************************************************
- * pltcl_init_load_unknown() - Load the unknown procedure from
- * table pltcl_modules (if it exists)
+ * call_pltcl_start_proc() - Call user-defined initialization proc, if any
**********************************************************************/
static void
-pltcl_init_load_unknown(Tcl_Interp *interp)
+call_pltcl_start_proc(Oid prolang, bool pltrusted)
{
- Relation pmrel;
- char *pmrelname,
- *nspname;
- char *buf;
- int buflen;
- int spi_rc;
- int tcl_rc;
- Tcl_DString unknown_src;
- char *part;
- uint64 i;
- int fno;
+ char *start_proc;
+ const char *gucname;
+ ErrorContextCallback errcallback;
+ List *namelist;
+ Oid fargtypes[1]; /* dummy */
+ Oid procOid;
+ HeapTuple procTup;
+ Form_pg_proc procStruct;
+ AclResult aclresult;
+ FmgrInfo finfo;
+ FunctionCallInfoData fcinfo;
+ PgStat_FunctionCallUsage fcusage;
- /************************************************************
- * Check if table pltcl_modules exists
- *
- * We allow the table to be found anywhere in the search_path.
- * This is for backwards compatibility. To ensure that the table
- * is trustworthy, we require it to be owned by a superuser.
- ************************************************************/
- pmrel = relation_openrv_extended(makeRangeVar(NULL, "pltcl_modules", -1),
- AccessShareLock, true);
- if (pmrel == NULL)
- return;
- /* sanity-check the relation kind */
- if (!(pmrel->rd_rel->relkind == RELKIND_RELATION ||
- pmrel->rd_rel->relkind == RELKIND_MATVIEW ||
- pmrel->rd_rel->relkind == RELKIND_VIEW))
- {
- relation_close(pmrel, AccessShareLock);
- return;
- }
- /* must be owned by superuser, else ignore */
- if (!superuser_arg(pmrel->rd_rel->relowner))
- {
- relation_close(pmrel, AccessShareLock);
+ /* select appropriate GUC */
+ start_proc = pltrusted ? pltcl_start_proc : pltclu_start_proc;
+ gucname = pltrusted ? "pltcl.start_proc" : "pltclu.start_proc";
+
+ /* Nothing to do if it's empty or unset */
+ if (start_proc == NULL || start_proc[0] == '\0')
return;
- }
- /* get fully qualified table name for use in select command */
- nspname = get_namespace_name(RelationGetNamespace(pmrel));
- if (!nspname)
- elog(ERROR, "cache lookup failed for namespace %u",
- RelationGetNamespace(pmrel));
- pmrelname = quote_qualified_identifier(nspname,
- RelationGetRelationName(pmrel));
- /************************************************************
- * Read all the rows from it where modname = 'unknown',
- * in the order of modseq
- ************************************************************/
- buflen = strlen(pmrelname) + 100;
- buf = (char *) palloc(buflen);
- snprintf(buf, buflen,
- "select modsrc from %s where modname = 'unknown' order by modseq",
- pmrelname);
+ /* Set up errcontext callback to make errors more helpful */
+ errcallback.callback = start_proc_error_callback;
+ errcallback.arg = (void *) gucname;
+ errcallback.previous = error_context_stack;
+ error_context_stack = &errcallback;
- spi_rc = SPI_execute(buf, false, 0);
- if (spi_rc != SPI_OK_SELECT)
- elog(ERROR, "select from pltcl_modules failed");
+ /* Parse possibly-qualified identifier and look up the function */
+ namelist = stringToQualifiedNameList(start_proc);
+ procOid = LookupFuncName(namelist, 0, fargtypes, false);
- pfree(buf);
+ /* Current user must have permission to call function */
+ aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_PROC, start_proc);
- /************************************************************
- * If there's nothing, module unknown doesn't exist
- ************************************************************/
- if (SPI_processed == 0)
- {
- SPI_freetuptable(SPI_tuptable);
- ereport(WARNING,
- (errmsg("module \"unknown\" not found in pltcl_modules")));
- relation_close(pmrel, AccessShareLock);
- return;
- }
+ /* Get the function's pg_proc entry */
+ procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procOid));
+ if (!HeapTupleIsValid(procTup))
+ elog(ERROR, "cache lookup failed for function %u", procOid);
+ procStruct = (Form_pg_proc) GETSTRUCT(procTup);
- /************************************************************
- * There is a module named unknown. Reassemble the
- * source from the modsrc attributes and evaluate
- * it in the Tcl interpreter
- *
- * leave this code as DString - it's only executed once per session
- ************************************************************/
- fno = SPI_fnumber(SPI_tuptable->tupdesc, "modsrc");
+ /* It must be same language as the function we're currently calling */
+ if (procStruct->prolang != prolang)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("function \"%s\" is in the wrong language",
+ start_proc)));
- Tcl_DStringInit(&unknown_src);
+ /*
+ * It must not be SECURITY DEFINER, either. This together with the
+ * language match check ensures that the function will execute in the same
+ * Tcl interpreter we just finished initializing.
+ */
+ if (procStruct->prosecdef)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("function \"%s\" must not be SECURITY DEFINER",
+ start_proc)));
- for (i = 0; i < SPI_processed; i++)
- {
- part = SPI_getvalue(SPI_tuptable->vals[i],
- SPI_tuptable->tupdesc, fno);
- if (part != NULL)
- {
- UTF_BEGIN;
- Tcl_DStringAppend(&unknown_src, UTF_E2U(part), -1);
- UTF_END;
- pfree(part);
- }
- }
- tcl_rc = Tcl_EvalEx(interp, Tcl_DStringValue(&unknown_src),
- Tcl_DStringLength(&unknown_src),
- TCL_EVAL_GLOBAL);
+ /* A-OK */
+ ReleaseSysCache(procTup);
- Tcl_DStringFree(&unknown_src);
- SPI_freetuptable(SPI_tuptable);
+ /*
+ * Call the function using the normal SQL function call mechanism. We
+ * could perhaps cheat and jump directly to pltcl_handler(), but it seems
+ * better to do it this way so that the call is exposed to, eg, call
+ * statistics collection.
+ */
+ InvokeFunctionExecuteHook(procOid);
+ fmgr_info(procOid, &finfo);
+ InitFunctionCallInfoData(fcinfo, &finfo,
+ 0,
+ InvalidOid, NULL, NULL);
+ pgstat_init_function_usage(&fcinfo, &fcusage);
+ (void) FunctionCallInvoke(&fcinfo);
+ pgstat_end_function_usage(&fcusage, true);
+
+ /* Pop the error context stack */
+ error_context_stack = errcallback.previous;
+}
- if (tcl_rc != TCL_OK)
- ereport(ERROR,
- (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
- errmsg("could not load module \"unknown\": %s",
- utf_u2e(Tcl_GetStringResult(interp)))));
+/*
+ * Error context callback for errors occurring during start_proc processing.
+ */
+static void
+start_proc_error_callback(void *arg)
+{
+ const char *gucname = (const char *) arg;
- relation_close(pmrel, AccessShareLock);
+ /* translator: %s is "pltcl.start_proc" or "pltclu.start_proc" */
+ errcontext("processing %s parameter", gucname);
}
@@ -630,18 +704,33 @@ pltclu_call_handler(PG_FUNCTION_ARGS)
}
+/**********************************************************************
+ * pltcl_handler() - Handler for function and trigger calls, for
+ * both trusted and untrusted interpreters.
+ **********************************************************************/
static Datum
pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
{
Datum retval;
- FunctionCallInfo save_fcinfo;
- pltcl_proc_desc *save_prodesc;
+ pltcl_call_state current_call_state;
+ pltcl_call_state *save_call_state;
/*
- * Ensure that static pointers are saved/restored properly
+ * Initialize current_call_state to nulls/zeroes; in particular, set its
+ * prodesc pointer to null. Anything that sets it non-null should
+ * increase the prodesc's fn_refcount at the same time. We'll decrease
+ * the refcount, and then delete the prodesc if it's no longer referenced,
+ * on the way out of this function. This ensures that prodescs live as
+ * long as needed even if somebody replaces the originating pg_proc row
+ * while they're executing.
*/
- save_fcinfo = pltcl_current_fcinfo;
- save_prodesc = pltcl_current_prodesc;
+ memset(&current_call_state, 0, sizeof(current_call_state));
+
+ /*
+ * Ensure that static pointer is saved/restored properly
+ */
+ save_call_state = pltcl_current_call_state;
+ pltcl_current_call_state = &current_call_state;
PG_TRY();
{
@@ -651,31 +740,47 @@ pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
*/
if (CALLED_AS_TRIGGER(fcinfo))
{
- pltcl_current_fcinfo = NULL;
- retval = PointerGetDatum(pltcl_trigger_handler(fcinfo, pltrusted));
+ /* invoke the trigger handler */
+ retval = PointerGetDatum(pltcl_trigger_handler(fcinfo,
+ &current_call_state,
+ pltrusted));
}
else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
{
- pltcl_current_fcinfo = NULL;
- pltcl_event_trigger_handler(fcinfo, pltrusted);
+ /* invoke the event trigger handler */
+ pltcl_event_trigger_handler(fcinfo, &current_call_state, pltrusted);
retval = (Datum) 0;
}
else
{
- pltcl_current_fcinfo = fcinfo;
- retval = pltcl_func_handler(fcinfo, pltrusted);
+ /* invoke the regular function handler */
+ current_call_state.fcinfo = fcinfo;
+ retval = pltcl_func_handler(fcinfo, &current_call_state, pltrusted);
}
}
PG_CATCH();
{
- pltcl_current_fcinfo = save_fcinfo;
- pltcl_current_prodesc = save_prodesc;
+ /* Restore static pointer, then clean up the prodesc refcount if any */
+ pltcl_current_call_state = save_call_state;
+ if (current_call_state.prodesc != NULL)
+ {
+ Assert(current_call_state.prodesc->fn_refcount > 0);
+ if (--current_call_state.prodesc->fn_refcount == 0)
+ MemoryContextDelete(current_call_state.prodesc->fn_cxt);
+ }
PG_RE_THROW();
}
PG_END_TRY();
- pltcl_current_fcinfo = save_fcinfo;
- pltcl_current_prodesc = save_prodesc;
+ /* Restore static pointer, then clean up the prodesc refcount if any */
+ /* (We're being paranoid in case an error is thrown in context deletion) */
+ pltcl_current_call_state = save_call_state;
+ if (current_call_state.prodesc != NULL)
+ {
+ Assert(current_call_state.prodesc->fn_refcount > 0);
+ if (--current_call_state.prodesc->fn_refcount == 0)
+ MemoryContextDelete(current_call_state.prodesc->fn_cxt);
+ }
return retval;
}
@@ -685,7 +790,8 @@ pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
* pltcl_func_handler() - Handler for regular function calls
**********************************************************************/
static Datum
-pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted)
+pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
+ bool pltrusted)
{
pltcl_proc_desc *prodesc;
Tcl_Interp *volatile interp;
@@ -702,10 +808,32 @@ pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted)
prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
false, pltrusted);
- pltcl_current_prodesc = prodesc;
+ call_state->prodesc = prodesc;
+ prodesc->fn_refcount++;
interp = prodesc->interp_desc->interp;
+ /*
+ * If we're a SRF, check caller can handle materialize mode, and save
+ * relevant info into call_state. We must ensure that the returned
+ * tuplestore is owned by the caller's context, even if we first create it
+ * inside a subtransaction.
+ */
+ if (prodesc->fn_retisset)
+ {
+ ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+
+ if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+ (rsi->allowedModes & SFRM_Materialize) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+
+ call_state->rsi = rsi;
+ call_state->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory;
+ call_state->tuple_store_owner = CurrentResourceOwner;
+ }
+
/************************************************************
* Create the tcl command to call the internal
* proc in the Tcl interpreter
@@ -814,11 +942,72 @@ pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted)
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish() failed");
- if (fcinfo->isnull)
+ if (prodesc->fn_retisset)
+ {
+ ReturnSetInfo *rsi = call_state->rsi;
+
+ /* We already checked this is OK */
+ rsi->returnMode = SFRM_Materialize;
+
+ /* If we produced any tuples, send back the result */
+ if (call_state->tuple_store)
+ {
+ rsi->setResult = call_state->tuple_store;
+ if (call_state->ret_tupdesc)
+ {
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(call_state->tuple_store_cxt);
+ rsi->setDesc = CreateTupleDescCopy(call_state->ret_tupdesc);
+ MemoryContextSwitchTo(oldcxt);
+ }
+ }
+ retval = (Datum) 0;
+ fcinfo->isnull = true;
+ }
+ else if (fcinfo->isnull)
+ {
retval = InputFunctionCall(&prodesc->result_in_func,
NULL,
prodesc->result_typioparam,
-1);
+ }
+ else if (prodesc->fn_retistuple)
+ {
+ TupleDesc td;
+ HeapTuple tup;
+ Tcl_Obj *resultObj;
+ Tcl_Obj **resultObjv;
+ int resultObjc;
+
+ /*
+ * Set up data about result type. XXX it's tempting to consider
+ * caching this in the prodesc, in the common case where the rowtype
+ * is determined by the function not the calling query. But we'd have
+ * to be able to deal with ADD/DROP/ALTER COLUMN events when the
+ * result type is a named composite type, so it's not exactly trivial.
+ * Maybe worth improving someday.
+ */
+ if (get_call_result_type(fcinfo, NULL, &td) != TYPEFUNC_COMPOSITE)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("function returning record called in context "
+ "that cannot accept type record")));
+
+ Assert(!call_state->ret_tupdesc);
+ Assert(!call_state->attinmeta);
+ call_state->ret_tupdesc = td;
+ call_state->attinmeta = TupleDescGetAttInMetadata(td);
+
+ /* Convert function result to tuple */
+ resultObj = Tcl_GetObjResult(interp);
+ if (Tcl_ListObjGetElements(interp, resultObj, &resultObjc, &resultObjv) == TCL_ERROR)
+ throw_tcl_error(interp, prodesc->user_proname);
+
+ tup = pltcl_build_tuple_result(interp, resultObjv, resultObjc,
+ call_state);
+ retval = HeapTupleGetDatum(tup);
+ }
else
retval = InputFunctionCall(&prodesc->result_in_func,
utf_u2e(Tcl_GetStringResult(interp)),
@@ -833,7 +1022,8 @@ pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted)
* pltcl_trigger_handler() - Handler for trigger calls
**********************************************************************/
static HeapTuple
-pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
+pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
+ bool pltrusted)
{
pltcl_proc_desc *prodesc;
Tcl_Interp *volatile interp;
@@ -846,28 +1036,33 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
Tcl_Obj *tcl_newtup;
int tcl_rc;
int i;
- int *modattrs;
- Datum *modvalues;
- char *modnulls;
- int ret_numvals;
const char *result;
- const char **ret_values;
+ int result_Objc;
+ Tcl_Obj **result_Objv;
+ int rc PG_USED_FOR_ASSERTS_ONLY;
+
+ call_state->trigdata = trigdata;
/* Connect to SPI manager */
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "could not connect to SPI manager");
+ /* Make transition tables visible to this SPI connection */
+ rc = SPI_register_trigger_data(trigdata);
+ Assert(rc >= 0);
+
/* Find or compile the function */
prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
RelationGetRelid(trigdata->tg_relation),
false, /* not an event trigger */
pltrusted);
- pltcl_current_prodesc = prodesc;
+ call_state->prodesc = prodesc;
+ prodesc->fn_refcount++;
interp = prodesc->interp_desc->interp;
- tupdesc = trigdata->tg_relation->rd_att;
+ tupdesc = RelationGetDescr(trigdata->tg_relation);
/************************************************************
* Create the tcl command to call the internal
@@ -1040,13 +1235,16 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
throw_tcl_error(interp, prodesc->user_proname);
/************************************************************
- * The return value from the procedure might be one of
- * the magic strings OK or SKIP or a list from array get.
- * We can check for OK or SKIP without worrying about encoding.
+ * Exit SPI environment.
************************************************************/
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish() failed");
+ /************************************************************
+ * The return value from the procedure might be one of
+ * the magic strings OK or SKIP, or a list from array get.
+ * We can check for OK or SKIP without worrying about encoding.
+ ************************************************************/
result = Tcl_GetStringResult(interp);
if (strcmp(result, "OK") == 0)
@@ -1055,106 +1253,19 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
return (HeapTuple) NULL;
/************************************************************
- * Convert the result value from the Tcl interpreter
- * and setup structures for SPI_modifytuple();
+ * Otherwise, the return value should be a column name/value list
+ * specifying the modified tuple to return.
************************************************************/
- if (Tcl_SplitList(interp, result,
- &ret_numvals, &ret_values) != TCL_OK)
+ if (Tcl_ListObjGetElements(interp, Tcl_GetObjResult(interp),
+ &result_Objc, &result_Objv) != TCL_OK)
ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
errmsg("could not split return value from trigger: %s",
utf_u2e(Tcl_GetStringResult(interp)))));
- /* Use a TRY to ensure ret_values will get freed */
- PG_TRY();
- {
- if (ret_numvals % 2 != 0)
- ereport(ERROR,
- (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
- errmsg("trigger's return list must have even number of elements")));
-
- modattrs = (int *) palloc(tupdesc->natts * sizeof(int));
- modvalues = (Datum *) palloc(tupdesc->natts * sizeof(Datum));
- for (i = 0; i < tupdesc->natts; i++)
- {
- modattrs[i] = i + 1;
- modvalues[i] = (Datum) NULL;
- }
-
- modnulls = palloc(tupdesc->natts);
- memset(modnulls, 'n', tupdesc->natts);
-
- for (i = 0; i < ret_numvals; i += 2)
- {
- char *ret_name = utf_u2e(ret_values[i]);
- char *ret_value = utf_u2e(ret_values[i + 1]);
- int attnum;
- Oid typinput;
- Oid typioparam;
- FmgrInfo finfo;
-
- /************************************************************
- * Ignore ".tupno" pseudo elements (see pltcl_set_tuple_values)
- ************************************************************/
- if (strcmp(ret_name, ".tupno") == 0)
- continue;
-
- /************************************************************
- * Get the attribute number
- ************************************************************/
- attnum = SPI_fnumber(tupdesc, ret_name);
- if (attnum == SPI_ERROR_NOATTRIBUTE)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("unrecognized attribute \"%s\"",
- ret_name)));
- if (attnum <= 0)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot set system attribute \"%s\"",
- ret_name)));
-
- /************************************************************
- * Ignore dropped columns
- ************************************************************/
- if (tupdesc->attrs[attnum - 1]->attisdropped)
- continue;
-
- /************************************************************
- * Lookup the attribute type in the syscache
- * for the input function
- ************************************************************/
- getTypeInputInfo(tupdesc->attrs[attnum - 1]->atttypid,
- &typinput, &typioparam);
- fmgr_info(typinput, &finfo);
-
- /************************************************************
- * Set the attribute to NOT NULL and convert the contents
- ************************************************************/
- modvalues[attnum - 1] = InputFunctionCall(&finfo,
- ret_value,
- typioparam,
- tupdesc->attrs[attnum - 1]->atttypmod);
- modnulls[attnum - 1] = ' ';
- }
-
- rettup = SPI_modifytuple(trigdata->tg_relation, rettup, tupdesc->natts,
- modattrs, modvalues, modnulls);
-
- pfree(modattrs);
- pfree(modvalues);
- pfree(modnulls);
-
- if (rettup == NULL)
- elog(ERROR, "SPI_modifytuple() failed - RC = %d", SPI_result);
- }
- PG_CATCH();
- {
- ckfree((char *) ret_values);
- PG_RE_THROW();
- }
- PG_END_TRY();
- ckfree((char *) ret_values);
+ /* Convert function result to tuple */
+ rettup = pltcl_build_tuple_result(interp, result_Objv, result_Objc,
+ call_state);
return rettup;
}
@@ -1163,7 +1274,8 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
* pltcl_event_trigger_handler() - Handler for event trigger calls
**********************************************************************/
static void
-pltcl_event_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
+pltcl_event_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
+ bool pltrusted)
{
pltcl_proc_desc *prodesc;
Tcl_Interp *volatile interp;
@@ -1179,7 +1291,8 @@ pltcl_event_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
InvalidOid, true, pltrusted);
- pltcl_current_prodesc = prodesc;
+ call_state->prodesc = prodesc;
+ prodesc->fn_refcount++;
interp = prodesc->interp_desc->interp;
@@ -1249,6 +1362,10 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
pltcl_proc_ptr *proc_ptr;
bool found;
pltcl_proc_desc *prodesc;
+ pltcl_proc_desc *old_prodesc;
+ volatile MemoryContext proc_cxt = NULL;
+ Tcl_DString proc_internal_def;
+ Tcl_DString proc_internal_body;
/* We'll need the pg_proc tuple in any case... */
procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
@@ -1256,7 +1373,10 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
elog(ERROR, "cache lookup failed for function %u", fn_oid);
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
- /* Try to find function in pltcl_proc_htab */
+ /*
+ * Look up function in pltcl_proc_htab; if it's not there, create an entry
+ * and set the entry's proc_ptr to NULL.
+ */
proc_key.proc_id = fn_oid;
proc_key.is_trigger = OidIsValid(tgreloid);
proc_key.user_id = pltrusted ? GetUserId() : InvalidOid;
@@ -1274,18 +1394,13 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
* This is needed because CREATE OR REPLACE FUNCTION can modify the
* function's pg_proc entry without changing its OID.
************************************************************/
- if (prodesc != NULL)
+ if (prodesc != NULL &&
+ prodesc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
+ ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self))
{
- bool uptodate;
-
- uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
- ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
-
- if (!uptodate)
- {
- proc_ptr->proc_ptr = NULL;
- prodesc = NULL;
- }
+ /* It's still up-to-date, so we can use it */
+ ReleaseSysCache(procTup);
+ return prodesc;
}
/************************************************************
@@ -1296,14 +1411,14 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
*
* Then we load the procedure into the Tcl interpreter.
************************************************************/
- if (prodesc == NULL)
+ Tcl_DStringInit(&proc_internal_def);
+ Tcl_DStringInit(&proc_internal_body);
+ PG_TRY();
{
bool is_trigger = OidIsValid(tgreloid);
char internal_proname[128];
HeapTuple typeTup;
Form_pg_type typeStruct;
- Tcl_DString proc_internal_def;
- Tcl_DString proc_internal_body;
char proc_internal_args[33 * FUNC_MAX_ARGS];
Datum prosrcdatum;
bool isnull;
@@ -1312,39 +1427,47 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
Tcl_Interp *interp;
int i;
int tcl_rc;
+ MemoryContext oldcontext;
/************************************************************
* Build our internal proc name from the function's Oid. Append
* "_trigger" when appropriate to ensure the normal and trigger
* cases are kept separate. Note name must be all-ASCII.
************************************************************/
- if (!is_trigger && !is_event_trigger)
- snprintf(internal_proname, sizeof(internal_proname),
- "__PLTcl_proc_%u", fn_oid);
- else if (is_event_trigger)
+ if (is_event_trigger)
snprintf(internal_proname, sizeof(internal_proname),
"__PLTcl_proc_%u_evttrigger", fn_oid);
else if (is_trigger)
snprintf(internal_proname, sizeof(internal_proname),
"__PLTcl_proc_%u_trigger", fn_oid);
+ else
+ snprintf(internal_proname, sizeof(internal_proname),
+ "__PLTcl_proc_%u", fn_oid);
/************************************************************
- * Allocate a new procedure description block
+ * Allocate a context that will hold all PG data for the procedure.
+ * We use the internal proc name as the context name.
************************************************************/
- prodesc = (pltcl_proc_desc *) malloc(sizeof(pltcl_proc_desc));
- if (prodesc == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("out of memory")));
- MemSet(prodesc, 0, sizeof(pltcl_proc_desc));
- prodesc->user_proname = strdup(NameStr(procStruct->proname));
- prodesc->internal_proname = strdup(internal_proname);
- if (prodesc->user_proname == NULL || prodesc->internal_proname == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("out of memory")));
+ proc_cxt = AllocSetContextCreate(TopMemoryContext,
+ internal_proname,
+ ALLOCSET_SMALL_SIZES);
+
+ /************************************************************
+ * Allocate and fill a new procedure description block.
+ * struct prodesc and subsidiary data must all live in proc_cxt.
+ ************************************************************/
+ oldcontext = MemoryContextSwitchTo(proc_cxt);
+ prodesc = (pltcl_proc_desc *) palloc0(sizeof(pltcl_proc_desc));
+ prodesc->user_proname = pstrdup(NameStr(procStruct->proname));
+ prodesc->internal_proname = pstrdup(internal_proname);
+ prodesc->fn_cxt = proc_cxt;
+ prodesc->fn_refcount = 0;
prodesc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
prodesc->fn_tid = procTup->t_self;
+ prodesc->nargs = procStruct->pronargs;
+ prodesc->arg_out_func = (FmgrInfo *) palloc0(prodesc->nargs * sizeof(FmgrInfo));
+ prodesc->arg_is_rowtype = (bool *) palloc0(prodesc->nargs * sizeof(bool));
+ MemoryContextSwitchTo(oldcontext);
/* Remember if function is STABLE/IMMUTABLE */
prodesc->fn_readonly =
@@ -1355,7 +1478,8 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
/************************************************************
* Identify the interpreter to use for the function
************************************************************/
- prodesc->interp_desc = pltcl_fetch_interp(prodesc->lanpltrusted);
+ prodesc->interp_desc = pltcl_fetch_interp(procStruct->prolang,
+ prodesc->lanpltrusted);
interp = prodesc->interp_desc->interp;
/************************************************************
@@ -1368,91 +1492,62 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
SearchSysCache1(TYPEOID,
ObjectIdGetDatum(procStruct->prorettype));
if (!HeapTupleIsValid(typeTup))
- {
- free(prodesc->user_proname);
- free(prodesc->internal_proname);
- free(prodesc);
elog(ERROR, "cache lookup failed for type %u",
procStruct->prorettype);
- }
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
- /* Disallow pseudotype result, except VOID */
+ /* Disallow pseudotype result, except VOID and RECORD */
if (typeStruct->typtype == TYPTYPE_PSEUDO)
{
- if (procStruct->prorettype == VOIDOID)
+ if (procStruct->prorettype == VOIDOID ||
+ procStruct->prorettype == RECORDOID)
/* okay */ ;
else if (procStruct->prorettype == TRIGGEROID ||
procStruct->prorettype == EVTTRIGGEROID)
- {
- free(prodesc->user_proname);
- free(prodesc->internal_proname);
- free(prodesc);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("trigger functions can only be called as triggers")));
- }
else
- {
- free(prodesc->user_proname);
- free(prodesc->internal_proname);
- free(prodesc);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("PL/Tcl functions cannot return type %s",
format_type_be(procStruct->prorettype))));
- }
}
- if (typeStruct->typtype == TYPTYPE_COMPOSITE)
- {
- free(prodesc->user_proname);
- free(prodesc->internal_proname);
- free(prodesc);
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("PL/Tcl functions cannot return composite types")));
- }
-
- perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func));
+ fmgr_info_cxt(typeStruct->typinput,
+ &(prodesc->result_in_func),
+ proc_cxt);
prodesc->result_typioparam = getTypeIOParam(typeTup);
+ prodesc->fn_retisset = procStruct->proretset;
+ prodesc->fn_retistuple = (procStruct->prorettype == RECORDOID ||
+ typeStruct->typtype == TYPTYPE_COMPOSITE);
+
ReleaseSysCache(typeTup);
}
/************************************************************
* Get the required information for output conversion
- * of all procedure arguments
+ * of all procedure arguments, and set up argument naming info.
************************************************************/
if (!is_trigger && !is_event_trigger)
{
- prodesc->nargs = procStruct->pronargs;
proc_internal_args[0] = '\0';
for (i = 0; i < prodesc->nargs; i++)
{
typeTup = SearchSysCache1(TYPEOID,
ObjectIdGetDatum(procStruct->proargtypes.values[i]));
if (!HeapTupleIsValid(typeTup))
- {
- free(prodesc->user_proname);
- free(prodesc->internal_proname);
- free(prodesc);
elog(ERROR, "cache lookup failed for type %u",
procStruct->proargtypes.values[i]);
- }
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
/* Disallow pseudotype argument */
if (typeStruct->typtype == TYPTYPE_PSEUDO)
- {
- free(prodesc->user_proname);
- free(prodesc->internal_proname);
- free(prodesc);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("PL/Tcl functions cannot accept type %s",
format_type_be(procStruct->proargtypes.values[i]))));
- }
if (typeStruct->typtype == TYPTYPE_COMPOSITE)
{
@@ -1462,8 +1557,9 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
else
{
prodesc->arg_is_rowtype[i] = false;
- perm_fmgr_info(typeStruct->typoutput,
- &(prodesc->arg_out_func[i]));
+ fmgr_info_cxt(typeStruct->typoutput,
+ &(prodesc->arg_out_func[i]),
+ proc_cxt);
snprintf(buf, sizeof(buf), "%d", i + 1);
}
@@ -1490,12 +1586,10 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
* Create the tcl command to define the internal
* procedure
*
- * leave this code as DString - it's a text processing function
- * that only gets invoked when the tcl function is invoked
- * for the first time
+ * Leave this code as DString - performance is not critical here,
+ * and we don't want to duplicate the knowledge of the Tcl quoting
+ * rules that's embedded in Tcl_DStringAppendElement.
************************************************************/
- Tcl_DStringInit(&proc_internal_def);
- Tcl_DStringInit(&proc_internal_body);
Tcl_DStringAppendElement(&proc_internal_def, "proc");
Tcl_DStringAppendElement(&proc_internal_def, internal_proname);
Tcl_DStringAppendElement(&proc_internal_def, proc_internal_args);
@@ -1514,7 +1608,6 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
"array set NEW $__PLTcl_Tup_NEW\n", -1);
Tcl_DStringAppend(&proc_internal_body,
"array set OLD $__PLTcl_Tup_OLD\n", -1);
-
Tcl_DStringAppend(&proc_internal_body,
"set i 0\n"
"set v 0\n"
@@ -1556,7 +1649,6 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
pfree(proc_source);
Tcl_DStringAppendElement(&proc_internal_def,
Tcl_DStringValue(&proc_internal_body));
- Tcl_DStringFree(&proc_internal_body);
/************************************************************
* Create the procedure in the interpreter
@@ -1565,28 +1657,52 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
Tcl_DStringValue(&proc_internal_def),
Tcl_DStringLength(&proc_internal_def),
TCL_EVAL_GLOBAL);
- Tcl_DStringFree(&proc_internal_def);
if (tcl_rc != TCL_OK)
- {
- free(prodesc->user_proname);
- free(prodesc->internal_proname);
- free(prodesc);
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
errmsg("could not create internal procedure \"%s\": %s",
internal_proname,
utf_u2e(Tcl_GetStringResult(interp)))));
- }
+ }
+ PG_CATCH();
+ {
+ /*
+ * If we failed anywhere above, clean up whatever got allocated. It
+ * should all be in the proc_cxt, except for the DStrings.
+ */
+ if (proc_cxt)
+ MemoryContextDelete(proc_cxt);
+ Tcl_DStringFree(&proc_internal_def);
+ Tcl_DStringFree(&proc_internal_body);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
- /************************************************************
- * Add the proc description block to the hashtable. Note we do not
- * attempt to free any previously existing prodesc block. This is
- * annoying, but necessary since there could be active calls using
- * the old prodesc.
- ************************************************************/
- proc_ptr->proc_ptr = prodesc;
+ /*
+ * Install the new proc description block in the hashtable, incrementing
+ * its refcount (the hashtable link counts as a reference). Then, if
+ * there was a previous definition of the function, decrement that one's
+ * refcount, and delete it if no longer referenced. The order of
+ * operations here is important: if something goes wrong during the
+ * MemoryContextDelete, leaking some memory for the old definition is OK,
+ * but we don't want to corrupt the live hashtable entry. (Likewise,
+ * freeing the DStrings is pretty low priority if that happens.)
+ */
+ old_prodesc = proc_ptr->proc_ptr;
+
+ proc_ptr->proc_ptr = prodesc;
+ prodesc->fn_refcount++;
+
+ if (old_prodesc != NULL)
+ {
+ Assert(old_prodesc->fn_refcount > 0);
+ if (--old_prodesc->fn_refcount == 0)
+ MemoryContextDelete(old_prodesc->fn_cxt);
}
+ Tcl_DStringFree(&proc_internal_def);
+ Tcl_DStringFree(&proc_internal_body);
+
ReleaseSysCache(procTup);
return prodesc;
@@ -1905,7 +2021,7 @@ pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[])
{
int argno;
- FunctionCallInfo fcinfo = pltcl_current_fcinfo;
+ FunctionCallInfo fcinfo = pltcl_current_call_state->fcinfo;
/************************************************************
* Check call syntax
@@ -1958,7 +2074,7 @@ static int
pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[])
{
- FunctionCallInfo fcinfo = pltcl_current_fcinfo;
+ FunctionCallInfo fcinfo = pltcl_current_call_state->fcinfo;
/************************************************************
* Check call syntax
@@ -1989,6 +2105,108 @@ pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
}
+/**********************************************************************
+ * pltcl_returnnext() - Add a row to the result tuplestore in a SRF.
+ **********************************************************************/
+static int
+pltcl_returnnext(ClientData cdata, Tcl_Interp *interp,
+ int objc, Tcl_Obj *const objv[])
+{
+ pltcl_call_state *call_state = pltcl_current_call_state;
+ FunctionCallInfo fcinfo = call_state->fcinfo;
+ pltcl_proc_desc *prodesc = call_state->prodesc;
+ MemoryContext oldcontext = CurrentMemoryContext;
+ ResourceOwner oldowner = CurrentResourceOwner;
+ volatile int result = TCL_OK;
+
+ /*
+ * Check that we're called as a set-returning function
+ */
+ if (fcinfo == NULL)
+ {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("return_next cannot be used in triggers", -1));
+ return TCL_ERROR;
+ }
+
+ if (!prodesc->fn_retisset)
+ {
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("return_next cannot be used in non-set-returning functions", -1));
+ return TCL_ERROR;
+ }
+
+ /*
+ * Check call syntax
+ */
+ if (objc != 2)
+ {
+ Tcl_WrongNumArgs(interp, 1, objv, "result");
+ return TCL_ERROR;
+ }
+
+ /*
+ * The rest might throw elog(ERROR), so must run in a subtransaction.
+ *
+ * A small advantage of using a subtransaction is that it provides a
+ * short-lived memory context for free, so we needn't worry about leaking
+ * memory here. To use that context, call BeginInternalSubTransaction
+ * directly instead of going through pltcl_subtrans_begin.
+ */
+ BeginInternalSubTransaction(NULL);
+ PG_TRY();
+ {
+ /* Set up tuple store if first output row */
+ if (call_state->tuple_store == NULL)
+ pltcl_init_tuple_store(call_state);
+
+ if (prodesc->fn_retistuple)
+ {
+ Tcl_Obj **rowObjv;
+ int rowObjc;
+
+ /* result should be a list, so break it down */
+ if (Tcl_ListObjGetElements(interp, objv[1], &rowObjc, &rowObjv) == TCL_ERROR)
+ result = TCL_ERROR;
+ else
+ {
+ HeapTuple tuple;
+
+ tuple = pltcl_build_tuple_result(interp, rowObjv, rowObjc,
+ call_state);
+ tuplestore_puttuple(call_state->tuple_store, tuple);
+ }
+ }
+ else
+ {
+ Datum retval;
+ bool isNull = false;
+
+ /* for paranoia's sake, check that tupdesc has exactly one column */
+ if (call_state->ret_tupdesc->natts != 1)
+ elog(ERROR, "wrong result type supplied in return_next");
+
+ retval = InputFunctionCall(&prodesc->result_in_func,
+ utf_u2e((char *) Tcl_GetString(objv[1])),
+ prodesc->result_typioparam,
+ -1);
+ tuplestore_putvalues(call_state->tuple_store, call_state->ret_tupdesc,
+ &retval, &isNull);
+ }
+
+ pltcl_subtrans_commit(oldcontext, oldowner);
+ }
+ PG_CATCH();
+ {
+ pltcl_subtrans_abort(interp, oldcontext, oldowner);
+ return TCL_ERROR;
+ }
+ PG_END_TRY();
+
+ return result;
+}
+
+
/*----------
* Support for running SPI operations inside subtransactions
*
@@ -2029,12 +2247,6 @@ pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
ReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
-
- /*
- * AtEOSubXact_SPI() should not have popped any SPI context, but just in
- * case it did, make sure we remain connected.
- */
- SPI_restore_connection();
}
static void
@@ -2053,13 +2265,6 @@ pltcl_subtrans_abort(Tcl_Interp *interp,
MemoryContextSwitchTo(oldcontext);
CurrentResourceOwner = oldowner;
- /*
- * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
- * have left us in a disconnected state. We need this hack to return to
- * connected state.
- */
- SPI_restore_connection();
-
/* Pass the error data to Tcl */
pltcl_construct_errorCode(interp, edata);
UTF_BEGIN;
@@ -2110,7 +2315,7 @@ pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp,
i = 1;
while (i < objc)
{
- if (Tcl_GetIndexFromObj(interp, objv[i], options, "option",
+ if (Tcl_GetIndexFromObj(NULL, objv[i], options, NULL,
TCL_EXACT, &optIndex) != TCL_OK)
break;
@@ -2155,7 +2360,7 @@ pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp,
{
UTF_BEGIN;
spi_rc = SPI_execute(UTF_U2E(Tcl_GetString(objv[query_idx])),
- pltcl_current_prodesc->fn_readonly, count);
+ pltcl_current_call_state->prodesc->fn_readonly, count);
UTF_END;
my_rc = pltcl_process_SPI_result(interp,
@@ -2330,10 +2535,8 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
* occur. FIXME someday.
************************************************************/
plan_cxt = AllocSetContextCreate(TopMemoryContext,
- "PL/TCL spi_prepare query",
- ALLOCSET_SMALL_MINSIZE,
- ALLOCSET_SMALL_INITSIZE,
- ALLOCSET_SMALL_MAXSIZE);
+ "PL/Tcl spi_prepare query",
+ ALLOCSET_SMALL_SIZES);
MemoryContextSwitchTo(plan_cxt);
qdesc = (pltcl_query_desc *) palloc0(sizeof(pltcl_query_desc));
snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
@@ -2407,7 +2610,7 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
* Insert a hashtable entry for the plan and return
* the key to the caller
************************************************************/
- query_hash = &pltcl_current_prodesc->interp_desc->query_hash;
+ query_hash = &pltcl_current_call_state->prodesc->interp_desc->query_hash;
hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
Tcl_SetHashValue(hashent, (ClientData) qdesc);
@@ -2458,7 +2661,7 @@ pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
i = 1;
while (i < objc)
{
- if (Tcl_GetIndexFromObj(interp, objv[i], options, "option",
+ if (Tcl_GetIndexFromObj(NULL, objv[i], options, NULL,
TCL_EXACT, &optIndex) != TCL_OK)
break;
@@ -2496,7 +2699,7 @@ pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
return TCL_ERROR;
}
- query_hash = &pltcl_current_prodesc->interp_desc->query_hash;
+ query_hash = &pltcl_current_call_state->prodesc->interp_desc->query_hash;
hashent = Tcl_FindHashEntry(query_hash, Tcl_GetString(objv[i]));
if (hashent == NULL)
@@ -2611,7 +2814,8 @@ pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
* Execute the plan
************************************************************/
spi_rc = SPI_execute_plan(qdesc->plan, argvalues, nulls,
- pltcl_current_prodesc->fn_readonly, count);
+ pltcl_current_call_state->prodesc->fn_readonly,
+ count);
my_rc = pltcl_process_SPI_result(interp,
arrayname,
@@ -2641,12 +2845,70 @@ static int
pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[])
{
+ /*
+ * Check call syntax
+ */
+ if (objc != 1)
+ {
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(SPI_lastoid));
return TCL_OK;
}
/**********************************************************************
+ * pltcl_subtransaction() - Execute some Tcl code in a subtransaction
+ *
+ * The subtransaction is aborted if the Tcl code fragment returns TCL_ERROR,
+ * otherwise it's subcommitted.
+ **********************************************************************/
+static int
+pltcl_subtransaction(ClientData cdata, Tcl_Interp *interp,
+ int objc, Tcl_Obj *const objv[])
+{
+ MemoryContext oldcontext = CurrentMemoryContext;
+ ResourceOwner oldowner = CurrentResourceOwner;
+ int retcode;
+
+ if (objc != 2)
+ {
+ Tcl_WrongNumArgs(interp, 1, objv, "command");
+ return TCL_ERROR;
+ }
+
+ /*
+ * Note: we don't use pltcl_subtrans_begin and friends here because we
+ * don't want the error handling in pltcl_subtrans_abort. But otherwise
+ * the processing should be about the same as in those functions.
+ */
+ BeginInternalSubTransaction(NULL);
+ MemoryContextSwitchTo(oldcontext);
+
+ retcode = Tcl_EvalObjEx(interp, objv[1], 0);
+
+ if (retcode == TCL_ERROR)
+ {
+ /* Rollback the subtransaction */
+ RollbackAndReleaseCurrentSubTransaction();
+ }
+ else
+ {
+ /* Commit the subtransaction */
+ ReleaseCurrentSubTransaction();
+ }
+
+ /* In either case, restore previous memory context and resource owner */
+ MemoryContextSwitchTo(oldcontext);
+ CurrentResourceOwner = oldowner;
+
+ return retcode;
+}
+
+
+/**********************************************************************
* pltcl_set_tuple_values() - Set variables for all attributes
* of a given tuple
*
@@ -2668,8 +2930,7 @@ pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
const char *nullname = NULL;
/************************************************************
- * Prepare pointers for Tcl_SetVar2() below and in array
- * mode set the .tupno element
+ * Prepare pointers for Tcl_SetVar2() below
************************************************************/
if (arrayname == NULL)
{
@@ -2680,6 +2941,12 @@ pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
{
arrptr = &arrayname;
nameptr = &attname;
+
+ /*
+ * When outputting to an array, fill the ".tupno" element with the
+ * current tuple number. This will be overridden below if ".tupno" is
+ * in use as an actual field name in the rowtype.
+ */
Tcl_SetVar2Ex(interp, arrayname, ".tupno", Tcl_NewWideIntObj(tupno), 0);
}
@@ -2787,3 +3054,121 @@ pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc)
return retobj;
}
+
+/**********************************************************************
+ * pltcl_build_tuple_result() - Build a tuple of function's result rowtype
+ * from a Tcl list of column names and values
+ *
+ * In a trigger function, we build a tuple of the trigger table's rowtype.
+ *
+ * Note: this function leaks memory. Even if we made it clean up its own
+ * mess, there's no way to prevent the datatype input functions it calls
+ * from leaking. Run it in a short-lived context, unless we're about to
+ * exit the procedure anyway.
+ **********************************************************************/
+static HeapTuple
+pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc,
+ pltcl_call_state *call_state)
+{
+ TupleDesc tupdesc;
+ AttInMetadata *attinmeta;
+ char **values;
+ int i;
+
+ if (call_state->ret_tupdesc)
+ {
+ tupdesc = call_state->ret_tupdesc;
+ attinmeta = call_state->attinmeta;
+ }
+ else if (call_state->trigdata)
+ {
+ tupdesc = RelationGetDescr(call_state->trigdata->tg_relation);
+ attinmeta = TupleDescGetAttInMetadata(tupdesc);
+ }
+ else
+ {
+ elog(ERROR, "PL/Tcl function does not return a tuple");
+ tupdesc = NULL; /* keep compiler quiet */
+ attinmeta = NULL;
+ }
+
+ values = (char **) palloc0(tupdesc->natts * sizeof(char *));
+
+ if (kvObjc % 2 != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("column name/value list must have even number of elements")));
+
+ for (i = 0; i < kvObjc; i += 2)
+ {
+ char *fieldName = utf_u2e(Tcl_GetString(kvObjv[i]));
+ int attn = SPI_fnumber(tupdesc, fieldName);
+
+ /*
+ * We silently ignore ".tupno", if it's present but doesn't match any
+ * actual output column. This allows direct use of a row returned by
+ * pltcl_set_tuple_values().
+ */
+ if (attn == SPI_ERROR_NOATTRIBUTE)
+ {
+ if (strcmp(fieldName, ".tupno") == 0)
+ continue;
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column name/value list contains nonexistent column name \"%s\"",
+ fieldName)));
+ }
+
+ if (attn <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot set system attribute \"%s\"",
+ fieldName)));
+
+ values[attn - 1] = utf_u2e(Tcl_GetString(kvObjv[i + 1]));
+ }
+
+ return BuildTupleFromCStrings(attinmeta, values);
+}
+
+/**********************************************************************
+ * pltcl_init_tuple_store() - Initialize the result tuplestore for a SRF
+ **********************************************************************/
+static void
+pltcl_init_tuple_store(pltcl_call_state *call_state)
+{
+ ReturnSetInfo *rsi = call_state->rsi;
+ MemoryContext oldcxt;
+ ResourceOwner oldowner;
+
+ /* Should be in a SRF */
+ Assert(rsi);
+ /* Should be first time through */
+ Assert(!call_state->tuple_store);
+ Assert(!call_state->attinmeta);
+
+ /* We expect caller to provide an appropriate result tupdesc */
+ Assert(rsi->expectedDesc);
+ call_state->ret_tupdesc = rsi->expectedDesc;
+
+ /*
+ * Switch to the right memory context and resource owner for storing the
+ * tuplestore. If we're within a subtransaction opened for an exception
+ * block, for example, we must still create the tuplestore in the resource
+ * owner that was active when this function was entered, and not in the
+ * subtransaction's resource owner.
+ */
+ oldcxt = MemoryContextSwitchTo(call_state->tuple_store_cxt);
+ oldowner = CurrentResourceOwner;
+ CurrentResourceOwner = call_state->tuple_store_owner;
+
+ call_state->tuple_store =
+ tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+ false, work_mem);
+
+ /* Build attinmeta in this context, too */
+ call_state->attinmeta = TupleDescGetAttInMetadata(call_state->ret_tupdesc);
+
+ CurrentResourceOwner = oldowner;
+ MemoryContextSwitchTo(oldcxt);
+}
diff --git a/src/pl/tcl/po/de.po b/src/pl/tcl/po/de.po
index 42b2fdcc65..8d47acb8f1 100644
--- a/src/pl/tcl/po/de.po
+++ b/src/pl/tcl/po/de.po
@@ -1,14 +1,14 @@
# German message translation file for PL/Tcl
-# Peter Eisentraut <peter_e@gmx.net>, 2009 - 2015.
+# Peter Eisentraut <peter_e@gmx.net>, 2009 - 2017.
#
# Use these quotes: »%s«
#
msgid ""
msgstr ""
-"Project-Id-Version: PostgreSQL 9.5\n"
+"Project-Id-Version: PostgreSQL 10\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2015-09-22 15:07+0000\n"
-"PO-Revision-Date: 2015-09-22 20:29-0400\n"
+"POT-Creation-Date: 2017-03-14 17:37+0000\n"
+"PO-Revision-Date: 2017-03-14 14:45-0400\n"
"Last-Translator: Peter Eisentraut <peter_e@gmx.net>\n"
"Language-Team: German <peter_e@gmx.net>\n"
"Language: de\n"
@@ -16,42 +16,51 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: pltcl.c:555
+#: pltcl.c:459
+msgid "PL/Tcl function to call once when pltcl is first used."
+msgstr "PL/Tcl-Funktion, die einmal aufgerufen wird, wenn pltcl zum ersten Mal benutzt wird."
+
+#: pltcl.c:466
+msgid "PL/TclU function to call once when pltclu is first used."
+msgstr "PL/Tcl-Funktion, die einmal aufgerufen wird, wenn pltclu zum ersten Mal benutzt wird."
+
+#: pltcl.c:629
#, c-format
-msgid "module \"unknown\" not found in pltcl_modules"
-msgstr "Modul »unknown« nicht in pltcl_modules gefunden"
+msgid "function \"%s\" is in the wrong language"
+msgstr "Funktion »%s« ist in der falschen Sprache"
-#: pltcl.c:591
+#: pltcl.c:640
#, c-format
-msgid "could not load module \"unknown\": %s"
-msgstr "konnte Modul »unknown« nicht laden: %s"
+msgid "function \"%s\" must not be SECURITY DEFINER"
+msgstr "Funktion »%s« darf nicht SECURITY DEFINER sein"
-#: pltcl.c:1047
+#. translator: %s is "pltcl.start_proc" or "pltclu.start_proc"
+#: pltcl.c:674
#, c-format
-msgid "could not split return value from trigger: %s"
-msgstr "konnte Rückgabewert des Triggers nicht splitten: %s"
+msgid "processing %s parameter"
+msgstr "Verarbeiten von Parameter von %s"
-#: pltcl.c:1058
+#: pltcl.c:830
#, c-format
-msgid "trigger's return list must have even number of elements"
-msgstr "Rückgabeliste des Triggers muss gerade Anzahl Elemente haben"
+msgid "set-valued function called in context that cannot accept a set"
+msgstr "Funktion mit Mengenergebnis in einem Zusammenhang aufgerufen, der keine Mengenergebnisse verarbeiten kann"
-#: pltcl.c:1094
+#: pltcl.c:994
#, c-format
-msgid "unrecognized attribute \"%s\""
-msgstr "unbekanntes Attribute »%s«"
+msgid "function returning record called in context that cannot accept type record"
+msgstr "Funktion, die einen Record zurückgibt, in einem Zusammenhang aufgerufen, der Typ record nicht verarbeiten kann"
-#: pltcl.c:1099
+#: pltcl.c:1258
#, c-format
-msgid "cannot set system attribute \"%s\""
-msgstr "Systemattribut »%s« kann nicht gesetzt werden"
+msgid "could not split return value from trigger: %s"
+msgstr "konnte Rückgabewert des Triggers nicht splitten: %s"
-#: pltcl.c:1222 pltcl.c:1648
+#: pltcl.c:1338 pltcl.c:1766
#, c-format
msgid "%s"
msgstr "%s"
-#: pltcl.c:1223
+#: pltcl.c:1339
#, c-format
msgid ""
"%s\n"
@@ -60,32 +69,37 @@ msgstr ""
"%s\n"
"in PL/Tcl-Funktion »%s«"
-#: pltcl.c:1331 pltcl.c:1338
-#, c-format
-msgid "out of memory"
-msgstr "Speicher aufgebraucht"
-
-#: pltcl.c:1386
+#: pltcl.c:1504
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "Triggerfunktionen können nur als Trigger aufgerufen werden"
-#: pltcl.c:1395
+#: pltcl.c:1508
#, c-format
msgid "PL/Tcl functions cannot return type %s"
msgstr "PL/Tcl-Funktionen können keinen Rückgabetyp %s haben"
-#: pltcl.c:1407
-#, c-format
-msgid "PL/Tcl functions cannot return composite types"
-msgstr "PL/Tcl-Funktion können keine zusammengesetzten Typen zurückgeben"
-
-#: pltcl.c:1446
+#: pltcl.c:1544
#, c-format
msgid "PL/Tcl functions cannot accept type %s"
msgstr "PL/Tcl-Funktionen können Typ %s nicht annehmen"
-#: pltcl.c:1564
+#: pltcl.c:1658
#, c-format
msgid "could not create internal procedure \"%s\": %s"
msgstr "konnte interne Prozedur »%s« nicht erzeugen: %s"
+
+#: pltcl.c:3095
+#, c-format
+msgid "column name/value list must have even number of elements"
+msgstr "Liste der Spaltennamen/-werte muss gerade Anzahl Elemente haben"
+
+#: pltcl.c:3113
+#, c-format
+msgid "column name/value list contains nonexistent column name \"%s\""
+msgstr "Liste der Spaltennamen/-werte enthält nicht existierenden Spaltennamen »%s«"
+
+#: pltcl.c:3120
+#, c-format
+msgid "cannot set system attribute \"%s\""
+msgstr "Systemattribut »%s« kann nicht gesetzt werden"
diff --git a/src/pl/tcl/po/pl.po b/src/pl/tcl/po/pl.po
index cd370a1a38..0a0b2bf401 100644
--- a/src/pl/tcl/po/pl.po
+++ b/src/pl/tcl/po/pl.po
@@ -2,59 +2,79 @@
# Copyright (C) 2011 PostgreSQL Global Development Group
# This file is distributed under the same license as the PostgreSQL package.
# Begina Felicysym <begina.felicysym@wp.eu>, 2011.
-# grzegorz <begina.felicysym@wp.eu>, 2015.
+# grzegorz <begina.felicysym@wp.eu>, 2015, 2017.
msgid ""
msgstr ""
"Project-Id-Version: pltcl (PostgreSQL 9.5)\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2015-10-29 01:37+0000\n"
-"PO-Revision-Date: 2015-12-22 21:19-0500\n"
+"POT-Creation-Date: 2017-04-09 21:07+0000\n"
+"PO-Revision-Date: 2017-05-02 13:57-0400\n"
"Last-Translator: grzegorz <begina.felicysym@wp.eu>\n"
"Language-Team: begina.felicysym@wp.eu\n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
"X-Generator: Virtaal 0.7.1\n"
-#: pltcl.c:555
+#: pltcl.c:459
+#| msgid "Perl initialization code to execute once when plperl is first used."
+msgid "PL/Tcl function to call once when pltcl is first used."
+msgstr ""
+"Funkcja PL/TCL do jednokrotnego wykonania gdy pltcl jest użyty po raz "
+"pierwszy."
+
+#: pltcl.c:466
+#| msgid "Perl initialization code to execute once when plperlu is first used."
+msgid "PL/TclU function to call once when pltclu is first used."
+msgstr ""
+"Funkcja PL/TCL do jednokrotnego wykonania gdy pltclu jest użyty po raz "
+"pierwszy."
+
+#: pltcl.c:629
#, c-format
-msgid "module \"unknown\" not found in pltcl_modules"
-msgstr "nie znaleziono modułu \"unknown\" w pltcl_modules"
+#| msgid "function \"%s\" was not called by trigger manager"
+msgid "function \"%s\" is in the wrong language"
+msgstr "funkcja \"%s\" jest napisana nie w tym języku"
-#: pltcl.c:591
+#: pltcl.c:640
#, c-format
-msgid "could not load module \"unknown\": %s"
-msgstr "nie można wczytać modułu \"unknown\": %s"
+#| msgid "function \"%s\" must be fired for INSERT"
+msgid "function \"%s\" must not be SECURITY DEFINER"
+msgstr "funkcja \"%s\" nie może być SECURITY DEFINER"
-#: pltcl.c:1047
+#. translator: %s is "pltcl.start_proc" or "pltclu.start_proc"
+#: pltcl.c:674
#, c-format
-msgid "could not split return value from trigger: %s"
-msgstr "nie można podzielić wartości zwracanej przez wyzwalacz: %s"
+#| msgid "processing %s\n"
+msgid "processing %s parameter"
+msgstr "przetwarzanie parametru %s"
-#: pltcl.c:1058
+#: pltcl.c:830
#, c-format
-#| msgid "argument list must have even number of elements"
-msgid "trigger's return list must have even number of elements"
-msgstr "lista zwracana przez wyzwalacz musi mieć parzystą liczbę elementów"
+msgid "set-valued function called in context that cannot accept a set"
+msgstr ""
+"funkcja zwracająca zbiór rekordów wywołana w kontekście, w którym nie jest "
+"to dopuszczalne"
-#: pltcl.c:1094
+#: pltcl.c:994
#, c-format
-msgid "unrecognized attribute \"%s\""
-msgstr "nierozpoznany atrybut \"%s\""
+msgid "function returning record called in context that cannot accept type record"
+msgstr "funkcja zwracająca rekord w wywołaniu, które nie akceptuje typów złożonych"
-#: pltcl.c:1099
+#: pltcl.c:1263
#, c-format
-msgid "cannot set system attribute \"%s\""
-msgstr "nie można ustawić atrybutu systemowego \"%s\""
+msgid "could not split return value from trigger: %s"
+msgstr "nie można podzielić wartości zwracanej przez wyzwalacz: %s"
-#: pltcl.c:1222 pltcl.c:1648
+#: pltcl.c:1343 pltcl.c:1771
#, c-format
msgid "%s"
msgstr "%s"
-#: pltcl.c:1223
+#: pltcl.c:1344
#, c-format
msgid ""
"%s\n"
@@ -63,32 +83,54 @@ msgstr ""
"%s\n"
"w funkcji PL/Tcl \"%s\""
-#: pltcl.c:1331 pltcl.c:1338
-#, c-format
-msgid "out of memory"
-msgstr "brak pamięci"
-
-#: pltcl.c:1386
+#: pltcl.c:1509
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "procedury wyzwalaczy mogą być wywoływane jedynie przez wyzwalacze"
-#: pltcl.c:1395
+#: pltcl.c:1513
#, c-format
msgid "PL/Tcl functions cannot return type %s"
msgstr "funkcje PL/Tcl nie mogą zwracać wartości typu %s"
-#: pltcl.c:1407
-#, c-format
-msgid "PL/Tcl functions cannot return composite types"
-msgstr "funkcje PL/Tcl nie mogą zwracać wartości złożonych"
-
-#: pltcl.c:1446
+#: pltcl.c:1549
#, c-format
msgid "PL/Tcl functions cannot accept type %s"
msgstr "funkcje PL/Tcl nie akceptują typu %s"
-#: pltcl.c:1564
+#: pltcl.c:1663
#, c-format
msgid "could not create internal procedure \"%s\": %s"
msgstr "nie można utworzyć procedury wewnętrznej \"%s\": %s"
+
+#: pltcl.c:3100
+#, c-format
+#| msgid "argument list must have even number of elements"
+msgid "column name/value list must have even number of elements"
+msgstr "lista kolumn nazwa/wartość musi mieć parzystą liczbę elementów"
+
+#: pltcl.c:3118
+#, c-format
+#| msgid "Perl hash contains nonexistent column \"%s\""
+msgid "column name/value list contains nonexistent column name \"%s\""
+msgstr "lista kolumn nazwa/wartość zawiera nieistniejącą kolumnę \"%s\""
+
+#: pltcl.c:3125
+#, c-format
+msgid "cannot set system attribute \"%s\""
+msgstr "nie można ustawić atrybutu systemowego \"%s\""
+
+#~ msgid "PL/Tcl functions cannot return composite types"
+#~ msgstr "funkcje PL/Tcl nie mogą zwracać wartości złożonych"
+
+#~ msgid "out of memory"
+#~ msgstr "brak pamięci"
+
+#~ msgid "unrecognized attribute \"%s\""
+#~ msgstr "nierozpoznany atrybut \"%s\""
+
+#~ msgid "could not load module \"unknown\": %s"
+#~ msgstr "nie można wczytać modułu \"unknown\": %s"
+
+#~ msgid "module \"unknown\" not found in pltcl_modules"
+#~ msgstr "nie znaleziono modułu \"unknown\" w pltcl_modules"
diff --git a/src/pl/tcl/po/ru.po b/src/pl/tcl/po/ru.po
index ee51e7b8c0..d048170a98 100644
--- a/src/pl/tcl/po/ru.po
+++ b/src/pl/tcl/po/ru.po
@@ -1,62 +1,71 @@
# Russian message translation file for pltcl
-# Copyright (C) 2012 PostgreSQL Global Development Group
+# Copyright (C) 2012-2016 PostgreSQL Global Development Group
# This file is distributed under the same license as the PostgreSQL package.
-# Alexander Lakhin <exclusion@gmail.com>, 2012.
+# Alexander Lakhin <exclusion@gmail.com>, 2012-2017.
#
-# ChangeLog:
-# - February 18, 2012: Complete translation for 9.1. Alexander Lakhin <exclusion@gmail.com>.
msgid ""
msgstr ""
-"Project-Id-Version: PostgreSQL 9.2\n"
+"Project-Id-Version: pltcl (PostgreSQL current)\n"
"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
-"POT-Creation-Date: 2015-09-19 03:07+0000\n"
-"PO-Revision-Date: 2015-10-16 21:38+0400\n"
-"Last-Translator: Alexander Lakhin <exclusion@gmail.com>\n"
-"Language-Team: Russian <pgsql-translators@postgresql.org>\n"
+"POT-Creation-Date: 2017-03-27 12:37+0000\n"
+"PO-Revision-Date: 2017-03-29 14:01+0300\n"
+"Language-Team: Russian <pgsql-ru-general@postgresql.org>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-"X-Generator: Lokalize 2.0\n"
+"Last-Translator: Alexander Lakhin <exclusion@gmail.com>\n"
+
+#: pltcl.c:459
+msgid "PL/Tcl function to call once when pltcl is first used."
+msgstr "Функция на PL/Tcl, вызываемая при первом использовании pltcl."
+
+#: pltcl.c:466
+msgid "PL/TclU function to call once when pltclu is first used."
+msgstr "Функция на PL/TclU, вызываемая при первом использовании pltclu."
-#: pltcl.c:555
+#: pltcl.c:629
#, c-format
-msgid "module \"unknown\" not found in pltcl_modules"
-msgstr "модуль \"unknown\" не найден в pltcl_modules"
+msgid "function \"%s\" is in the wrong language"
+msgstr "Функция \"%s\" объявлена на другом языке"
-#: pltcl.c:591
+#: pltcl.c:640
#, c-format
-msgid "could not load module \"unknown\": %s"
-msgstr "загрузить модуль \"unknown\" не удалось: %s"
+msgid "function \"%s\" must not be SECURITY DEFINER"
+msgstr "функция \"%s\" не должна иметь характеристику SECURITY DEFINER"
-#: pltcl.c:1047
+#. translator: %s is "pltcl.start_proc" or "pltclu.start_proc"
+#: pltcl.c:674
#, c-format
-msgid "could not split return value from trigger: %s"
-msgstr "разложить возвращаемое из триггера значение не удалось: %s"
+msgid "processing %s parameter"
+msgstr "обработка параметра %s"
-#: pltcl.c:1058
+#: pltcl.c:830
#, c-format
-msgid "trigger's return list must have even number of elements"
-msgstr "в возвращаемом триггером списке должно быть чётное число элементов"
+msgid "set-valued function called in context that cannot accept a set"
+msgstr ""
+"функция, возвращающая множество, вызвана в контексте, где ему нет места"
-#: pltcl.c:1094
+#: pltcl.c:994
#, c-format
-msgid "unrecognized attribute \"%s\""
-msgstr "нераспознанный атрибут \"%s\""
+msgid ""
+"function returning record called in context that cannot accept type record"
+msgstr ""
+"функция, возвращающая запись, вызвана в контексте, не допускающем этот тип"
-#: pltcl.c:1099
+#: pltcl.c:1258
#, c-format
-msgid "cannot set system attribute \"%s\""
-msgstr "установить системный атрибут \"%s\" нельзя"
+msgid "could not split return value from trigger: %s"
+msgstr "разложить возвращаемое из триггера значение не удалось: %s"
-#: pltcl.c:1222 pltcl.c:1648
+#: pltcl.c:1338 pltcl.c:1766
#, c-format
msgid "%s"
msgstr "%s"
-#: pltcl.c:1223
+#: pltcl.c:1339
#, c-format
msgid ""
"%s\n"
@@ -65,32 +74,53 @@ msgstr ""
"%s\n"
"в функции PL/Tcl \"%s\""
-#: pltcl.c:1331 pltcl.c:1338
-#, c-format
-msgid "out of memory"
-msgstr "нехватка памяти"
-
-#: pltcl.c:1386
+#: pltcl.c:1504
#, c-format
msgid "trigger functions can only be called as triggers"
msgstr "триггерные функции могут вызываться только в триггерах"
-#: pltcl.c:1395
+#: pltcl.c:1508
#, c-format
msgid "PL/Tcl functions cannot return type %s"
msgstr "функции PL/Tcl не могут возвращать тип %s"
-#: pltcl.c:1407
-#, c-format
-msgid "PL/Tcl functions cannot return composite types"
-msgstr "функции PL/Tcl не могут возвращать составные типы"
-
-#: pltcl.c:1446
+#: pltcl.c:1544
#, c-format
msgid "PL/Tcl functions cannot accept type %s"
msgstr "функции PL/Tcl не могут принимать тип %s"
-#: pltcl.c:1564
+#: pltcl.c:1658
#, c-format
msgid "could not create internal procedure \"%s\": %s"
msgstr "не удалось создать внутреннюю процедуру \"%s\": %s"
+
+#: pltcl.c:3095
+#, c-format
+msgid "column name/value list must have even number of elements"
+msgstr "в списке имён/значений столбцов должно быть чётное число элементов"
+
+#: pltcl.c:3113
+#, c-format
+msgid "column name/value list contains nonexistent column name \"%s\""
+msgstr ""
+"список имён/значений столбцов содержит имя несуществующего столбца \"%s\""
+
+#: pltcl.c:3120
+#, c-format
+msgid "cannot set system attribute \"%s\""
+msgstr "установить системный атрибут \"%s\" нельзя"
+
+#~ msgid "module \"unknown\" not found in pltcl_modules"
+#~ msgstr "модуль \"unknown\" не найден в pltcl_modules"
+
+#~ msgid "could not load module \"unknown\": %s"
+#~ msgstr "загрузить модуль \"unknown\" не удалось: %s"
+
+#~ msgid "unrecognized attribute \"%s\""
+#~ msgstr "нераспознанный атрибут \"%s\""
+
+#~ msgid "out of memory"
+#~ msgstr "нехватка памяти"
+
+#~ msgid "PL/Tcl functions cannot return composite types"
+#~ msgstr "функции PL/Tcl не могут возвращать составные типы"
diff --git a/src/pl/tcl/sql/pltcl_queries.sql b/src/pl/tcl/sql/pltcl_queries.sql
index a0a9619a9b..dabd8cd35f 100644
--- a/src/pl/tcl/sql/pltcl_queries.sql
+++ b/src/pl/tcl/sql/pltcl_queries.sql
@@ -80,8 +80,10 @@ insert into trigger_test_view values(2,'insert');
update trigger_test_view set v = 'update' where i=1;
delete from trigger_test_view;
+update trigger_test set v = 'update', test_skip=true where i = 1;
update trigger_test set v = 'update' where i = 1;
delete from trigger_test;
+truncate trigger_test;
-- Test composite-type arguments
select tcl_composite_arg_ref1(row('tkey', 42, 'ref2'));
@@ -91,9 +93,146 @@ select tcl_composite_arg_ref2(row('tkey', 42, 'ref2'));
select tcl_argisnull('foo');
select tcl_argisnull('');
select tcl_argisnull(null);
+-- should error
+insert into trigger_test(test_argisnull) values(true);
+select trigger_data();
-- Test spi_lastoid primitive
create temp table t1 (f1 int);
select tcl_lastoid('t1');
create temp table t2 (f1 int) with oids;
select tcl_lastoid('t2') > 0;
+
+-- test some error cases
+create function tcl_error(out a int, out b int) as $$return {$$ language pltcl;
+select tcl_error();
+
+create function bad_record(out a text, out b text) as $$return [list a]$$ language pltcl;
+select bad_record();
+
+create function bad_field(out a text, out b text) as $$return [list a 1 b 2 cow 3]$$ language pltcl;
+select bad_field();
+
+-- test compound return
+select * from tcl_test_cube_squared(5);
+
+-- test SRF
+select * from tcl_test_squared_rows(0,5);
+
+select * from tcl_test_sequence(0,5) as a;
+
+select 1, tcl_test_sequence(0,5);
+
+create function non_srf() returns int as $$return_next 1$$ language pltcl;
+select non_srf();
+
+create function bad_record_srf(out a text, out b text) returns setof record as $$
+return_next [list a]
+$$ language pltcl;
+select bad_record_srf();
+
+create function bad_field_srf(out a text, out b text) returns setof record as $$
+return_next [list a 1 b 2 cow 3]
+$$ language pltcl;
+select bad_field_srf();
+
+-- test quote
+select tcl_eval('quote foo bar');
+select tcl_eval('quote [format %c 39]');
+select tcl_eval('quote [format %c 92]');
+
+-- Test argisnull
+select tcl_eval('argisnull');
+select tcl_eval('argisnull 14');
+select tcl_eval('argisnull abc');
+
+-- Test return_null
+select tcl_eval('return_null 14');
+-- should error
+insert into trigger_test(test_return_null) values(true);
+
+-- Test spi_exec
+select tcl_eval('spi_exec');
+select tcl_eval('spi_exec -count');
+select tcl_eval('spi_exec -array');
+select tcl_eval('spi_exec -count abc');
+select tcl_eval('spi_exec query loop body toomuch');
+select tcl_eval('spi_exec "begin; rollback;"');
+
+-- Test spi_execp
+select tcl_eval('spi_execp');
+select tcl_eval('spi_execp -count');
+select tcl_eval('spi_execp -array');
+select tcl_eval('spi_execp -count abc');
+select tcl_eval('spi_execp -nulls');
+select tcl_eval('spi_execp ""');
+
+-- test spi_prepare
+select tcl_eval('spi_prepare');
+select tcl_eval('spi_prepare a b');
+select tcl_eval('spi_prepare a "b {"');
+select tcl_error_handling_test($tcl$spi_prepare "select moo" []$tcl$);
+
+-- test full error text
+select tcl_error_handling_test($tcl$
+spi_exec "DO $$
+BEGIN
+RAISE 'my message'
+ USING HINT = 'my hint'
+ , DETAIL = 'my detail'
+ , SCHEMA = 'my schema'
+ , TABLE = 'my table'
+ , COLUMN = 'my column'
+ , CONSTRAINT = 'my constraint'
+ , DATATYPE = 'my datatype'
+;
+END$$;"
+$tcl$);
+
+-- verify tcl_error_handling_test() properly reports non-postgres errors
+select tcl_error_handling_test('moo');
+
+-- test elog
+select tcl_eval('elog');
+select tcl_eval('elog foo bar');
+
+-- test forced error
+select tcl_eval('error "forced error"');
+
+-- test loop control in spi_exec[p]
+select tcl_spi_exec(true, 'break');
+select tcl_spi_exec(true, 'continue');
+select tcl_spi_exec(true, 'error');
+select tcl_spi_exec(true, 'return');
+select tcl_spi_exec(false, 'break');
+select tcl_spi_exec(false, 'continue');
+select tcl_spi_exec(false, 'error');
+select tcl_spi_exec(false, 'return');
+
+-- forcibly run the Tcl event loop for awhile, to check that we have not
+-- messed things up too badly by disabling the Tcl notifier subsystem
+select tcl_eval($$
+ unset -nocomplain ::tcl_vwait
+ after 100 {set ::tcl_vwait 1}
+ vwait ::tcl_vwait
+ unset -nocomplain ::tcl_vwait$$);
+
+-- test transition table visibility
+create table transition_table_test (id int, name text);
+insert into transition_table_test values (1, 'a');
+create function transition_table_test_f() returns trigger language pltcl as
+$$
+ spi_exec -array C "SELECT id, name FROM old_table" {
+ elog INFO "old: $C(id) -> $C(name)"
+ }
+ spi_exec -array C "SELECT id, name FROM new_table" {
+ elog INFO "new: $C(id) -> $C(name)"
+ }
+ return OK
+$$;
+CREATE TRIGGER a_t AFTER UPDATE ON transition_table_test
+ REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
+ FOR EACH STATEMENT EXECUTE PROCEDURE transition_table_test_f();
+update transition_table_test set name = 'b';
+drop table transition_table_test;
+drop function transition_table_test_f();
diff --git a/src/pl/tcl/sql/pltcl_setup.sql b/src/pl/tcl/sql/pltcl_setup.sql
index 8df65a5816..56a90dc844 100644
--- a/src/pl/tcl/sql/pltcl_setup.sql
+++ b/src/pl/tcl/sql/pltcl_setup.sql
@@ -57,12 +57,33 @@ create function check_pkey1_exists(int4, bpchar) returns bool as E'
-- dump trigger data
-CREATE TABLE trigger_test
- (i int, v text );
+CREATE TABLE trigger_test (
+ i int,
+ v text,
+ dropme text,
+ test_skip boolean DEFAULT false,
+ test_return_null boolean DEFAULT false,
+ test_argisnull boolean DEFAULT false
+);
+-- Make certain dropped attributes are handled correctly
+ALTER TABLE trigger_test DROP dropme;
-CREATE VIEW trigger_test_view AS SELECT * FROM trigger_test;
+CREATE VIEW trigger_test_view AS SELECT i, v FROM trigger_test;
CREATE FUNCTION trigger_data() returns trigger language pltcl as $_$
+ if {$TG_table_name eq "trigger_test" && $TG_level eq "ROW" && $TG_op ne "DELETE"} {
+ # Special case tests
+ if {$NEW(test_return_null) eq "t" } {
+ return_null
+ }
+ if {$NEW(test_argisnull) eq "t" } {
+ set should_error [argisnull 1]
+ }
+ if {$NEW(test_skip) eq "t" } {
+ elog NOTICE "SKIPPING OPERATION $TG_op"
+ return SKIP
+ }
+ }
if { [info exists TG_relid] } {
set TG_relid "bogus:12345"
@@ -97,6 +118,9 @@ $_$;
CREATE TRIGGER show_trigger_data_trig
BEFORE INSERT OR UPDATE OR DELETE ON trigger_test
FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
+CREATE TRIGGER statement_trigger
+BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE ON trigger_test
+FOR EACH STATEMENT EXECUTE PROCEDURE trigger_data(42,'statement trigger');
CREATE TRIGGER show_trigger_data_view_trig
INSTEAD OF INSERT OR UPDATE OR DELETE ON trigger_test_view
@@ -575,18 +599,18 @@ create function tcl_date_week(int4,int4,int4) returns text as $$
return [clock format [clock scan "$2/$3/$1"] -format "%U"]
$$ language pltcl immutable;
-select tcl_date_week(2010,1,24);
+select tcl_date_week(2010,1,26);
select tcl_date_week(2001,10,24);
-- test pltcl event triggers
-create or replace function tclsnitch() returns event_trigger language pltcl as $$
+create function tclsnitch() returns event_trigger language pltcl as $$
elog NOTICE "tclsnitch: $TG_event $TG_tag"
$$;
create event trigger tcl_a_snitch on ddl_command_start execute procedure tclsnitch();
create event trigger tcl_b_snitch on ddl_command_end execute procedure tclsnitch();
-create or replace function foobar() returns int language sql as $$select 1;$$;
+create function foobar() returns int language sql as $$select 1;$$;
alter function foobar() cost 77;
drop function foobar();
@@ -596,26 +620,113 @@ drop table foo;
drop event trigger tcl_a_snitch;
drop event trigger tcl_b_snitch;
--- test use of errorCode in error handling
+create function tcl_test_cube_squared(in int, out squared int, out cubed int) as $$
+ return [list squared [expr {$1 * $1}] cubed [expr {$1 * $1 * $1}]]
+$$ language pltcl;
-create function tcl_error_handling_test() returns text as $$
- global errorCode
- if {[catch { spi_exec "select no_such_column from foo;" }]} {
- array set errArray $errorCode
- if {$errArray(condition) == "undefined_table"} {
- return "expected error: $errArray(message)"
- } else {
- return "unexpected error: $errArray(condition) $errArray(message)"
- }
- } else {
- return "no error"
+create function tcl_test_squared_rows(int,int) returns table (x int, y int) as $$
+ for {set i $1} {$i < $2} {incr i} {
+ return_next [list y [expr {$i * $i}] x $i]
}
$$ language pltcl;
-select tcl_error_handling_test();
+create function tcl_test_sequence(int,int) returns setof int as $$
+ for {set i $1} {$i < $2} {incr i} {
+ return_next $i
+ }
+$$ language pltcl;
-create temp table foo(f1 int);
+create function tcl_eval(string text) returns text as $$
+ eval $1
+$$ language pltcl;
-select tcl_error_handling_test();
+-- test use of errorCode in error handling
+create function tcl_error_handling_test(text) returns text
+language pltcl
+as $function$
+ if {[catch $1 err]} {
+ # If not a Postgres error, just return the basic error message
+ if {[lindex $::errorCode 0] != "POSTGRES"} {
+ return $err
+ }
-drop table foo;
+ # Get rid of keys that can't be expected to remain constant
+ array set myArray $::errorCode
+ unset myArray(POSTGRES)
+ unset -nocomplain myArray(funcname)
+ unset -nocomplain myArray(filename)
+ unset -nocomplain myArray(lineno)
+
+ # Format into something nicer
+ set vals []
+ foreach {key} [lsort [array names myArray]] {
+ set value [string map {"\n" "\n\t"} $myArray($key)]
+ lappend vals "$key: $value"
+ }
+ return [join $vals "\n"]
+ } else {
+ return "no error"
+ }
+$function$;
+
+-- test spi_exec and spi_execp with -array
+create function tcl_spi_exec(
+ prepare boolean,
+ action text
+)
+returns void language pltcl AS $function$
+set query "select * from (values (1,'foo'),(2,'bar'),(3,'baz')) v(col1,col2)"
+if {$1 == "t"} {
+ set prep [spi_prepare $query {}]
+ spi_execp -array A $prep {
+ elog NOTICE "col1 $A(col1), col2 $A(col2)"
+
+ switch $A(col1) {
+ 2 {
+ elog NOTICE "action: $2"
+ switch $2 {
+ break {
+ break
+ }
+ continue {
+ continue
+ }
+ return {
+ return
+ }
+ error {
+ error "error message"
+ }
+ }
+ error "should not get here"
+ }
+ }
+ }
+} else {
+ spi_exec -array A $query {
+ elog NOTICE "col1 $A(col1), col2 $A(col2)"
+
+ switch $A(col1) {
+ 2 {
+ elog NOTICE "action: $2"
+ switch $2 {
+ break {
+ break
+ }
+ continue {
+ continue
+ }
+ return {
+ return
+ }
+ error {
+ error "error message"
+ }
+ }
+ error "should not get here"
+ }
+ }
+ }
+}
+elog NOTICE "end of function"
+$function$;
diff --git a/src/pl/tcl/sql/pltcl_start_proc.sql b/src/pl/tcl/sql/pltcl_start_proc.sql
new file mode 100644
index 0000000000..7a8e68e266
--- /dev/null
+++ b/src/pl/tcl/sql/pltcl_start_proc.sql
@@ -0,0 +1,21 @@
+--
+-- Test start_proc execution
+--
+
+SET pltcl.start_proc = 'no_such_function';
+
+select tcl_int4add(1, 2);
+select tcl_int4add(1, 2);
+
+create function tcl_initialize() returns void as
+$$ elog NOTICE "in tcl_initialize" $$ language pltcl SECURITY DEFINER;
+
+SET pltcl.start_proc = 'public.tcl_initialize';
+
+select tcl_int4add(1, 2); -- fail
+
+create or replace function tcl_initialize() returns void as
+$$ elog NOTICE "in tcl_initialize" $$ language pltcl;
+
+select tcl_int4add(1, 2);
+select tcl_int4add(1, 2);
diff --git a/src/pl/tcl/sql/pltcl_subxact.sql b/src/pl/tcl/sql/pltcl_subxact.sql
new file mode 100644
index 0000000000..0625736ea4
--- /dev/null
+++ b/src/pl/tcl/sql/pltcl_subxact.sql
@@ -0,0 +1,95 @@
+--
+-- Test explicit subtransactions
+--
+
+CREATE TABLE subtransaction_tbl (
+ i integer
+);
+
+--
+-- We use this wrapper to catch errors and return errormsg only,
+-- because values of $::errorinfo variable contain procedure name which
+-- includes OID, so it's not stable
+--
+CREATE FUNCTION pltcl_wrapper(statement text) RETURNS text
+AS $$
+ if [catch {spi_exec $1} msg] {
+ return "ERROR: $msg"
+ } else {
+ return "SUCCESS: $msg"
+ }
+$$ LANGUAGE pltcl;
+
+-- Test subtransaction successfully committed
+
+CREATE FUNCTION subtransaction_ctx_success() RETURNS void
+AS $$
+ spi_exec "INSERT INTO subtransaction_tbl VALUES(1)"
+ subtransaction {
+ spi_exec "INSERT INTO subtransaction_tbl VALUES(2)"
+ }
+$$ LANGUAGE pltcl;
+
+BEGIN;
+INSERT INTO subtransaction_tbl VALUES(0);
+SELECT subtransaction_ctx_success();
+COMMIT;
+SELECT * FROM subtransaction_tbl;
+TRUNCATE subtransaction_tbl;
+
+-- Test subtransaction rollback
+
+CREATE FUNCTION subtransaction_ctx_test(what_error text = NULL) RETURNS void
+AS $$
+ spi_exec "INSERT INTO subtransaction_tbl VALUES (1)"
+ subtransaction {
+ spi_exec "INSERT INTO subtransaction_tbl VALUES (2)"
+ if {$1 == "SPI"} {
+ spi_exec "INSERT INTO subtransaction_tbl VALUES ('oops')"
+ } elseif { $1 == "Tcl"} {
+ elog ERROR "Tcl error"
+ }
+ }
+$$ LANGUAGE pltcl;
+
+SELECT pltcl_wrapper('SELECT subtransaction_ctx_test()');
+SELECT * FROM subtransaction_tbl;
+TRUNCATE subtransaction_tbl;
+
+SELECT pltcl_wrapper('SELECT subtransaction_ctx_test(''SPI'')');
+SELECT * FROM subtransaction_tbl;
+TRUNCATE subtransaction_tbl;
+
+SELECT pltcl_wrapper('SELECT subtransaction_ctx_test(''Tcl'')');
+SELECT * FROM subtransaction_tbl;
+TRUNCATE subtransaction_tbl;
+
+-- Nested subtransactions
+
+CREATE FUNCTION subtransaction_nested_test(swallow boolean = 'f') RETURNS text
+AS $$
+spi_exec "INSERT INTO subtransaction_tbl VALUES (1)"
+subtransaction {
+ spi_exec "INSERT INTO subtransaction_tbl VALUES (2)"
+ if [catch {
+ subtransaction {
+ spi_exec "INSERT INTO subtransaction_tbl VALUES (3)"
+ spi_exec "error"
+ }
+ } errormsg] {
+ if {$1 != "t"} {
+ error $errormsg $::errorInfo $::errorCode
+ }
+ elog NOTICE "Swallowed $errormsg"
+ }
+}
+return "ok"
+$$ LANGUAGE pltcl;
+
+SELECT pltcl_wrapper('SELECT subtransaction_nested_test()');
+SELECT * FROM subtransaction_tbl;
+TRUNCATE subtransaction_tbl;
+
+SELECT pltcl_wrapper('SELECT subtransaction_nested_test(''t'')');
+SELECT * FROM subtransaction_tbl;
+TRUNCATE subtransaction_tbl;