mirror of https://github.com/postgres/postgres
This provides a mechanism for specifying conversions between SQL data types and procedural languages. As examples, there are transforms for hstore and ltree for PL/Perl and PL/Python. reviews by Pavel Stěhule and Andres Freundpull/14/head
parent
f320cbb615
commit
cac7658205
@ -0,0 +1,4 @@ |
||||
# Generated subdirectories |
||||
/log/ |
||||
/results/ |
||||
/tmp_check/ |
@ -0,0 +1,24 @@ |
||||
# contrib/hstore_plperl/Makefile
|
||||
|
||||
MODULE_big = hstore_plperl
|
||||
OBJS = hstore_plperl.o
|
||||
|
||||
PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plperl -I$(perl_archlibexp)/CORE -I$(top_srcdir)/contrib/hstore
|
||||
|
||||
EXTENSION = hstore_plperl hstore_plperlu
|
||||
DATA = hstore_plperl--1.0.sql hstore_plperlu--1.0.sql
|
||||
|
||||
REGRESS = hstore_plperl create_transform
|
||||
REGRESS_OPTS = --load-extension=hstore --load-extension=plperl --load-extension=plperlu
|
||||
EXTRA_INSTALL = contrib/hstore
|
||||
|
||||
ifdef USE_PGXS |
||||
PG_CONFIG = pg_config
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
include $(PGXS) |
||||
else |
||||
subdir = contrib/hstore_plperl
|
||||
top_builddir = ../..
|
||||
include $(top_builddir)/src/Makefile.global |
||||
include $(top_srcdir)/contrib/contrib-global.mk |
||||
endif |
@ -0,0 +1,74 @@ |
||||
-- general regression test for transforms |
||||
DROP EXTENSION IF EXISTS hstore CASCADE; |
||||
NOTICE: extension "hstore" does not exist, skipping |
||||
DROP EXTENSION IF EXISTS plperl CASCADE; |
||||
NOTICE: extension "plperl" does not exist, skipping |
||||
DROP EXTENSION IF EXISTS hstore_plperl CASCADE; |
||||
NOTICE: extension "hstore_plperl" does not exist, skipping |
||||
CREATE EXTENSION hstore; |
||||
CREATE EXTENSION plperl; |
||||
CREATE FUNCTION hstore_to_plperl(val internal) RETURNS internal |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS '$libdir/hstore_plperl'; |
||||
CREATE FUNCTION plperl_to_hstore(val internal) RETURNS hstore |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS '$libdir/hstore_plperl'; |
||||
CREATE TRANSFORM FOR foo LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail |
||||
ERROR: type "foo" does not exist |
||||
CREATE TRANSFORM FOR hstore LANGUAGE foo (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail |
||||
ERROR: language "foo" does not exist |
||||
CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_out(hstore), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail |
||||
ERROR: return data type of FROM SQL function must be "internal" |
||||
CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION internal_in(cstring), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail |
||||
ERROR: first argument of transform function must be type "internal" |
||||
CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- ok |
||||
CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail |
||||
ERROR: transform for type hstore language plperl already exists |
||||
CREATE OR REPLACE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- ok |
||||
CREATE OR REPLACE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal)); -- ok |
||||
CREATE OR REPLACE TRANSFORM FOR hstore LANGUAGE plperl (TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- ok |
||||
DROP TRANSFORM IF EXISTS FOR fake_type LANGUAGE plperl; |
||||
NOTICE: type "fake_type" does not exist, skipping |
||||
DROP TRANSFORM IF EXISTS FOR hstore LANGUAGE fake_lang; |
||||
NOTICE: transform for type hstore language fake_lang does not exist, skipping |
||||
DROP TRANSFORM FOR foo LANGUAGE plperl; |
||||
ERROR: type "foo" does not exist |
||||
DROP TRANSFORM FOR hstore LANGUAGE foo; |
||||
ERROR: language "foo" does not exist |
||||
DROP TRANSFORM FOR hstore LANGUAGE plperl; |
||||
DROP TRANSFORM IF EXISTS FOR hstore LANGUAGE plperl; |
||||
NOTICE: transform for type hstore language plperl does not exist, skipping |
||||
DROP FUNCTION hstore_to_plperl(val internal); |
||||
DROP FUNCTION plperl_to_hstore(val internal); |
||||
CREATE EXTENSION hstore_plperl; |
||||
\dx+ hstore_plperl |
||||
Objects in extension "hstore_plperl" |
||||
Object Description |
||||
-------------------------------------- |
||||
function hstore_to_plperl(internal) |
||||
function plperl_to_hstore(internal) |
||||
transform for hstore language plperl |
||||
(3 rows) |
||||
|
||||
ALTER EXTENSION hstore_plperl DROP TRANSFORM FOR hstore LANGUAGE plperl; |
||||
\dx+ hstore_plperl |
||||
Objects in extension "hstore_plperl" |
||||
Object Description |
||||
------------------------------------- |
||||
function hstore_to_plperl(internal) |
||||
function plperl_to_hstore(internal) |
||||
(2 rows) |
||||
|
||||
ALTER EXTENSION hstore_plperl ADD TRANSFORM FOR hstore LANGUAGE plperl; |
||||
\dx+ hstore_plperl |
||||
Objects in extension "hstore_plperl" |
||||
Object Description |
||||
-------------------------------------- |
||||
function hstore_to_plperl(internal) |
||||
function plperl_to_hstore(internal) |
||||
transform for hstore language plperl |
||||
(3 rows) |
||||
|
||||
DROP EXTENSION hstore CASCADE; |
||||
NOTICE: drop cascades to extension hstore_plperl |
||||
DROP EXTENSION plperl CASCADE; |
@ -0,0 +1,213 @@ |
||||
CREATE EXTENSION hstore_plperl; |
||||
CREATE EXTENSION hstore_plperlu; |
||||
SELECT transforms.udt_schema, transforms.udt_name, |
||||
routine_schema, routine_name, |
||||
group_name, transform_type |
||||
FROM information_schema.transforms JOIN information_schema.routines |
||||
USING (specific_catalog, specific_schema, specific_name) |
||||
ORDER BY 1, 2, 5, 6; |
||||
udt_schema | udt_name | routine_schema | routine_name | group_name | transform_type |
||||
------------+----------+----------------+-------------------+------------+---------------- |
||||
public | hstore | public | hstore_to_plperl | plperl | FROM SQL |
||||
public | hstore | public | plperl_to_hstore | plperl | TO SQL |
||||
public | hstore | public | hstore_to_plperlu | plperlu | FROM SQL |
||||
public | hstore | public | plperlu_to_hstore | plperlu | TO SQL |
||||
(4 rows) |
||||
|
||||
-- test hstore -> perl |
||||
CREATE FUNCTION test1(val hstore) RETURNS int |
||||
LANGUAGE plperlu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
use Data::Dumper; |
||||
$Data::Dumper::Sortkeys = 1; |
||||
elog(INFO, Dumper($_[0])); |
||||
return scalar(keys %{$_[0]}); |
||||
$$; |
||||
SELECT test1('aa=>bb, cc=>NULL'::hstore); |
||||
INFO: $VAR1 = { |
||||
'aa' => 'bb', |
||||
'cc' => undef |
||||
}; |
||||
|
||||
CONTEXT: PL/Perl function "test1" |
||||
test1 |
||||
------- |
||||
2 |
||||
(1 row) |
||||
|
||||
CREATE FUNCTION test1none(val hstore) RETURNS int |
||||
LANGUAGE plperlu |
||||
AS $$ |
||||
use Data::Dumper; |
||||
$Data::Dumper::Sortkeys = 1; |
||||
elog(INFO, Dumper($_[0])); |
||||
return scalar(keys %{$_[0]}); |
||||
$$; |
||||
SELECT test1none('aa=>bb, cc=>NULL'::hstore); |
||||
INFO: $VAR1 = '"aa"=>"bb", "cc"=>NULL'; |
||||
|
||||
CONTEXT: PL/Perl function "test1none" |
||||
test1none |
||||
----------- |
||||
0 |
||||
(1 row) |
||||
|
||||
CREATE FUNCTION test1list(val hstore) RETURNS int |
||||
LANGUAGE plperlu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
use Data::Dumper; |
||||
$Data::Dumper::Sortkeys = 1; |
||||
elog(INFO, Dumper($_[0])); |
||||
return scalar(keys %{$_[0]}); |
||||
$$; |
||||
SELECT test1list('aa=>bb, cc=>NULL'::hstore); |
||||
INFO: $VAR1 = { |
||||
'aa' => 'bb', |
||||
'cc' => undef |
||||
}; |
||||
|
||||
CONTEXT: PL/Perl function "test1list" |
||||
test1list |
||||
----------- |
||||
2 |
||||
(1 row) |
||||
|
||||
-- test hstore[] -> perl |
||||
CREATE FUNCTION test1arr(val hstore[]) RETURNS int |
||||
LANGUAGE plperlu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
use Data::Dumper; |
||||
$Data::Dumper::Sortkeys = 1; |
||||
elog(INFO, Dumper($_[0]->[0], $_[0]->[1])); |
||||
return scalar(keys %{$_[0]}); |
||||
$$; |
||||
SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']); |
||||
INFO: $VAR1 = { |
||||
'aa' => 'bb', |
||||
'cc' => undef |
||||
}; |
||||
$VAR2 = { |
||||
'dd' => 'ee' |
||||
}; |
||||
|
||||
CONTEXT: PL/Perl function "test1arr" |
||||
test1arr |
||||
---------- |
||||
2 |
||||
(1 row) |
||||
|
||||
-- test perl -> hstore |
||||
CREATE FUNCTION test2() RETURNS hstore |
||||
LANGUAGE plperl |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
$val = {a => 1, b => 'boo', c => undef}; |
||||
return $val; |
||||
$$; |
||||
SELECT test2(); |
||||
test2 |
||||
--------------------------------- |
||||
"a"=>"1", "b"=>"boo", "c"=>NULL |
||||
(1 row) |
||||
|
||||
-- test perl -> hstore[] |
||||
CREATE FUNCTION test2arr() RETURNS hstore[] |
||||
LANGUAGE plperl |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
$val = [{a => 1, b => 'boo', c => undef}, {d => 2}]; |
||||
return $val; |
||||
$$; |
||||
SELECT test2arr(); |
||||
test2arr |
||||
-------------------------------------------------------------- |
||||
{"\"a\"=>\"1\", \"b\"=>\"boo\", \"c\"=>NULL","\"d\"=>\"2\""} |
||||
(1 row) |
||||
|
||||
-- test as part of prepare/execute |
||||
CREATE FUNCTION test3() RETURNS void |
||||
LANGUAGE plperlu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
use Data::Dumper; |
||||
$Data::Dumper::Sortkeys = 1; |
||||
|
||||
$rv = spi_exec_query(q{SELECT 'aa=>bb, cc=>NULL'::hstore AS col1}); |
||||
elog(INFO, Dumper($rv->{rows}[0]->{col1})); |
||||
|
||||
$val = {a => 1, b => 'boo', c => undef}; |
||||
$plan = spi_prepare(q{SELECT $1::text AS col1}, "hstore"); |
||||
$rv = spi_exec_prepared($plan, {}, $val); |
||||
elog(INFO, Dumper($rv->{rows}[0]->{col1})); |
||||
$$; |
||||
SELECT test3(); |
||||
INFO: $VAR1 = { |
||||
'aa' => 'bb', |
||||
'cc' => undef |
||||
}; |
||||
|
||||
CONTEXT: PL/Perl function "test3" |
||||
INFO: $VAR1 = '"a"=>"1", "b"=>"boo", "c"=>NULL'; |
||||
|
||||
CONTEXT: PL/Perl function "test3" |
||||
test3 |
||||
------- |
||||
|
||||
(1 row) |
||||
|
||||
-- test trigger |
||||
CREATE TABLE test1 (a int, b hstore); |
||||
INSERT INTO test1 VALUES (1, 'aa=>bb, cc=>NULL'); |
||||
SELECT * FROM test1; |
||||
a | b |
||||
---+------------------------ |
||||
1 | "aa"=>"bb", "cc"=>NULL |
||||
(1 row) |
||||
|
||||
CREATE FUNCTION test4() RETURNS trigger |
||||
LANGUAGE plperlu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
use Data::Dumper; |
||||
$Data::Dumper::Sortkeys = 1; |
||||
elog(INFO, Dumper($_TD->{new})); |
||||
if ($_TD->{new}{a} == 1) { |
||||
$_TD->{new}{b} = {a => 1, b => 'boo', c => undef}; |
||||
} |
||||
|
||||
return "MODIFY"; |
||||
$$; |
||||
CREATE TRIGGER test4 BEFORE UPDATE ON test1 FOR EACH ROW EXECUTE PROCEDURE test4(); |
||||
UPDATE test1 SET a = a; |
||||
INFO: $VAR1 = { |
||||
'a' => '1', |
||||
'b' => { |
||||
'aa' => 'bb', |
||||
'cc' => undef |
||||
} |
||||
}; |
||||
|
||||
CONTEXT: PL/Perl function "test4" |
||||
SELECT * FROM test1; |
||||
a | b |
||||
---+--------------------------------- |
||||
1 | "a"=>"1", "b"=>"boo", "c"=>NULL |
||||
(1 row) |
||||
|
||||
DROP TABLE test1; |
||||
DROP FUNCTION test1(hstore); |
||||
DROP FUNCTION test1none(hstore); |
||||
DROP FUNCTION test1list(hstore); |
||||
DROP FUNCTION test1arr(hstore[]); |
||||
DROP FUNCTION test2(); |
||||
DROP FUNCTION test2arr(); |
||||
DROP FUNCTION test3(); |
||||
DROP FUNCTION test4(); |
||||
DROP EXTENSION hstore_plperl; |
||||
DROP EXTENSION hstore_plperlu; |
||||
DROP EXTENSION hstore; |
||||
DROP EXTENSION plperl; |
||||
DROP EXTENSION plperlu; |
@ -0,0 +1,17 @@ |
||||
-- make sure the prerequisite libraries are loaded |
||||
DO '' LANGUAGE plperl; |
||||
SELECT NULL::hstore; |
||||
|
||||
|
||||
CREATE FUNCTION hstore_to_plperl(val internal) RETURNS internal |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS 'MODULE_PATHNAME'; |
||||
|
||||
CREATE FUNCTION plperl_to_hstore(val internal) RETURNS hstore |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS 'MODULE_PATHNAME'; |
||||
|
||||
CREATE TRANSFORM FOR hstore LANGUAGE plperl ( |
||||
FROM SQL WITH FUNCTION hstore_to_plperl(internal), |
||||
TO SQL WITH FUNCTION plperl_to_hstore(internal) |
||||
); |
@ -0,0 +1,90 @@ |
||||
#include "postgres.h" |
||||
#undef _ |
||||
#include "fmgr.h" |
||||
#include "plperl.h" |
||||
#include "plperl_helpers.h" |
||||
#include "hstore.h" |
||||
|
||||
PG_MODULE_MAGIC; |
||||
|
||||
|
||||
PG_FUNCTION_INFO_V1(hstore_to_plperl); |
||||
Datum hstore_to_plperl(PG_FUNCTION_ARGS); |
||||
|
||||
Datum |
||||
hstore_to_plperl(PG_FUNCTION_ARGS) |
||||
{ |
||||
HStore *in = PG_GETARG_HS(0); |
||||
int i; |
||||
int count = HS_COUNT(in); |
||||
char *base = STRPTR(in); |
||||
HEntry *entries = ARRPTR(in); |
||||
HV *hv; |
||||
|
||||
hv = newHV(); |
||||
|
||||
for (i = 0; i < count; i++) |
||||
{ |
||||
const char *key; |
||||
SV *value; |
||||
|
||||
key = pnstrdup(HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); |
||||
value = HS_VALISNULL(entries, i) ? newSV(0) : cstr2sv(pnstrdup(HS_VAL(entries, base,i), HS_VALLEN(entries, i))); |
||||
|
||||
(void) hv_store(hv, key, strlen(key), value, 0); |
||||
} |
||||
|
||||
return PointerGetDatum(newRV((SV *) hv)); |
||||
} |
||||
|
||||
|
||||
PG_FUNCTION_INFO_V1(plperl_to_hstore); |
||||
Datum plperl_to_hstore(PG_FUNCTION_ARGS); |
||||
|
||||
Datum |
||||
plperl_to_hstore(PG_FUNCTION_ARGS) |
||||
{ |
||||
HV *hv; |
||||
HE *he; |
||||
int32 buflen; |
||||
int32 i; |
||||
int32 pcount; |
||||
HStore *out; |
||||
Pairs *pairs; |
||||
|
||||
hv = (HV *) SvRV((SV *) PG_GETARG_POINTER(0)); |
||||
|
||||
pcount = hv_iterinit(hv); |
||||
|
||||
pairs = palloc(pcount * sizeof(Pairs)); |
||||
|
||||
i = 0; |
||||
while ((he = hv_iternext(hv))) |
||||
{ |
||||
char *key = sv2cstr(HeSVKEY_force(he)); |
||||
SV *value = HeVAL(he); |
||||
|
||||
pairs[i].key = pstrdup(key); |
||||
pairs[i].keylen = hstoreCheckKeyLen(strlen(pairs[i].key)); |
||||
pairs[i].needfree = true; |
||||
|
||||
if (!SvOK(value)) |
||||
{ |
||||
pairs[i].val = NULL; |
||||
pairs[i].vallen = 0; |
||||
pairs[i].isnull = true; |
||||
} |
||||
else |
||||
{ |
||||
pairs[i].val = pstrdup(sv2cstr(value)); |
||||
pairs[i].vallen = hstoreCheckValLen(strlen(pairs[i].val)); |
||||
pairs[i].isnull = false; |
||||
} |
||||
|
||||
i++; |
||||
} |
||||
|
||||
pcount = hstoreUniquePairs(pairs, pcount, &buflen); |
||||
out = hstorePairs(pairs, pcount, buflen); |
||||
PG_RETURN_POINTER(out); |
||||
} |
@ -0,0 +1,6 @@ |
||||
# hstore_plperl extension |
||||
comment = 'transform between hstore and plperl' |
||||
default_version = '1.0' |
||||
module_pathname = '$libdir/hstore_plperl' |
||||
relocatable = true |
||||
requires = 'hstore,plperl' |
@ -0,0 +1,17 @@ |
||||
-- make sure the prerequisite libraries are loaded |
||||
DO '' LANGUAGE plperlu; |
||||
SELECT NULL::hstore; |
||||
|
||||
|
||||
CREATE FUNCTION hstore_to_plperlu(val internal) RETURNS internal |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS 'MODULE_PATHNAME', 'hstore_to_plperl'; |
||||
|
||||
CREATE FUNCTION plperlu_to_hstore(val internal) RETURNS hstore |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS 'MODULE_PATHNAME', 'plperl_to_hstore'; |
||||
|
||||
CREATE TRANSFORM FOR hstore LANGUAGE plperlu ( |
||||
FROM SQL WITH FUNCTION hstore_to_plperlu(internal), |
||||
TO SQL WITH FUNCTION plperlu_to_hstore(internal) |
||||
); |
@ -0,0 +1,6 @@ |
||||
# hstore_plperlu extension |
||||
comment = 'transform between hstore and plperlu' |
||||
default_version = '1.0' |
||||
module_pathname = '$libdir/hstore_plperl' |
||||
relocatable = true |
||||
requires = 'hstore,plperlu' |
@ -0,0 +1,47 @@ |
||||
-- general regression test for transforms |
||||
|
||||
DROP EXTENSION IF EXISTS hstore CASCADE; |
||||
DROP EXTENSION IF EXISTS plperl CASCADE; |
||||
DROP EXTENSION IF EXISTS hstore_plperl CASCADE; |
||||
|
||||
CREATE EXTENSION hstore; |
||||
CREATE EXTENSION plperl; |
||||
|
||||
CREATE FUNCTION hstore_to_plperl(val internal) RETURNS internal |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS '$libdir/hstore_plperl'; |
||||
|
||||
CREATE FUNCTION plperl_to_hstore(val internal) RETURNS hstore |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS '$libdir/hstore_plperl'; |
||||
|
||||
CREATE TRANSFORM FOR foo LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail |
||||
CREATE TRANSFORM FOR hstore LANGUAGE foo (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail |
||||
CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_out(hstore), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail |
||||
CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION internal_in(cstring), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail |
||||
|
||||
CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- ok |
||||
CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail |
||||
CREATE OR REPLACE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- ok |
||||
CREATE OR REPLACE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal)); -- ok |
||||
CREATE OR REPLACE TRANSFORM FOR hstore LANGUAGE plperl (TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- ok |
||||
|
||||
DROP TRANSFORM IF EXISTS FOR fake_type LANGUAGE plperl; |
||||
DROP TRANSFORM IF EXISTS FOR hstore LANGUAGE fake_lang; |
||||
DROP TRANSFORM FOR foo LANGUAGE plperl; |
||||
DROP TRANSFORM FOR hstore LANGUAGE foo; |
||||
DROP TRANSFORM FOR hstore LANGUAGE plperl; |
||||
DROP TRANSFORM IF EXISTS FOR hstore LANGUAGE plperl; |
||||
|
||||
DROP FUNCTION hstore_to_plperl(val internal); |
||||
DROP FUNCTION plperl_to_hstore(val internal); |
||||
|
||||
CREATE EXTENSION hstore_plperl; |
||||
\dx+ hstore_plperl |
||||
ALTER EXTENSION hstore_plperl DROP TRANSFORM FOR hstore LANGUAGE plperl; |
||||
\dx+ hstore_plperl |
||||
ALTER EXTENSION hstore_plperl ADD TRANSFORM FOR hstore LANGUAGE plperl; |
||||
\dx+ hstore_plperl |
||||
|
||||
DROP EXTENSION hstore CASCADE; |
||||
DROP EXTENSION plperl CASCADE; |
@ -0,0 +1,148 @@ |
||||
CREATE EXTENSION hstore_plperl; |
||||
CREATE EXTENSION hstore_plperlu; |
||||
|
||||
SELECT transforms.udt_schema, transforms.udt_name, |
||||
routine_schema, routine_name, |
||||
group_name, transform_type |
||||
FROM information_schema.transforms JOIN information_schema.routines |
||||
USING (specific_catalog, specific_schema, specific_name) |
||||
ORDER BY 1, 2, 5, 6; |
||||
|
||||
|
||||
-- test hstore -> perl |
||||
CREATE FUNCTION test1(val hstore) RETURNS int |
||||
LANGUAGE plperlu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
use Data::Dumper; |
||||
$Data::Dumper::Sortkeys = 1; |
||||
elog(INFO, Dumper($_[0])); |
||||
return scalar(keys %{$_[0]}); |
||||
$$; |
||||
|
||||
SELECT test1('aa=>bb, cc=>NULL'::hstore); |
||||
|
||||
CREATE FUNCTION test1none(val hstore) RETURNS int |
||||
LANGUAGE plperlu |
||||
AS $$ |
||||
use Data::Dumper; |
||||
$Data::Dumper::Sortkeys = 1; |
||||
elog(INFO, Dumper($_[0])); |
||||
return scalar(keys %{$_[0]}); |
||||
$$; |
||||
|
||||
SELECT test1none('aa=>bb, cc=>NULL'::hstore); |
||||
|
||||
CREATE FUNCTION test1list(val hstore) RETURNS int |
||||
LANGUAGE plperlu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
use Data::Dumper; |
||||
$Data::Dumper::Sortkeys = 1; |
||||
elog(INFO, Dumper($_[0])); |
||||
return scalar(keys %{$_[0]}); |
||||
$$; |
||||
|
||||
SELECT test1list('aa=>bb, cc=>NULL'::hstore); |
||||
|
||||
|
||||
-- test hstore[] -> perl |
||||
CREATE FUNCTION test1arr(val hstore[]) RETURNS int |
||||
LANGUAGE plperlu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
use Data::Dumper; |
||||
$Data::Dumper::Sortkeys = 1; |
||||
elog(INFO, Dumper($_[0]->[0], $_[0]->[1])); |
||||
return scalar(keys %{$_[0]}); |
||||
$$; |
||||
|
||||
SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']); |
||||
|
||||
|
||||
-- test perl -> hstore |
||||
CREATE FUNCTION test2() RETURNS hstore |
||||
LANGUAGE plperl |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
$val = {a => 1, b => 'boo', c => undef}; |
||||
return $val; |
||||
$$; |
||||
|
||||
SELECT test2(); |
||||
|
||||
|
||||
-- test perl -> hstore[] |
||||
CREATE FUNCTION test2arr() RETURNS hstore[] |
||||
LANGUAGE plperl |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
$val = [{a => 1, b => 'boo', c => undef}, {d => 2}]; |
||||
return $val; |
||||
$$; |
||||
|
||||
SELECT test2arr(); |
||||
|
||||
|
||||
-- test as part of prepare/execute |
||||
CREATE FUNCTION test3() RETURNS void |
||||
LANGUAGE plperlu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
use Data::Dumper; |
||||
$Data::Dumper::Sortkeys = 1; |
||||
|
||||
$rv = spi_exec_query(q{SELECT 'aa=>bb, cc=>NULL'::hstore AS col1}); |
||||
elog(INFO, Dumper($rv->{rows}[0]->{col1})); |
||||
|
||||
$val = {a => 1, b => 'boo', c => undef}; |
||||
$plan = spi_prepare(q{SELECT $1::text AS col1}, "hstore"); |
||||
$rv = spi_exec_prepared($plan, {}, $val); |
||||
elog(INFO, Dumper($rv->{rows}[0]->{col1})); |
||||
$$; |
||||
|
||||
SELECT test3(); |
||||
|
||||
|
||||
-- test trigger |
||||
CREATE TABLE test1 (a int, b hstore); |
||||
INSERT INTO test1 VALUES (1, 'aa=>bb, cc=>NULL'); |
||||
SELECT * FROM test1; |
||||
|
||||
CREATE FUNCTION test4() RETURNS trigger |
||||
LANGUAGE plperlu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
use Data::Dumper; |
||||
$Data::Dumper::Sortkeys = 1; |
||||
elog(INFO, Dumper($_TD->{new})); |
||||
if ($_TD->{new}{a} == 1) { |
||||
$_TD->{new}{b} = {a => 1, b => 'boo', c => undef}; |
||||
} |
||||
|
||||
return "MODIFY"; |
||||
$$; |
||||
|
||||
CREATE TRIGGER test4 BEFORE UPDATE ON test1 FOR EACH ROW EXECUTE PROCEDURE test4(); |
||||
|
||||
UPDATE test1 SET a = a; |
||||
SELECT * FROM test1; |
||||
|
||||
|
||||
DROP TABLE test1; |
||||
|
||||
DROP FUNCTION test1(hstore); |
||||
DROP FUNCTION test1none(hstore); |
||||
DROP FUNCTION test1list(hstore); |
||||
DROP FUNCTION test1arr(hstore[]); |
||||
DROP FUNCTION test2(); |
||||
DROP FUNCTION test2arr(); |
||||
DROP FUNCTION test3(); |
||||
DROP FUNCTION test4(); |
||||
|
||||
|
||||
DROP EXTENSION hstore_plperl; |
||||
DROP EXTENSION hstore_plperlu; |
||||
DROP EXTENSION hstore; |
||||
DROP EXTENSION plperl; |
||||
DROP EXTENSION plperlu; |
@ -0,0 +1,6 @@ |
||||
# Generated subdirectories |
||||
/expected/python3/ |
||||
/log/ |
||||
/results/ |
||||
/sql/python3/ |
||||
/tmp_check/ |
@ -0,0 +1,31 @@ |
||||
# contrib/hstore_plpython/Makefile
|
||||
|
||||
MODULE_big = hstore_plpython$(python_majorversion)
|
||||
OBJS = hstore_plpython.o
|
||||
|
||||
PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plpython $(python_includespec) -I$(top_srcdir)/contrib/hstore
|
||||
|
||||
EXTENSION = hstore_plpythonu hstore_plpython2u hstore_plpython3u
|
||||
DATA = hstore_plpythonu--1.0.sql hstore_plpython2u--1.0.sql hstore_plpython3u--1.0.sql
|
||||
|
||||
REGRESS = hstore_plpython
|
||||
REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
|
||||
|
||||
ifdef USE_PGXS |
||||
PG_CONFIG = pg_config
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
include $(PGXS) |
||||
else |
||||
subdir = contrib/hstore_plpython
|
||||
top_builddir = ../..
|
||||
include $(top_builddir)/src/Makefile.global |
||||
include $(top_srcdir)/contrib/contrib-global.mk |
||||
endif |
||||
|
||||
REGRESS_OPTS = --load-extension=hstore
|
||||
ifeq ($(python_majorversion),2) |
||||
REGRESS_OPTS += --load-extension=plpythonu --load-extension=hstore_plpythonu
|
||||
endif |
||||
EXTRA_INSTALL = contrib/hstore
|
||||
|
||||
include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk |
@ -0,0 +1,132 @@ |
||||
CREATE EXTENSION plpython2u; |
||||
CREATE EXTENSION hstore_plpython2u; |
||||
-- test hstore -> python |
||||
CREATE FUNCTION test1(val hstore) RETURNS int |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
assert isinstance(val, dict) |
||||
plpy.info(sorted(val.items())) |
||||
return len(val) |
||||
$$; |
||||
SELECT test1('aa=>bb, cc=>NULL'::hstore); |
||||
INFO: [('aa', 'bb'), ('cc', None)] |
||||
CONTEXT: PL/Python function "test1" |
||||
test1 |
||||
------- |
||||
2 |
||||
(1 row) |
||||
|
||||
-- the same with the versioned language name |
||||
CREATE FUNCTION test1n(val hstore) RETURNS int |
||||
LANGUAGE plpython2u |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
assert isinstance(val, dict) |
||||
plpy.info(sorted(val.items())) |
||||
return len(val) |
||||
$$; |
||||
SELECT test1n('aa=>bb, cc=>NULL'::hstore); |
||||
INFO: [('aa', 'bb'), ('cc', None)] |
||||
CONTEXT: PL/Python function "test1n" |
||||
test1n |
||||
-------- |
||||
2 |
||||
(1 row) |
||||
|
||||
-- test hstore[] -> python |
||||
CREATE FUNCTION test1arr(val hstore[]) RETURNS int |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
plpy.info(repr(val)) |
||||
return len(val) |
||||
$$; |
||||
SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']); |
||||
INFO: [{'aa': 'bb', 'cc': None}, {'dd': 'ee'}] |
||||
CONTEXT: PL/Python function "test1arr" |
||||
test1arr |
||||
---------- |
||||
2 |
||||
(1 row) |
||||
|
||||
-- test python -> hstore |
||||
CREATE FUNCTION test2() RETURNS hstore |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
val = {'a': 1, 'b': 'boo', 'c': None} |
||||
return val |
||||
$$; |
||||
SELECT test2(); |
||||
test2 |
||||
--------------------------------- |
||||
"a"=>"1", "b"=>"boo", "c"=>NULL |
||||
(1 row) |
||||
|
||||
-- test python -> hstore[] |
||||
CREATE FUNCTION test2arr() RETURNS hstore[] |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
val = [{'a': 1, 'b': 'boo', 'c': None}, {'d': 2}] |
||||
return val |
||||
$$; |
||||
SELECT test2arr(); |
||||
test2arr |
||||
-------------------------------------------------------------- |
||||
{"\"a\"=>\"1\", \"b\"=>\"boo\", \"c\"=>NULL","\"d\"=>\"2\""} |
||||
(1 row) |
||||
|
||||
-- test as part of prepare/execute |
||||
CREATE FUNCTION test3() RETURNS void |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
rv = plpy.execute("SELECT 'aa=>bb, cc=>NULL'::hstore AS col1") |
||||
plpy.info(repr(rv[0]["col1"])) |
||||
|
||||
val = {'a': 1, 'b': 'boo', 'c': None} |
||||
plan = plpy.prepare("SELECT $1::text AS col1", ["hstore"]) |
||||
rv = plpy.execute(plan, [val]) |
||||
plpy.info(repr(rv[0]["col1"])) |
||||
$$; |
||||
SELECT test3(); |
||||
INFO: {'aa': 'bb', 'cc': None} |
||||
CONTEXT: PL/Python function "test3" |
||||
INFO: '"a"=>"1", "b"=>"boo", "c"=>NULL' |
||||
CONTEXT: PL/Python function "test3" |
||||
test3 |
||||
------- |
||||
|
||||
(1 row) |
||||
|
||||
-- test trigger |
||||
CREATE TABLE test1 (a int, b hstore); |
||||
INSERT INTO test1 VALUES (1, 'aa=>bb, cc=>NULL'); |
||||
SELECT * FROM test1; |
||||
a | b |
||||
---+------------------------ |
||||
1 | "aa"=>"bb", "cc"=>NULL |
||||
(1 row) |
||||
|
||||
CREATE FUNCTION test4() RETURNS trigger |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
plpy.info("Trigger row: {'a': %r, 'b': %r}" % (TD["new"]["a"], TD["new"]["b"])) |
||||
if TD["new"]["a"] == 1: |
||||
TD["new"]["b"] = {'a': 1, 'b': 'boo', 'c': None} |
||||
|
||||
return "MODIFY" |
||||
$$; |
||||
CREATE TRIGGER test4 BEFORE UPDATE ON test1 FOR EACH ROW EXECUTE PROCEDURE test4(); |
||||
UPDATE test1 SET a = a; |
||||
INFO: Trigger row: {'a': 1, 'b': {'aa': 'bb', 'cc': None}} |
||||
CONTEXT: PL/Python function "test4" |
||||
SELECT * FROM test1; |
||||
a | b |
||||
---+--------------------------------- |
||||
1 | "a"=>"1", "b"=>"boo", "c"=>NULL |
||||
(1 row) |
||||
|
@ -0,0 +1,116 @@ |
||||
#include "postgres.h" |
||||
#include "fmgr.h" |
||||
#include "plpython.h" |
||||
#include "plpy_typeio.h" |
||||
#include "hstore.h" |
||||
|
||||
PG_MODULE_MAGIC; |
||||
|
||||
|
||||
PG_FUNCTION_INFO_V1(hstore_to_plpython); |
||||
Datum hstore_to_plpython(PG_FUNCTION_ARGS); |
||||
|
||||
Datum |
||||
hstore_to_plpython(PG_FUNCTION_ARGS) |
||||
{ |
||||
HStore *in = PG_GETARG_HS(0); |
||||
int i; |
||||
int count = HS_COUNT(in); |
||||
char *base = STRPTR(in); |
||||
HEntry *entries = ARRPTR(in); |
||||
PyObject *dict; |
||||
|
||||
dict = PyDict_New(); |
||||
|
||||
for (i = 0; i < count; i++) |
||||
{ |
||||
PyObject *key; |
||||
|
||||
key = PyString_FromStringAndSize(HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); |
||||
if (HS_VALISNULL(entries, i)) |
||||
PyDict_SetItem(dict, key, Py_None); |
||||
else |
||||
{ |
||||
PyObject *value; |
||||
|
||||
value = PyString_FromStringAndSize(HS_VAL(entries, base,i), HS_VALLEN(entries, i)); |
||||
PyDict_SetItem(dict, key, value); |
||||
Py_XDECREF(value); |
||||
} |
||||
Py_XDECREF(key); |
||||
} |
||||
|
||||
return PointerGetDatum(dict); |
||||
} |
||||
|
||||
|
||||
PG_FUNCTION_INFO_V1(plpython_to_hstore); |
||||
Datum plpython_to_hstore(PG_FUNCTION_ARGS); |
||||
|
||||
Datum |
||||
plpython_to_hstore(PG_FUNCTION_ARGS) |
||||
{ |
||||
PyObject *dict; |
||||
volatile PyObject *items_v = NULL; |
||||
int32 pcount; |
||||
HStore *out; |
||||
|
||||
dict = (PyObject *) PG_GETARG_POINTER(0); |
||||
if (!PyMapping_Check(dict)) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE), |
||||
errmsg("not a Python mapping"))); |
||||
|
||||
pcount = PyMapping_Size(dict); |
||||
items_v = PyMapping_Items(dict); |
||||
|
||||
PG_TRY(); |
||||
{ |
||||
int32 buflen; |
||||
int32 i; |
||||
Pairs *pairs; |
||||
PyObject *items = (PyObject *) items_v; |
||||
|
||||
pairs = palloc(pcount * sizeof(*pairs)); |
||||
|
||||
for (i = 0; i < pcount; i++) |
||||
{ |
||||
PyObject *tuple; |
||||
PyObject *key; |
||||
PyObject *value; |
||||
|
||||
tuple = PyList_GetItem(items, i); |
||||
key = PyTuple_GetItem(tuple, 0); |
||||
value = PyTuple_GetItem(tuple, 1); |
||||
|
||||
pairs[i].key = PLyObject_AsString(key); |
||||
pairs[i].keylen = hstoreCheckKeyLen(strlen(pairs[i].key)); |
||||
pairs[i].needfree = true; |
||||
|
||||
if (value == Py_None) |
||||
{ |
||||
pairs[i].val = NULL; |
||||
pairs[i].vallen = 0; |
||||
pairs[i].isnull = true; |
||||
} |
||||
else |
||||
{ |
||||
pairs[i].val = PLyObject_AsString(value); |
||||
pairs[i].vallen = hstoreCheckValLen(strlen(pairs[i].val)); |
||||
pairs[i].isnull = false; |
||||
} |
||||
} |
||||
Py_DECREF(items_v); |
||||
|
||||
pcount = hstoreUniquePairs(pairs, pcount, &buflen); |
||||
out = hstorePairs(pairs, pcount, buflen); |
||||
} |
||||
PG_CATCH(); |
||||
{ |
||||
Py_DECREF(items_v); |
||||
PG_RE_THROW(); |
||||
} |
||||
PG_END_TRY(); |
||||
|
||||
PG_RETURN_POINTER(out); |
||||
} |
@ -0,0 +1,19 @@ |
||||
-- make sure the prerequisite libraries are loaded |
||||
DO '1' LANGUAGE plpython2u; |
||||
SELECT NULL::hstore; |
||||
|
||||
|
||||
CREATE FUNCTION hstore_to_plpython2(val internal) RETURNS internal |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS 'MODULE_PATHNAME', 'hstore_to_plpython'; |
||||
|
||||
CREATE FUNCTION plpython2_to_hstore(val internal) RETURNS hstore |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS 'MODULE_PATHNAME', 'plpython_to_hstore'; |
||||
|
||||
CREATE TRANSFORM FOR hstore LANGUAGE plpython2u ( |
||||
FROM SQL WITH FUNCTION hstore_to_plpython2(internal), |
||||
TO SQL WITH FUNCTION plpython2_to_hstore(internal) |
||||
); |
||||
|
||||
COMMENT ON TRANSFORM FOR hstore LANGUAGE plpython2u IS 'transform between hstore and Python dict'; |
@ -0,0 +1,6 @@ |
||||
# hstore_plpython2u extension |
||||
comment = 'transform between hstore and plpython2u' |
||||
default_version = '1.0' |
||||
module_pathname = '$libdir/hstore_plpython2' |
||||
relocatable = true |
||||
requires = 'hstore,plpython2u' |
@ -0,0 +1,19 @@ |
||||
-- make sure the prerequisite libraries are loaded |
||||
DO '1' LANGUAGE plpython3u; |
||||
SELECT NULL::hstore; |
||||
|
||||
|
||||
CREATE FUNCTION hstore_to_plpython3(val internal) RETURNS internal |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS 'MODULE_PATHNAME', 'hstore_to_plpython'; |
||||
|
||||
CREATE FUNCTION plpython3_to_hstore(val internal) RETURNS hstore |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS 'MODULE_PATHNAME', 'plpython_to_hstore'; |
||||
|
||||
CREATE TRANSFORM FOR hstore LANGUAGE plpython3u ( |
||||
FROM SQL WITH FUNCTION hstore_to_plpython3(internal), |
||||
TO SQL WITH FUNCTION plpython3_to_hstore(internal) |
||||
); |
||||
|
||||
COMMENT ON TRANSFORM FOR hstore LANGUAGE plpython3u IS 'transform between hstore and Python dict'; |
@ -0,0 +1,6 @@ |
||||
# hstore_plpython3u extension |
||||
comment = 'transform between hstore and plpython3u' |
||||
default_version = '1.0' |
||||
module_pathname = '$libdir/hstore_plpython3' |
||||
relocatable = true |
||||
requires = 'hstore,plpython3u' |
@ -0,0 +1,19 @@ |
||||
-- make sure the prerequisite libraries are loaded |
||||
DO '1' LANGUAGE plpythonu; |
||||
SELECT NULL::hstore; |
||||
|
||||
|
||||
CREATE FUNCTION hstore_to_plpython(val internal) RETURNS internal |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS 'MODULE_PATHNAME'; |
||||
|
||||
CREATE FUNCTION plpython_to_hstore(val internal) RETURNS hstore |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS 'MODULE_PATHNAME'; |
||||
|
||||
CREATE TRANSFORM FOR hstore LANGUAGE plpythonu ( |
||||
FROM SQL WITH FUNCTION hstore_to_plpython(internal), |
||||
TO SQL WITH FUNCTION plpython_to_hstore(internal) |
||||
); |
||||
|
||||
COMMENT ON TRANSFORM FOR hstore LANGUAGE plpythonu IS 'transform between hstore and Python dict'; |
@ -0,0 +1,6 @@ |
||||
# hstore_plpythonu extension |
||||
comment = 'transform between hstore and plpythonu' |
||||
default_version = '1.0' |
||||
module_pathname = '$libdir/hstore_plpython2' |
||||
relocatable = true |
||||
requires = 'hstore,plpythonu' |
@ -0,0 +1,103 @@ |
||||
CREATE EXTENSION plpython2u; |
||||
CREATE EXTENSION hstore_plpython2u; |
||||
|
||||
|
||||
-- test hstore -> python |
||||
CREATE FUNCTION test1(val hstore) RETURNS int |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
assert isinstance(val, dict) |
||||
plpy.info(sorted(val.items())) |
||||
return len(val) |
||||
$$; |
||||
|
||||
SELECT test1('aa=>bb, cc=>NULL'::hstore); |
||||
|
||||
|
||||
-- the same with the versioned language name |
||||
CREATE FUNCTION test1n(val hstore) RETURNS int |
||||
LANGUAGE plpython2u |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
assert isinstance(val, dict) |
||||
plpy.info(sorted(val.items())) |
||||
return len(val) |
||||
$$; |
||||
|
||||
SELECT test1n('aa=>bb, cc=>NULL'::hstore); |
||||
|
||||
|
||||
-- test hstore[] -> python |
||||
CREATE FUNCTION test1arr(val hstore[]) RETURNS int |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
plpy.info(repr(val)) |
||||
return len(val) |
||||
$$; |
||||
|
||||
SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']); |
||||
|
||||
|
||||
-- test python -> hstore |
||||
CREATE FUNCTION test2() RETURNS hstore |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
val = {'a': 1, 'b': 'boo', 'c': None} |
||||
return val |
||||
$$; |
||||
|
||||
SELECT test2(); |
||||
|
||||
|
||||
-- test python -> hstore[] |
||||
CREATE FUNCTION test2arr() RETURNS hstore[] |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
val = [{'a': 1, 'b': 'boo', 'c': None}, {'d': 2}] |
||||
return val |
||||
$$; |
||||
|
||||
SELECT test2arr(); |
||||
|
||||
|
||||
-- test as part of prepare/execute |
||||
CREATE FUNCTION test3() RETURNS void |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
rv = plpy.execute("SELECT 'aa=>bb, cc=>NULL'::hstore AS col1") |
||||
plpy.info(repr(rv[0]["col1"])) |
||||
|
||||
val = {'a': 1, 'b': 'boo', 'c': None} |
||||
plan = plpy.prepare("SELECT $1::text AS col1", ["hstore"]) |
||||
rv = plpy.execute(plan, [val]) |
||||
plpy.info(repr(rv[0]["col1"])) |
||||
$$; |
||||
|
||||
SELECT test3(); |
||||
|
||||
|
||||
-- test trigger |
||||
CREATE TABLE test1 (a int, b hstore); |
||||
INSERT INTO test1 VALUES (1, 'aa=>bb, cc=>NULL'); |
||||
SELECT * FROM test1; |
||||
|
||||
CREATE FUNCTION test4() RETURNS trigger |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE hstore |
||||
AS $$ |
||||
plpy.info("Trigger row: {'a': %r, 'b': %r}" % (TD["new"]["a"], TD["new"]["b"])) |
||||
if TD["new"]["a"] == 1: |
||||
TD["new"]["b"] = {'a': 1, 'b': 'boo', 'c': None} |
||||
|
||||
return "MODIFY" |
||||
$$; |
||||
|
||||
CREATE TRIGGER test4 BEFORE UPDATE ON test1 FOR EACH ROW EXECUTE PROCEDURE test4(); |
||||
|
||||
UPDATE test1 SET a = a; |
||||
SELECT * FROM test1; |
@ -0,0 +1,6 @@ |
||||
# Generated subdirectories |
||||
/expected/python3/ |
||||
/log/ |
||||
/results/ |
||||
/sql/python3/ |
||||
/tmp_check/ |
@ -0,0 +1,31 @@ |
||||
# contrib/ltree_plpython/Makefile
|
||||
|
||||
MODULE_big = ltree_plpython$(python_majorversion)
|
||||
OBJS = ltree_plpython.o
|
||||
|
||||
PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plpython $(python_includespec) -I$(top_srcdir)/contrib/ltree
|
||||
|
||||
EXTENSION = ltree_plpythonu ltree_plpython2u ltree_plpython3u
|
||||
DATA = ltree_plpythonu--1.0.sql ltree_plpython2u--1.0.sql ltree_plpython3u--1.0.sql
|
||||
|
||||
REGRESS = ltree_plpython
|
||||
REGRESS_PLPYTHON3_MANGLE := $(REGRESS)
|
||||
|
||||
ifdef USE_PGXS |
||||
PG_CONFIG = pg_config
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
include $(PGXS) |
||||
else |
||||
subdir = contrib/ltree_plpython
|
||||
top_builddir = ../..
|
||||
include $(top_builddir)/src/Makefile.global |
||||
include $(top_srcdir)/contrib/contrib-global.mk |
||||
endif |
||||
|
||||
REGRESS_OPTS = --load-extension=ltree
|
||||
ifeq ($(python_majorversion),2) |
||||
REGRESS_OPTS += --load-extension=plpythonu --load-extension=ltree_plpythonu
|
||||
endif |
||||
EXTRA_INSTALL = contrib/ltree
|
||||
|
||||
include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk |
@ -0,0 +1,45 @@ |
||||
CREATE EXTENSION plpython2u; |
||||
CREATE EXTENSION ltree_plpython2u; |
||||
CREATE FUNCTION test1(val ltree) RETURNS int |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE ltree |
||||
AS $$ |
||||
plpy.info(repr(val)) |
||||
return len(val) |
||||
$$; |
||||
SELECT test1('aa.bb.cc'::ltree); |
||||
INFO: ['aa', 'bb', 'cc'] |
||||
CONTEXT: PL/Python function "test1" |
||||
test1 |
||||
------- |
||||
3 |
||||
(1 row) |
||||
|
||||
CREATE FUNCTION test1n(val ltree) RETURNS int |
||||
LANGUAGE plpython2u |
||||
TRANSFORM FOR TYPE ltree |
||||
AS $$ |
||||
plpy.info(repr(val)) |
||||
return len(val) |
||||
$$; |
||||
SELECT test1n('aa.bb.cc'::ltree); |
||||
INFO: ['aa', 'bb', 'cc'] |
||||
CONTEXT: PL/Python function "test1n" |
||||
test1n |
||||
-------- |
||||
3 |
||||
(1 row) |
||||
|
||||
CREATE FUNCTION test2() RETURNS ltree |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE ltree |
||||
AS $$ |
||||
return ['foo', 'bar', 'baz'] |
||||
$$; |
||||
-- plpython to ltree is not yet implemented, so this will fail, |
||||
-- because it will try to parse the Python list as an ltree input |
||||
-- string. |
||||
SELECT test2(); |
||||
ERROR: syntax error at position 0 |
||||
CONTEXT: while creating return value |
||||
PL/Python function "test2" |
@ -0,0 +1,32 @@ |
||||
#include "postgres.h" |
||||
#include "fmgr.h" |
||||
#include "plpython.h" |
||||
#include "ltree.h" |
||||
|
||||
PG_MODULE_MAGIC; |
||||
|
||||
|
||||
PG_FUNCTION_INFO_V1(ltree_to_plpython); |
||||
Datum ltree_to_plpython(PG_FUNCTION_ARGS); |
||||
|
||||
Datum |
||||
ltree_to_plpython(PG_FUNCTION_ARGS) |
||||
{ |
||||
ltree *in = PG_GETARG_LTREE(0); |
||||
int i; |
||||
PyObject *list; |
||||
ltree_level *curlevel; |
||||
|
||||
list = PyList_New(in->numlevel); |
||||
|
||||
curlevel = LTREE_FIRST(in); |
||||
for (i = 0; i < in->numlevel; i++) |
||||
{ |
||||
PyList_SetItem(list, i, PyString_FromStringAndSize(curlevel->name, curlevel->len)); |
||||
curlevel = LEVEL_NEXT(curlevel); |
||||
} |
||||
|
||||
PG_FREE_IF_COPY(in, 0); |
||||
|
||||
return PointerGetDatum(list); |
||||
} |
@ -0,0 +1,12 @@ |
||||
-- make sure the prerequisite libraries are loaded |
||||
DO '1' LANGUAGE plpython2u; |
||||
SELECT NULL::ltree; |
||||
|
||||
|
||||
CREATE FUNCTION ltree_to_plpython2(val internal) RETURNS internal |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS 'MODULE_PATHNAME', 'ltree_to_plpython'; |
||||
|
||||
CREATE TRANSFORM FOR ltree LANGUAGE plpython2u ( |
||||
FROM SQL WITH FUNCTION ltree_to_plpython2(internal) |
||||
); |
@ -0,0 +1,6 @@ |
||||
# ltree_plpython2u extension |
||||
comment = 'transform between ltree and plpython2u' |
||||
default_version = '1.0' |
||||
module_pathname = '$libdir/ltree_plpython2' |
||||
relocatable = true |
||||
requires = 'ltree,plpython2u' |
@ -0,0 +1,12 @@ |
||||
-- make sure the prerequisite libraries are loaded |
||||
DO '1' LANGUAGE plpython3u; |
||||
SELECT NULL::ltree; |
||||
|
||||
|
||||
CREATE FUNCTION ltree_to_plpython3(val internal) RETURNS internal |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS 'MODULE_PATHNAME', 'ltree_to_plpython'; |
||||
|
||||
CREATE TRANSFORM FOR ltree LANGUAGE plpython3u ( |
||||
FROM SQL WITH FUNCTION ltree_to_plpython3(internal) |
||||
); |
@ -0,0 +1,6 @@ |
||||
# ltree_plpython3u extension |
||||
comment = 'transform between ltree and plpython3u' |
||||
default_version = '1.0' |
||||
module_pathname = '$libdir/ltree_plpython3' |
||||
relocatable = true |
||||
requires = 'ltree,plpython3u' |
@ -0,0 +1,12 @@ |
||||
-- make sure the prerequisite libraries are loaded |
||||
DO '1' LANGUAGE plpythonu; |
||||
SELECT NULL::ltree; |
||||
|
||||
|
||||
CREATE FUNCTION ltree_to_plpython(val internal) RETURNS internal |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS 'MODULE_PATHNAME'; |
||||
|
||||
CREATE TRANSFORM FOR ltree LANGUAGE plpythonu ( |
||||
FROM SQL WITH FUNCTION ltree_to_plpython(internal) |
||||
); |
@ -0,0 +1,6 @@ |
||||
# ltree_plpythonu extension |
||||
comment = 'transform between ltree and plpythonu' |
||||
default_version = '1.0' |
||||
module_pathname = '$libdir/ltree_plpython2' |
||||
relocatable = true |
||||
requires = 'ltree,plpythonu' |
@ -0,0 +1,37 @@ |
||||
CREATE EXTENSION plpython2u; |
||||
CREATE EXTENSION ltree_plpython2u; |
||||
|
||||
|
||||
CREATE FUNCTION test1(val ltree) RETURNS int |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE ltree |
||||
AS $$ |
||||
plpy.info(repr(val)) |
||||
return len(val) |
||||
$$; |
||||
|
||||
SELECT test1('aa.bb.cc'::ltree); |
||||
|
||||
|
||||
CREATE FUNCTION test1n(val ltree) RETURNS int |
||||
LANGUAGE plpython2u |
||||
TRANSFORM FOR TYPE ltree |
||||
AS $$ |
||||
plpy.info(repr(val)) |
||||
return len(val) |
||||
$$; |
||||
|
||||
SELECT test1n('aa.bb.cc'::ltree); |
||||
|
||||
|
||||
CREATE FUNCTION test2() RETURNS ltree |
||||
LANGUAGE plpythonu |
||||
TRANSFORM FOR TYPE ltree |
||||
AS $$ |
||||
return ['foo', 'bar', 'baz'] |
||||
$$; |
||||
|
||||
-- plpython to ltree is not yet implemented, so this will fail, |
||||
-- because it will try to parse the Python list as an ltree input |
||||
-- string. |
||||
SELECT test2(); |
@ -0,0 +1,207 @@ |
||||
<!-- doc/src/sgml/ref/create_transform.sgml --> |
||||
|
||||
<refentry id="SQL-CREATETRANSFORM"> |
||||
<indexterm zone="sql-createtransform"> |
||||
<primary>CREATE TRANSFORM</primary> |
||||
</indexterm> |
||||
|
||||
<refmeta> |
||||
<refentrytitle>CREATE TRANSFORM</refentrytitle> |
||||
<manvolnum>7</manvolnum> |
||||
<refmiscinfo>SQL - Language Statements</refmiscinfo> |
||||
</refmeta> |
||||
|
||||
<refnamediv> |
||||
<refname>CREATE TRANSFORM</refname> |
||||
<refpurpose>define a new transform</refpurpose> |
||||
</refnamediv> |
||||
|
||||
<refsynopsisdiv> |
||||
<synopsis> |
||||
CREATE [ OR REPLACE ] TRANSFORM FOR <replaceable>type_name</replaceable> LANGUAGE <replaceable>lang_name</replaceable> ( |
||||
FROM SQL WITH FUNCTION <replaceable>from_sql_function_name</replaceable> (<replaceable>argument_type</replaceable> [, ...]), |
||||
TO SQL WITH FUNCTION <replaceable>to_sql_function_name</replaceable> (<replaceable>argument_type</replaceable> [, ...]) |
||||
); |
||||
</synopsis> |
||||
</refsynopsisdiv> |
||||
|
||||
<refsect1 id="sql-createtransform-description"> |
||||
<title>Description</title> |
||||
|
||||
<para> |
||||
<command>CREATE TRANSFORM</command> defines a new transform. |
||||
<command>CREATE OR REPLACE TRANSFORM</command> will either create a new |
||||
transform, or replace an existing definition. |
||||
</para> |
||||
|
||||
<para> |
||||
A transform specifies how to adapt a data type to a procedural language. |
||||
For example, when writing a function in PL/Python using |
||||
the <type>hstore</type> type, PL/Python has no prior knowledge how to |
||||
present <type>hstore</type> values in the Python environment. Language |
||||
implementations usually default to using the text representation, but that |
||||
is inconvenient when, for example, an associative array or a list would be |
||||
more appropriate. |
||||
</para> |
||||
|
||||
<para> |
||||
A transform specifies two functions: |
||||
<itemizedlist> |
||||
<listitem> |
||||
<para> |
||||
A <quote>from SQL</quote> function that converts the type from the SQL |
||||
environment to the language. This function will be invoked on the |
||||
arguments of a function written in the language. |
||||
</para> |
||||
</listitem> |
||||
|
||||
<listitem> |
||||
<para> |
||||
A <quote>to SQL</quote> function that converts the type from the |
||||
language to the SQL environment. This function will be invoked on the |
||||
return value of a function written in the language. |
||||
</para> |
||||
</listitem> |
||||
</itemizedlist> |
||||
It is not necessary to provide both of these functions. If one is not |
||||
specified, the language-specific default behavior will be used if |
||||
necessary. (To prevent a transformation in a certain direction from |
||||
happening at all, you could also write a transform function that always |
||||
errors out.) |
||||
</para> |
||||
|
||||
<para> |
||||
To be able to create a transform, you must own and |
||||
have <literal>USAGE</literal> privilege on the type, have |
||||
<literal>USAGE</literal> privilege on the language, and own and |
||||
have <literal>EXECUTE</literal> privilege on the from-SQL and to-SQL |
||||
functions, if specified. |
||||
</para> |
||||
</refsect1> |
||||
|
||||
<refsect1> |
||||
<title>Parameters</title> |
||||
|
||||
<variablelist> |
||||
<varlistentry> |
||||
<term><replaceable>type_name</replaceable></term> |
||||
|
||||
<listitem> |
||||
<para> |
||||
The name of the data type of the transform. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
|
||||
<varlistentry> |
||||
<term><replaceable>lang_name</replaceable></term> |
||||
|
||||
<listitem> |
||||
<para> |
||||
The name of the language of the transform. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
|
||||
<varlistentry> |
||||
<term><replaceable>from_sql_function_name</replaceable>(<replaceable>argument_type</replaceable> [, ...])</term> |
||||
|
||||
<listitem> |
||||
<para> |
||||
The name of the function for converting the type from the SQL |
||||
environment to the language. It must take one argument of |
||||
type <type>internal</type> and return type <type>internal</type>. The |
||||
actual argument will be of the type for the transform, and the function |
||||
should be coded as if it were. (But it is not allowed to declare an |
||||
SQL-level function function returning <type>internal</type> without at |
||||
least one argument of type <type>internal</type>.) The actual return |
||||
value will be something specific to the language implementation. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
|
||||
<varlistentry> |
||||
<term><replaceable>to_sql_function_name</replaceable>(<replaceable>argument_type</replaceable> [, ...])</term> |
||||
|
||||
<listitem> |
||||
<para> |
||||
The name of the function for converting the type from the language to |
||||
the SQL environment. It must take one argument of type |
||||
<type>internal</type> and return the type that is the type for the |
||||
transform. The actual argument value will be something specific to the |
||||
language implementation. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
</variablelist> |
||||
</refsect1> |
||||
|
||||
<refsect1 id="sql-createtransform-notes"> |
||||
<title>Notes</title> |
||||
|
||||
<para> |
||||
Use <xref linkend="sql-droptransform"> to remove transforms. |
||||
</para> |
||||
</refsect1> |
||||
|
||||
<refsect1 id="sql-createtransform-examples"> |
||||
<title>Examples</title> |
||||
|
||||
<para> |
||||
To create a transform for type <type>hstore</type> and language |
||||
<literal>plpythonu</literal>, first set up the type and the language: |
||||
<programlisting> |
||||
CREATE TYPE hstore ...; |
||||
|
||||
CREATE LANGUAGE plpythonu ...; |
||||
</programlisting> |
||||
Then create the necessary functions: |
||||
<programlisting> |
||||
CREATE FUNCTION hstore_to_plpython(val internal) RETURNS internal |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS ...; |
||||
|
||||
CREATE FUNCTION plpython_to_hstore(val internal) RETURNS hstore |
||||
LANGUAGE C STRICT IMMUTABLE |
||||
AS ...; |
||||
</programlisting> |
||||
And finally create the transform to connect them all together: |
||||
<programlisting> |
||||
CREATE TRANSFORM FOR hstore LANGUAGE plpythonu ( |
||||
FROM SQL WITH FUNCTION hstore_to_plpython(internal), |
||||
TO SQL WITH FUNCTION plpython_to_hstore(internal) |
||||
); |
||||
</programlisting> |
||||
In practice, these commands would be wrapped up in extensions. |
||||
</para> |
||||
|
||||
<para> |
||||
The <filename>contrib</filename> section contains a number of extensions |
||||
that provide transforms, which can serve as real-world examples. |
||||
</para> |
||||
</refsect1> |
||||
|
||||
<refsect1 id="sql-createtransform-compat"> |
||||
<title>Compatibility</title> |
||||
|
||||
<para> |
||||
This form of <command>CREATE TRANSFORM</command> is a |
||||
<productname>PostgreSQL</productname> extension. There is a <command>CREATE |
||||
TRANSFORM</command> command in the <acronym>SQL</acronym> standard, but it |
||||
is for adapting data types to client languages. That usage is not supported |
||||
by <productname>PostgreSQL</productname>. |
||||
</para> |
||||
</refsect1> |
||||
|
||||
<refsect1 id="sql-createtransform-seealso"> |
||||
<title>See Also</title> |
||||
|
||||
<para> |
||||
<xref linkend="sql-createfunction">, |
||||
<xref linkend="sql-createlanguage">, |
||||
<xref linkend="sql-createtype">, |
||||
<xref linkend="sql-droptransform"> |
||||
</para> |
||||
</refsect1> |
||||
|
||||
</refentry> |
@ -0,0 +1,123 @@ |
||||
<!-- doc/src/sgml/ref/drop_transform.sgml --> |
||||
|
||||
<refentry id="SQL-DROPTRANSFORM"> |
||||
<indexterm zone="sql-droptransform"> |
||||
<primary>DROP TRANSFORM</primary> |
||||
</indexterm> |
||||
|
||||
<refmeta> |
||||
<refentrytitle>DROP TRANSFORM</refentrytitle> |
||||
<manvolnum>7</manvolnum> |
||||
<refmiscinfo>SQL - Language Statements</refmiscinfo> |
||||
</refmeta> |
||||
|
||||
<refnamediv> |
||||
<refname>DROP TRANSFORM</refname> |
||||
<refpurpose>remove a transform</refpurpose> |
||||
</refnamediv> |
||||
|
||||
<refsynopsisdiv> |
||||
<synopsis> |
||||
DROP TRANSFORM [ IF EXISTS ] FOR <replaceable>type_name</replaceable> LANGUAGE <replaceable>lang_name</replaceable> |
||||
</synopsis> |
||||
</refsynopsisdiv> |
||||
|
||||
<refsect1 id="sql-droptransform-description"> |
||||
<title>Description</title> |
||||
|
||||
<para> |
||||
<command>DROP TRANSFORM</command> removes a previously defined transform. |
||||
</para> |
||||
|
||||
<para> |
||||
To be able to drop a transform, you must own the type and the language. |
||||
These are the same privileges that are required to create a transform. |
||||
</para> |
||||
</refsect1> |
||||
|
||||
<refsect1> |
||||
<title>Parameters</title> |
||||
|
||||
<variablelist> |
||||
|
||||
<varlistentry> |
||||
<term><literal>IF EXISTS</literal></term> |
||||
<listitem> |
||||
<para> |
||||
Do not throw an error if the transform does not exist. A notice is issued |
||||
in this case. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
|
||||
<varlistentry> |
||||
<term><replaceable>type_name</replaceable></term> |
||||
|
||||
<listitem> |
||||
<para> |
||||
The name of the data type of the transform. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
|
||||
<varlistentry> |
||||
<term><replaceable>lang_name</replaceable></term> |
||||
|
||||
<listitem> |
||||
<para> |
||||
The name of the language of the transform. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
|
||||
<varlistentry> |
||||
<term><literal>CASCADE</literal></term> |
||||
<listitem> |
||||
<para> |
||||
Automatically drop objects that depend on the transform. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
|
||||
<varlistentry> |
||||
<term><literal>RESTRICT</literal></term> |
||||
<listitem> |
||||
<para> |
||||
Refuse to drop the transform if any objects depend on it. This is the |
||||
default. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
</variablelist> |
||||
</refsect1> |
||||
|
||||
<refsect1 id="sql-droptransform-examples"> |
||||
<title>Examples</title> |
||||
|
||||
<para> |
||||
To drop the transform for type <type>hstore</type> and language |
||||
<literal>plpythonu</literal>: |
||||
<programlisting> |
||||
DROP TRANSFORM FOR hstore LANGUAGE plpythonu; |
||||
</programlisting></para> |
||||
</refsect1> |
||||
|
||||
<refsect1 id="sql-droptransform-compat"> |
||||
<title>Compatibility</title> |
||||
|
||||
<para> |
||||
This form of <command>DROP TRANSFORM</command> is a |
||||
<productname>PostgreSQL</productname> extension. See <xref |
||||
linkend="sql-createtransform"> for details. |
||||
</para> |
||||
</refsect1> |
||||
|
||||
<refsect1> |
||||
<title>See Also</title> |
||||
|
||||
<simplelist type="inline"> |
||||
<member><xref linkend="sql-createtransform"></member> |
||||
</simplelist> |
||||
</refsect1> |
||||
|
||||
</refentry> |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,47 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* pg_transform.h |
||||
* |
||||
* Copyright (c) 2012-2015, PostgreSQL Global Development Group |
||||
* |
||||
* src/include/catalog/pg_transform.h |
||||
* |
||||
* NOTES |
||||
* the genbki.pl script reads this file and generates .bki |
||||
* information from the DATA() statements. |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#ifndef PG_TRANSFORM_H |
||||
#define PG_TRANSFORM_H |
||||
|
||||
#include "catalog/genbki.h" |
||||
|
||||
/* ----------------
|
||||
* pg_transform definition. cpp turns this into |
||||
* typedef struct FormData_pg_transform |
||||
* ---------------- |
||||
*/ |
||||
#define TransformRelationId 3576 |
||||
|
||||
CATALOG(pg_transform,3576) |
||||
{ |
||||
Oid trftype; |
||||
Oid trflang; |
||||
regproc trffromsql; |
||||
regproc trftosql; |
||||
} FormData_pg_transform; |
||||
|
||||
typedef FormData_pg_transform *Form_pg_transform; |
||||
|
||||
/* ----------------
|
||||
* compiler constants for pg_transform |
||||
* ---------------- |
||||
*/ |
||||
#define Natts_pg_transform 4 |
||||
#define Anum_pg_transform_trftype 1 |
||||
#define Anum_pg_transform_trflang 2 |
||||
#define Anum_pg_transform_trffromsql 3 |
||||
#define Anum_pg_transform_trftosql 4 |
||||
|
||||
#endif /* PG_TRANSFORM_H */ |
@ -0,0 +1,35 @@ |
||||
ifeq ($(python_majorversion),3) |
||||
# Adjust regression tests for Python 3 compatibility
|
||||
#
|
||||
# Mention those regression test files that need to be mangled in the
|
||||
# variable REGRESS_PLPYTHON3_MANGLE. They will be copied to a
|
||||
# subdirectory python3/ and have their Python syntax and other bits
|
||||
# adjusted to work with Python 3.
|
||||
|
||||
# Note that the order of the tests needs to be preserved in this
|
||||
# expression.
|
||||
REGRESS := $(foreach test,$(REGRESS),$(if $(filter $(test),$(REGRESS_PLPYTHON3_MANGLE)),python3/$(test),$(test)))
|
||||
|
||||
.PHONY: pgregress-python3-mangle |
||||
pgregress-python3-mangle: |
||||
$(MKDIR_P) sql/python3 expected/python3 results/python3
|
||||
for file in $(patsubst %,$(srcdir)/sql/%.sql,$(REGRESS_PLPYTHON3_MANGLE)) $(patsubst %,$(srcdir)/expected/%*.out,$(REGRESS_PLPYTHON3_MANGLE)); do \
|
||||
sed -e 's/except \([[:alpha:]][[:alpha:].]*\), *\([[:alpha:]][[:alpha:]]*\):/except \1 as \2:/g' \
|
||||
-e "s/<type 'exceptions\.\([[:alpha:]]*\)'>/<class '\1'>/g" \
|
||||
-e "s/<type 'long'>/<class 'int'>/g" \
|
||||
-e "s/\([0-9][0-9]*\)L/\1/g" \
|
||||
-e 's/\([ [{]\)u"/\1"/g' \
|
||||
-e "s/\([ [{]\)u'/\1'/g" \
|
||||
-e "s/def next/def __next__/g" \
|
||||
-e "s/LANGUAGE plpythonu/LANGUAGE plpython3u/g" \
|
||||
-e "s/LANGUAGE plpython2u/LANGUAGE plpython3u/g" \
|
||||
-e "s/EXTENSION \([^ ]*_\)*plpythonu/EXTENSION \1plpython3u/g" \
|
||||
-e "s/EXTENSION \([^ ]*_\)*plpython2u/EXTENSION \1plpython3u/g" \
|
||||
$$file >`echo $$file | sed 's,^.*/\([^/][^/]*/\)\([^/][^/]*\)$$,\1python3/\2,'` || exit; \
|
||||
done
|
||||
|
||||
check installcheck: pgregress-python3-mangle |
||||
|
||||
pg_regress_clean_files += sql/python3/ expected/python3/ results/python3/
|
||||
|
||||
endif # Python 3
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue