On 04/02/11 17:19, Hitoshi Harada wrote:
> 2011/1/28 Jan Urbański <[email protected]>:
>> On 23/12/10 15:15, Jan Urbański wrote:
>>> Here's a patch implementing custom parsers for data types mentioned in
>>> http://archives.postgresql.org/pgsql-hackers/2010-12/msg01991.php. It's
>>> an incremental patch on top of the plpython-refactor patch sent eariler.
>>
>> Updated to master.
>
> I reviewed this for some time today.
Thank you.
> The patch applies with hunks, compiles and tests are passed, though it
> looks like not having additional test along with it.
I added a simple test. I had to add an expected file for the case when
hstore is compiled without PL/Python integration.
> - in hstore_plpython.c,
> PLyParsers parsers = {
> .in = hstore_to_dict,
> .out = dict_to_hstore
> };
> I'm not sure if this coding style is used anywhere in the core.
> Isn't this the C99 style?
Ooops, you're right. Fixed.
> - You need define custom variable class to use this feature.
> plpython.hstore = 'public.hstore'. I wonder why it's called
> plpython[u].hstore = 'public.hstore' (with 'u') because the language
> is called "plpythonu".
I think plpython.hstore was what showed up in discussion... I'd be fine
with calling the variable plpythonu.hstore, if that's the consensus.
> - typo in plpython.h,
> Types for parsres functions that ...
Fixed.
> - I tried the sample you mention upthread,
> regression=# select pick_one('a=>3, b=>4', 'b');
> ERROR: TypeError: string indices must be integers
> CONTEXT: PL/Python function "pick_one"
>
> My python is 2.4.3 again.
Hm, this means that the hstore has not been transformed into a Python
dict, but into a string, which is what happens if you *don't* have
plpython hstore integration enabled. I think that was because of an
issue with my changes to hstore's Makefile, that made it compile without
Python support, even if the sources were configured with --with-python.
There's also a gotcha: if you set plpython.hstore to 'public.hstore',
you will have to DROP (or CREATE OR REPLACE again) all functions that
accept or return hstores, because their I/O routines are already cached.
Not sure how big of a problem that is (or how to fix it in an elegant
manner). Making the parameter PGC_POSTMASTER is an easy solution... but
not very nice.
> That's it for now. It is an exciting feature and plpython will be the
> first language to think of when you're building "object database" if
> this feature is in. The design here will affect following pl/perl and
> other so it is important enough to discuss.
Yes, I ended up writing this patch as a PoC of how you can integrate
procedural languages with arbitrary addon modules, so it would be good
to have a discussion about the general mechanisms. I'm aware that this
discussion, and subsequently this patch, might be punted to 9.2
(although that would be a shame).
Cheers,
Jan
diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile
index 1d533fd..fd85052 100644
*** a/contrib/hstore/Makefile
--- b/contrib/hstore/Makefile
***************
*** 1,8 ****
# contrib/hstore/Makefile
MODULE_big = hstore
OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
! crc32.o
DATA_built = hstore.sql
DATA = uninstall_hstore.sql
--- 1,9 ----
# contrib/hstore/Makefile
MODULE_big = hstore
+
OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
! hstore_plpython.o crc32.o
DATA_built = hstore.sql
DATA = uninstall_hstore.sql
*************** top_builddir = ../..
*** 18,20 ****
--- 19,28 ----
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
+
+ ifeq ($(with_python), yes)
+ override CFLAGS += -I$(srcdir) -I$(top_builddir)/src/pl/plpython \
+ $(python_includespec) -DHSTORE_PLPYTHON_SUPPORT
+ SHLIB_LINK = $(python_libspec) $(python_additional_libs) \
+ $(filter -lintl,$(LIBS)) $(CPPFLAGS)
+ endif
diff --git a/contrib/hstore/expected/hstore.out b/contrib/hstore/expected/hstore.out
index 354fff2..049fdd5 100644
*** a/contrib/hstore/expected/hstore.out
--- b/contrib/hstore/expected/hstore.out
*************** select count(*) from testhstore where h
*** 1461,1463 ****
--- 1461,1502 ----
1
(1 row)
+ -- plpython integration
+ set plpython.hstore = 'public.hstore';
+ create language plpythonu;
+ create or replace function select_one(h hstore, idx text) returns text as $$
+ try:
+ return h.get(idx)
+ except AttributeError:
+ # h has not been transformed into a dict and is a string
+ return None
+ $$ language plpythonu;
+ select coalesce(select_one(h, 'node'), '<null>') as node from testhstore where select_one(h, 'line')::integer < 10;
+ node
+ --------
+ AA
+ CBB
+ <null>
+ <null>
+ CBA
+ CBC
+ <null>
+ <null>
+ <null>
+ (9 rows)
+
+ reset plpython.hstore;
+ create or replace function select_one(h hstore, idx text) returns text as $$
+ try:
+ return h.get(idx)
+ except AttributeError:
+ # h has not been transformed into a dict and is a string
+ return None
+ $$ language plpythonu;
+ select coalesce(select_one(h, 'node'), '<null>') as node from testhstore where select_one(h, 'line')::integer < 10;
+ node
+ ------
+ (0 rows)
+
+ drop function select_one(hstore, text);
+ drop language plpythonu;
diff --git a/contrib/hstore/expected/hstore_0.out b/contrib/hstore/expected/hstore_0.out
index ...af06337 .
*** a/contrib/hstore/expected/hstore_0.out
--- b/contrib/hstore/expected/hstore_0.out
***************
*** 0 ****
--- 1,1495 ----
+ --
+ -- first, define the datatype. Turn off echoing so that expected file
+ -- does not depend on contents of hstore.sql.
+ --
+ SET client_min_messages = warning;
+ \set ECHO none
+ psql:hstore.sql:228: WARNING: => is deprecated as an operator name
+ DETAIL: This name may be disallowed altogether in future versions of PostgreSQL.
+ RESET client_min_messages;
+ set escape_string_warning=off;
+ --hstore;
+ select ''::hstore;
+ hstore
+ --------
+
+ (1 row)
+
+ select 'a=>b'::hstore;
+ hstore
+ ----------
+ "a"=>"b"
+ (1 row)
+
+ select ' a=>b'::hstore;
+ hstore
+ ----------
+ "a"=>"b"
+ (1 row)
+
+ select 'a =>b'::hstore;
+ hstore
+ ----------
+ "a"=>"b"
+ (1 row)
+
+ select 'a=>b '::hstore;
+ hstore
+ ----------
+ "a"=>"b"
+ (1 row)
+
+ select 'a=> b'::hstore;
+ hstore
+ ----------
+ "a"=>"b"
+ (1 row)
+
+ select '"a"=>"b"'::hstore;
+ hstore
+ ----------
+ "a"=>"b"
+ (1 row)
+
+ select ' "a"=>"b"'::hstore;
+ hstore
+ ----------
+ "a"=>"b"
+ (1 row)
+
+ select '"a" =>"b"'::hstore;
+ hstore
+ ----------
+ "a"=>"b"
+ (1 row)
+
+ select '"a"=>"b" '::hstore;
+ hstore
+ ----------
+ "a"=>"b"
+ (1 row)
+
+ select '"a"=> "b"'::hstore;
+ hstore
+ ----------
+ "a"=>"b"
+ (1 row)
+
+ select 'aa=>bb'::hstore;
+ hstore
+ ------------
+ "aa"=>"bb"
+ (1 row)
+
+ select ' aa=>bb'::hstore;
+ hstore
+ ------------
+ "aa"=>"bb"
+ (1 row)
+
+ select 'aa =>bb'::hstore;
+ hstore
+ ------------
+ "aa"=>"bb"
+ (1 row)
+
+ select 'aa=>bb '::hstore;
+ hstore
+ ------------
+ "aa"=>"bb"
+ (1 row)
+
+ select 'aa=> bb'::hstore;
+ hstore
+ ------------
+ "aa"=>"bb"
+ (1 row)
+
+ select '"aa"=>"bb"'::hstore;
+ hstore
+ ------------
+ "aa"=>"bb"
+ (1 row)
+
+ select ' "aa"=>"bb"'::hstore;
+ hstore
+ ------------
+ "aa"=>"bb"
+ (1 row)
+
+ select '"aa" =>"bb"'::hstore;
+ hstore
+ ------------
+ "aa"=>"bb"
+ (1 row)
+
+ select '"aa"=>"bb" '::hstore;
+ hstore
+ ------------
+ "aa"=>"bb"
+ (1 row)
+
+ select '"aa"=> "bb"'::hstore;
+ hstore
+ ------------
+ "aa"=>"bb"
+ (1 row)
+
+ select 'aa=>bb, cc=>dd'::hstore;
+ hstore
+ ------------------------
+ "aa"=>"bb", "cc"=>"dd"
+ (1 row)
+
+ select 'aa=>bb , cc=>dd'::hstore;
+ hstore
+ ------------------------
+ "aa"=>"bb", "cc"=>"dd"
+ (1 row)
+
+ select 'aa=>bb ,cc=>dd'::hstore;
+ hstore
+ ------------------------
+ "aa"=>"bb", "cc"=>"dd"
+ (1 row)
+
+ select 'aa=>bb, "cc"=>dd'::hstore;
+ hstore
+ ------------------------
+ "aa"=>"bb", "cc"=>"dd"
+ (1 row)
+
+ select 'aa=>bb , "cc"=>dd'::hstore;
+ hstore
+ ------------------------
+ "aa"=>"bb", "cc"=>"dd"
+ (1 row)
+
+ select 'aa=>bb ,"cc"=>dd'::hstore;
+ hstore
+ ------------------------
+ "aa"=>"bb", "cc"=>"dd"
+ (1 row)
+
+ select 'aa=>"bb", cc=>dd'::hstore;
+ hstore
+ ------------------------
+ "aa"=>"bb", "cc"=>"dd"
+ (1 row)
+
+ select 'aa=>"bb" , cc=>dd'::hstore;
+ hstore
+ ------------------------
+ "aa"=>"bb", "cc"=>"dd"
+ (1 row)
+
+ select 'aa=>"bb" ,cc=>dd'::hstore;
+ hstore
+ ------------------------
+ "aa"=>"bb", "cc"=>"dd"
+ (1 row)
+
+ select 'aa=>null'::hstore;
+ hstore
+ ------------
+ "aa"=>NULL
+ (1 row)
+
+ select 'aa=>NuLl'::hstore;
+ hstore
+ ------------
+ "aa"=>NULL
+ (1 row)
+
+ select 'aa=>"NuLl"'::hstore;
+ hstore
+ --------------
+ "aa"=>"NuLl"
+ (1 row)
+
+ select e'\\=a=>q=w'::hstore;
+ hstore
+ -------------
+ "=a"=>"q=w"
+ (1 row)
+
+ select e'"=a"=>q\\=w'::hstore;
+ hstore
+ -------------
+ "=a"=>"q=w"
+ (1 row)
+
+ select e'"\\"a"=>q>w'::hstore;
+ hstore
+ --------------
+ "\"a"=>"q>w"
+ (1 row)
+
+ select e'\\"a=>q"w'::hstore;
+ hstore
+ ---------------
+ "\"a"=>"q\"w"
+ (1 row)
+
+ select ''::hstore;
+ hstore
+ --------
+
+ (1 row)
+
+ select ' '::hstore;
+ hstore
+ --------
+
+ (1 row)
+
+ -- -> operator
+ select 'aa=>b, c=>d , b=>16'::hstore->'c';
+ ?column?
+ ----------
+ d
+ (1 row)
+
+ select 'aa=>b, c=>d , b=>16'::hstore->'b';
+ ?column?
+ ----------
+ 16
+ (1 row)
+
+ select 'aa=>b, c=>d , b=>16'::hstore->'aa';
+ ?column?
+ ----------
+ b
+ (1 row)
+
+ select ('aa=>b, c=>d , b=>16'::hstore->'gg') is null;
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select ('aa=>NULL, c=>d , b=>16'::hstore->'aa') is null;
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select ('aa=>"NULL", c=>d , b=>16'::hstore->'aa') is null;
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ -- -> array operator
+ select 'aa=>"NULL", c=>d , b=>16'::hstore -> ARRAY['aa','c'];
+ ?column?
+ ------------
+ {"NULL",d}
+ (1 row)
+
+ select 'aa=>"NULL", c=>d , b=>16'::hstore -> ARRAY['c','aa'];
+ ?column?
+ ------------
+ {d,"NULL"}
+ (1 row)
+
+ select 'aa=>NULL, c=>d , b=>16'::hstore -> ARRAY['aa','c',null];
+ ?column?
+ ---------------
+ {NULL,d,NULL}
+ (1 row)
+
+ select 'aa=>1, c=>3, b=>2, d=>4'::hstore -> ARRAY[['b','d'],['aa','c']];
+ ?column?
+ ---------------
+ {{2,4},{1,3}}
+ (1 row)
+
+ -- exists/defined
+ select exist('a=>NULL, b=>qq', 'a');
+ exist
+ -------
+ t
+ (1 row)
+
+ select exist('a=>NULL, b=>qq', 'b');
+ exist
+ -------
+ t
+ (1 row)
+
+ select exist('a=>NULL, b=>qq', 'c');
+ exist
+ -------
+ f
+ (1 row)
+
+ select exist('a=>"NULL", b=>qq', 'a');
+ exist
+ -------
+ t
+ (1 row)
+
+ select defined('a=>NULL, b=>qq', 'a');
+ defined
+ ---------
+ f
+ (1 row)
+
+ select defined('a=>NULL, b=>qq', 'b');
+ defined
+ ---------
+ t
+ (1 row)
+
+ select defined('a=>NULL, b=>qq', 'c');
+ defined
+ ---------
+ f
+ (1 row)
+
+ select defined('a=>"NULL", b=>qq', 'a');
+ defined
+ ---------
+ t
+ (1 row)
+
+ select hstore 'a=>NULL, b=>qq' ? 'a';
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select hstore 'a=>NULL, b=>qq' ? 'b';
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select hstore 'a=>NULL, b=>qq' ? 'c';
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select hstore 'a=>"NULL", b=>qq' ? 'a';
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select hstore 'a=>NULL, b=>qq' ?| ARRAY['a','b'];
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select hstore 'a=>NULL, b=>qq' ?| ARRAY['b','a'];
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select hstore 'a=>NULL, b=>qq' ?| ARRAY['c','a'];
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select hstore 'a=>NULL, b=>qq' ?| ARRAY['c','d'];
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select hstore 'a=>NULL, b=>qq' ?| '{}'::text[];
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select hstore 'a=>NULL, b=>qq' ?& ARRAY['a','b'];
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select hstore 'a=>NULL, b=>qq' ?& ARRAY['b','a'];
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select hstore 'a=>NULL, b=>qq' ?& ARRAY['c','a'];
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select hstore 'a=>NULL, b=>qq' ?& ARRAY['c','d'];
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select hstore 'a=>NULL, b=>qq' ?& '{}'::text[];
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ -- delete
+ select delete('a=>1 , b=>2, c=>3'::hstore, 'a');
+ delete
+ --------------------
+ "b"=>"2", "c"=>"3"
+ (1 row)
+
+ select delete('a=>null , b=>2, c=>3'::hstore, 'a');
+ delete
+ --------------------
+ "b"=>"2", "c"=>"3"
+ (1 row)
+
+ select delete('a=>1 , b=>2, c=>3'::hstore, 'b');
+ delete
+ --------------------
+ "a"=>"1", "c"=>"3"
+ (1 row)
+
+ select delete('a=>1 , b=>2, c=>3'::hstore, 'c');
+ delete
+ --------------------
+ "a"=>"1", "b"=>"2"
+ (1 row)
+
+ select delete('a=>1 , b=>2, c=>3'::hstore, 'd');
+ delete
+ ------------------------------
+ "a"=>"1", "b"=>"2", "c"=>"3"
+ (1 row)
+
+ select 'a=>1 , b=>2, c=>3'::hstore - 'a'::text;
+ ?column?
+ --------------------
+ "b"=>"2", "c"=>"3"
+ (1 row)
+
+ select 'a=>null , b=>2, c=>3'::hstore - 'a'::text;
+ ?column?
+ --------------------
+ "b"=>"2", "c"=>"3"
+ (1 row)
+
+ select 'a=>1 , b=>2, c=>3'::hstore - 'b'::text;
+ ?column?
+ --------------------
+ "a"=>"1", "c"=>"3"
+ (1 row)
+
+ select 'a=>1 , b=>2, c=>3'::hstore - 'c'::text;
+ ?column?
+ --------------------
+ "a"=>"1", "b"=>"2"
+ (1 row)
+
+ select 'a=>1 , b=>2, c=>3'::hstore - 'd'::text;
+ ?column?
+ ------------------------------
+ "a"=>"1", "b"=>"2", "c"=>"3"
+ (1 row)
+
+ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b'::text)
+ = pg_column_size('a=>1, b=>2'::hstore);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ -- delete (array)
+ select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','e']);
+ delete
+ ------------------------------
+ "a"=>"1", "b"=>"2", "c"=>"3"
+ (1 row)
+
+ select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','b']);
+ delete
+ --------------------
+ "a"=>"1", "c"=>"3"
+ (1 row)
+
+ select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['a','c']);
+ delete
+ ----------
+ "b"=>"2"
+ (1 row)
+
+ select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY[['b'],['c'],['a']]);
+ delete
+ --------
+
+ (1 row)
+
+ select delete('a=>1 , b=>2, c=>3'::hstore, '{}'::text[]);
+ delete
+ ------------------------------
+ "a"=>"1", "b"=>"2", "c"=>"3"
+ (1 row)
+
+ select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','e'];
+ ?column?
+ ------------------------------
+ "a"=>"1", "b"=>"2", "c"=>"3"
+ (1 row)
+
+ select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','b'];
+ ?column?
+ --------------------
+ "a"=>"1", "c"=>"3"
+ (1 row)
+
+ select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['a','c'];
+ ?column?
+ ----------
+ "b"=>"2"
+ (1 row)
+
+ select 'a=>1 , b=>2, c=>3'::hstore - ARRAY[['b'],['c'],['a']];
+ ?column?
+ ----------
+
+ (1 row)
+
+ select 'a=>1 , b=>2, c=>3'::hstore - '{}'::text[];
+ ?column?
+ ------------------------------
+ "a"=>"1", "b"=>"2", "c"=>"3"
+ (1 row)
+
+ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - ARRAY['a','c'])
+ = pg_column_size('b=>2'::hstore);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - '{}'::text[])
+ = pg_column_size('a=>1, b=>2, c=>3'::hstore);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ -- delete (hstore)
+ select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>4, b=>2'::hstore);
+ delete
+ ---------------------
+ "c"=>"3", "aa"=>"1"
+ (1 row)
+
+ select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>NULL, c=>3'::hstore);
+ delete
+ ---------------------
+ "b"=>"2", "aa"=>"1"
+ (1 row)
+
+ select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>1, b=>2, c=>3'::hstore);
+ delete
+ --------
+
+ (1 row)
+
+ select delete('aa=>1 , b=>2, c=>3'::hstore, 'b=>2'::hstore);
+ delete
+ ---------------------
+ "c"=>"3", "aa"=>"1"
+ (1 row)
+
+ select delete('aa=>1 , b=>2, c=>3'::hstore, ''::hstore);
+ delete
+ -------------------------------
+ "b"=>"2", "c"=>"3", "aa"=>"1"
+ (1 row)
+
+ select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>4, b=>2'::hstore;
+ ?column?
+ ---------------------
+ "c"=>"3", "aa"=>"1"
+ (1 row)
+
+ select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>NULL, c=>3'::hstore;
+ ?column?
+ ---------------------
+ "b"=>"2", "aa"=>"1"
+ (1 row)
+
+ select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>1, b=>2, c=>3'::hstore;
+ ?column?
+ ----------
+
+ (1 row)
+
+ select 'aa=>1 , b=>2, c=>3'::hstore - 'b=>2'::hstore;
+ ?column?
+ ---------------------
+ "c"=>"3", "aa"=>"1"
+ (1 row)
+
+ select 'aa=>1 , b=>2, c=>3'::hstore - ''::hstore;
+ ?column?
+ -------------------------------
+ "b"=>"2", "c"=>"3", "aa"=>"1"
+ (1 row)
+
+ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b=>2'::hstore)
+ = pg_column_size('a=>1, c=>3'::hstore);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - ''::hstore)
+ = pg_column_size('a=>1, b=>2, c=>3'::hstore);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ -- ||
+ select 'aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>f';
+ ?column?
+ -------------------------------------------
+ "b"=>"g", "aa"=>"1", "cq"=>"l", "fg"=>"f"
+ (1 row)
+
+ select 'aa=>1 , b=>2, cq=>3'::hstore || 'aq=>l';
+ ?column?
+ -------------------------------------------
+ "b"=>"2", "aa"=>"1", "aq"=>"l", "cq"=>"3"
+ (1 row)
+
+ select 'aa=>1 , b=>2, cq=>3'::hstore || 'aa=>l';
+ ?column?
+ --------------------------------
+ "b"=>"2", "aa"=>"l", "cq"=>"3"
+ (1 row)
+
+ select 'aa=>1 , b=>2, cq=>3'::hstore || '';
+ ?column?
+ --------------------------------
+ "b"=>"2", "aa"=>"1", "cq"=>"3"
+ (1 row)
+
+ select ''::hstore || 'cq=>l, b=>g, fg=>f';
+ ?column?
+ --------------------------------
+ "b"=>"g", "cq"=>"l", "fg"=>"f"
+ (1 row)
+
+ select pg_column_size(''::hstore || ''::hstore) = pg_column_size(''::hstore);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select pg_column_size('aa=>1'::hstore || 'b=>2'::hstore)
+ = pg_column_size('aa=>1, b=>2'::hstore);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select pg_column_size('aa=>1, b=>2'::hstore || ''::hstore)
+ = pg_column_size('aa=>1, b=>2'::hstore);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select pg_column_size(''::hstore || 'aa=>1, b=>2'::hstore)
+ = pg_column_size('aa=>1, b=>2'::hstore);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ -- =>
+ select 'a=>g, b=>c'::hstore || ( 'asd'=>'gf' );
+ ?column?
+ ---------------------------------
+ "a"=>"g", "b"=>"c", "asd"=>"gf"
+ (1 row)
+
+ select 'a=>g, b=>c'::hstore || ( 'b'=>'gf' );
+ ?column?
+ ---------------------
+ "a"=>"g", "b"=>"gf"
+ (1 row)
+
+ select 'a=>g, b=>c'::hstore || ( 'b'=>'NULL' );
+ ?column?
+ -----------------------
+ "a"=>"g", "b"=>"NULL"
+ (1 row)
+
+ select 'a=>g, b=>c'::hstore || ( 'b'=>NULL );
+ ?column?
+ ---------------------
+ "a"=>"g", "b"=>NULL
+ (1 row)
+
+ select ('a=>g, b=>c'::hstore || ( NULL=>'b' )) is null;
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select pg_column_size(('b'=>'gf'))
+ = pg_column_size('b=>gf'::hstore);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select pg_column_size('a=>g, b=>c'::hstore || ('b'=>'gf'))
+ = pg_column_size('a=>g, b=>gf'::hstore);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ -- slice()
+ select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']);
+ slice
+ -------
+
+ (1 row)
+
+ select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b']);
+ slice
+ --------------------
+ "b"=>"2", "c"=>"3"
+ (1 row)
+
+ select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['aa','b']);
+ slice
+ ---------------------
+ "b"=>"2", "aa"=>"1"
+ (1 row)
+
+ select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b','aa']);
+ slice
+ -------------------------------
+ "b"=>"2", "c"=>"3", "aa"=>"1"
+ (1 row)
+
+ select pg_column_size(slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b']))
+ = pg_column_size('b=>2, c=>3'::hstore);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select pg_column_size(slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b','aa']))
+ = pg_column_size('aa=>1, b=>2, c=>3'::hstore);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ -- array input
+ select '{}'::text[]::hstore;
+ hstore
+ --------
+
+ (1 row)
+
+ select ARRAY['a','g','b','h','asd']::hstore;
+ ERROR: array must have even number of elements
+ select ARRAY['a','g','b','h','asd','i']::hstore;
+ array
+ --------------------------------
+ "a"=>"g", "b"=>"h", "asd"=>"i"
+ (1 row)
+
+ select ARRAY[['a','g'],['b','h'],['asd','i']]::hstore;
+ array
+ --------------------------------
+ "a"=>"g", "b"=>"h", "asd"=>"i"
+ (1 row)
+
+ select ARRAY[['a','g','b'],['h','asd','i']]::hstore;
+ ERROR: array must have two columns
+ select ARRAY[[['a','g'],['b','h'],['asd','i']]]::hstore;
+ ERROR: wrong number of array subscripts
+ select hstore('{}'::text[]);
+ hstore
+ --------
+
+ (1 row)
+
+ select hstore(ARRAY['a','g','b','h','asd']);
+ ERROR: array must have even number of elements
+ select hstore(ARRAY['a','g','b','h','asd','i']);
+ hstore
+ --------------------------------
+ "a"=>"g", "b"=>"h", "asd"=>"i"
+ (1 row)
+
+ select hstore(ARRAY[['a','g'],['b','h'],['asd','i']]);
+ hstore
+ --------------------------------
+ "a"=>"g", "b"=>"h", "asd"=>"i"
+ (1 row)
+
+ select hstore(ARRAY[['a','g','b'],['h','asd','i']]);
+ ERROR: array must have two columns
+ select hstore(ARRAY[[['a','g'],['b','h'],['asd','i']]]);
+ ERROR: wrong number of array subscripts
+ select hstore('[0:5]={a,g,b,h,asd,i}'::text[]);
+ hstore
+ --------------------------------
+ "a"=>"g", "b"=>"h", "asd"=>"i"
+ (1 row)
+
+ select hstore('[0:2][1:2]={{a,g},{b,h},{asd,i}}'::text[]);
+ hstore
+ --------------------------------
+ "a"=>"g", "b"=>"h", "asd"=>"i"
+ (1 row)
+
+ -- pairs of arrays
+ select hstore(ARRAY['a','b','asd'], ARRAY['g','h','i']);
+ hstore
+ --------------------------------
+ "a"=>"g", "b"=>"h", "asd"=>"i"
+ (1 row)
+
+ select hstore(ARRAY['a','b','asd'], ARRAY['g','h',NULL]);
+ hstore
+ ---------------------------------
+ "a"=>"g", "b"=>"h", "asd"=>NULL
+ (1 row)
+
+ select hstore(ARRAY['z','y','x'], ARRAY['1','2','3']);
+ hstore
+ ------------------------------
+ "x"=>"3", "y"=>"2", "z"=>"1"
+ (1 row)
+
+ select hstore(ARRAY['aaa','bb','c','d'], ARRAY[null::text,null,null,null]);
+ hstore
+ -----------------------------------------------
+ "c"=>NULL, "d"=>NULL, "bb"=>NULL, "aaa"=>NULL
+ (1 row)
+
+ select hstore(ARRAY['aaa','bb','c','d'], null);
+ hstore
+ -----------------------------------------------
+ "c"=>NULL, "d"=>NULL, "bb"=>NULL, "aaa"=>NULL
+ (1 row)
+
+ select quote_literal(hstore('{}'::text[], '{}'::text[]));
+ quote_literal
+ ---------------
+ ''
+ (1 row)
+
+ select quote_literal(hstore('{}'::text[], null));
+ quote_literal
+ ---------------
+ ''
+ (1 row)
+
+ select hstore(ARRAY['a'], '{}'::text[]); -- error
+ ERROR: arrays must have same bounds
+ select hstore('{}'::text[], ARRAY['a']); -- error
+ ERROR: arrays must have same bounds
+ select pg_column_size(hstore(ARRAY['a','b','asd'], ARRAY['g','h','i']))
+ = pg_column_size('a=>g, b=>h, asd=>i'::hstore);
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ -- records
+ select hstore(v) from (values (1, 'foo', 1.2, 3::float8)) v(a,b,c,d);
+ hstore
+ ------------------------------------------------
+ "f1"=>"1", "f2"=>"foo", "f3"=>"1.2", "f4"=>"3"
+ (1 row)
+
+ create domain hstestdom1 as integer not null default 0;
+ create table testhstore0 (a integer, b text, c numeric, d float8);
+ create table testhstore1 (a integer, b text, c numeric, d float8, e hstestdom1);
+ insert into testhstore0 values (1, 'foo', 1.2, 3::float8);
+ insert into testhstore1 values (1, 'foo', 1.2, 3::float8);
+ select hstore(v) from testhstore1 v;
+ hstore
+ ------------------------------------------------------
+ "a"=>"1", "b"=>"foo", "c"=>"1.2", "d"=>"3", "e"=>"0"
+ (1 row)
+
+ select hstore(null::testhstore0);
+ hstore
+ --------------------------------------------
+ "a"=>NULL, "b"=>NULL, "c"=>NULL, "d"=>NULL
+ (1 row)
+
+ select hstore(null::testhstore1);
+ hstore
+ -------------------------------------------------------
+ "a"=>NULL, "b"=>NULL, "c"=>NULL, "d"=>NULL, "e"=>NULL
+ (1 row)
+
+ select pg_column_size(hstore(v))
+ = pg_column_size('a=>1, b=>"foo", c=>"1.2", d=>"3", e=>"0"'::hstore)
+ from testhstore1 v;
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select populate_record(v, ('c' => '3.45')) from testhstore1 v;
+ populate_record
+ ------------------
+ (1,foo,3.45,3,0)
+ (1 row)
+
+ select populate_record(v, ('d' => '3.45')) from testhstore1 v;
+ populate_record
+ --------------------
+ (1,foo,1.2,3.45,0)
+ (1 row)
+
+ select populate_record(v, ('e' => '123')) from testhstore1 v;
+ populate_record
+ -------------------
+ (1,foo,1.2,3,123)
+ (1 row)
+
+ select populate_record(v, ('e' => null)) from testhstore1 v;
+ ERROR: domain hstestdom1 does not allow null values
+ select populate_record(v, ('c' => null)) from testhstore1 v;
+ populate_record
+ -----------------
+ (1,foo,,3,0)
+ (1 row)
+
+ select populate_record(v, ('b' => 'foo') || ('a' => '123')) from testhstore1 v;
+ populate_record
+ -------------------
+ (123,foo,1.2,3,0)
+ (1 row)
+
+ select populate_record(v, ('b' => 'foo') || ('e' => null)) from testhstore0 v;
+ populate_record
+ -----------------
+ (1,foo,1.2,3)
+ (1 row)
+
+ select populate_record(v, ('b' => 'foo') || ('e' => null)) from testhstore1 v;
+ ERROR: domain hstestdom1 does not allow null values
+ select populate_record(v, '') from testhstore0 v;
+ populate_record
+ -----------------
+ (1,foo,1.2,3)
+ (1 row)
+
+ select populate_record(v, '') from testhstore1 v;
+ populate_record
+ -----------------
+ (1,foo,1.2,3,0)
+ (1 row)
+
+ select populate_record(null::testhstore1, ('c' => '3.45') || ('a' => '123'));
+ ERROR: domain hstestdom1 does not allow null values
+ select populate_record(null::testhstore1, ('c' => '3.45') || ('e' => '123'));
+ populate_record
+ -----------------
+ (,,3.45,,123)
+ (1 row)
+
+ select populate_record(null::testhstore0, '');
+ populate_record
+ -----------------
+ (,,,)
+ (1 row)
+
+ select populate_record(null::testhstore1, '');
+ ERROR: domain hstestdom1 does not allow null values
+ select v #= ('c' => '3.45') from testhstore1 v;
+ ?column?
+ ------------------
+ (1,foo,3.45,3,0)
+ (1 row)
+
+ select v #= ('d' => '3.45') from testhstore1 v;
+ ?column?
+ --------------------
+ (1,foo,1.2,3.45,0)
+ (1 row)
+
+ select v #= ('e' => '123') from testhstore1 v;
+ ?column?
+ -------------------
+ (1,foo,1.2,3,123)
+ (1 row)
+
+ select v #= ('c' => null) from testhstore1 v;
+ ?column?
+ --------------
+ (1,foo,,3,0)
+ (1 row)
+
+ select v #= ('e' => null) from testhstore0 v;
+ ?column?
+ ---------------
+ (1,foo,1.2,3)
+ (1 row)
+
+ select v #= ('e' => null) from testhstore1 v;
+ ERROR: domain hstestdom1 does not allow null values
+ select v #= (('b' => 'foo') || ('a' => '123')) from testhstore1 v;
+ ?column?
+ -------------------
+ (123,foo,1.2,3,0)
+ (1 row)
+
+ select v #= (('b' => 'foo') || ('e' => '123')) from testhstore1 v;
+ ?column?
+ -------------------
+ (1,foo,1.2,3,123)
+ (1 row)
+
+ select v #= hstore '' from testhstore0 v;
+ ?column?
+ ---------------
+ (1,foo,1.2,3)
+ (1 row)
+
+ select v #= hstore '' from testhstore1 v;
+ ?column?
+ -----------------
+ (1,foo,1.2,3,0)
+ (1 row)
+
+ select null::testhstore1 #= (('c' => '3.45') || ('a' => '123'));
+ ERROR: domain hstestdom1 does not allow null values
+ select null::testhstore1 #= (('c' => '3.45') || ('e' => '123'));
+ ?column?
+ ---------------
+ (,,3.45,,123)
+ (1 row)
+
+ select null::testhstore0 #= hstore '';
+ ?column?
+ ----------
+ (,,,)
+ (1 row)
+
+ select null::testhstore1 #= hstore '';
+ ERROR: domain hstestdom1 does not allow null values
+ select v #= h from testhstore1 v, (values (hstore 'a=>123',1),('b=>foo,c=>3.21',2),('a=>null',3),('e=>123',4),('f=>blah',5)) x(h,i) order by i;
+ ?column?
+ -------------------
+ (123,foo,1.2,3,0)
+ (1,foo,3.21,3,0)
+ (,foo,1.2,3,0)
+ (1,foo,1.2,3,123)
+ (1,foo,1.2,3,0)
+ (5 rows)
+
+ -- keys/values
+ select akeys('aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>f');
+ akeys
+ --------------
+ {b,aa,cq,fg}
+ (1 row)
+
+ select akeys('""=>1');
+ akeys
+ -------
+ {""}
+ (1 row)
+
+ select akeys('');
+ akeys
+ -------
+ {}
+ (1 row)
+
+ select avals('aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>f');
+ avals
+ -----------
+ {g,1,l,f}
+ (1 row)
+
+ select avals('aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>NULL');
+ avals
+ --------------
+ {g,1,l,NULL}
+ (1 row)
+
+ select avals('""=>1');
+ avals
+ -------
+ {1}
+ (1 row)
+
+ select avals('');
+ avals
+ -------
+ {}
+ (1 row)
+
+ select hstore_to_array('aa=>1, cq=>l, b=>g, fg=>NULL'::hstore);
+ hstore_to_array
+ -------------------------
+ {b,g,aa,1,cq,l,fg,NULL}
+ (1 row)
+
+ select %% 'aa=>1, cq=>l, b=>g, fg=>NULL';
+ ?column?
+ -------------------------
+ {b,g,aa,1,cq,l,fg,NULL}
+ (1 row)
+
+ select hstore_to_matrix('aa=>1, cq=>l, b=>g, fg=>NULL'::hstore);
+ hstore_to_matrix
+ ---------------------------------
+ {{b,g},{aa,1},{cq,l},{fg,NULL}}
+ (1 row)
+
+ select %# 'aa=>1, cq=>l, b=>g, fg=>NULL';
+ ?column?
+ ---------------------------------
+ {{b,g},{aa,1},{cq,l},{fg,NULL}}
+ (1 row)
+
+ select * from skeys('aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>f');
+ skeys
+ -------
+ b
+ aa
+ cq
+ fg
+ (4 rows)
+
+ select * from skeys('""=>1');
+ skeys
+ -------
+
+ (1 row)
+
+ select * from skeys('');
+ skeys
+ -------
+ (0 rows)
+
+ select * from svals('aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>f');
+ svals
+ -------
+ g
+ 1
+ l
+ f
+ (4 rows)
+
+ select *, svals is null from svals('aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>NULL');
+ svals | ?column?
+ -------+----------
+ g | f
+ 1 | f
+ l | f
+ | t
+ (4 rows)
+
+ select * from svals('""=>1');
+ svals
+ -------
+ 1
+ (1 row)
+
+ select * from svals('');
+ svals
+ -------
+ (0 rows)
+
+ select * from each('aaa=>bq, b=>NULL, ""=>1 ');
+ key | value
+ -----+-------
+ | 1
+ b |
+ aaa | bq
+ (3 rows)
+
+ -- @>
+ select 'a=>b, b=>1, c=>NULL'::hstore @> 'a=>b';
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select 'a=>b, b=>1, c=>NULL'::hstore @> 'a=>b, c=>NULL';
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select 'a=>b, b=>1, c=>NULL'::hstore @> 'a=>b, g=>NULL';
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select 'a=>b, b=>1, c=>NULL'::hstore @> 'g=>NULL';
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select 'a=>b, b=>1, c=>NULL'::hstore @> 'a=>c';
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ select 'a=>b, b=>1, c=>NULL'::hstore @> 'a=>b';
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ select 'a=>b, b=>1, c=>NULL'::hstore @> 'a=>b, c=>q';
+ ?column?
+ ----------
+ f
+ (1 row)
+
+ CREATE TABLE testhstore (h hstore);
+ \copy testhstore from 'data/hstore.data'
+ select count(*) from testhstore where h @> 'wait=>NULL';
+ count
+ -------
+ 1
+ (1 row)
+
+ select count(*) from testhstore where h @> 'wait=>CC';
+ count
+ -------
+ 15
+ (1 row)
+
+ select count(*) from testhstore where h @> 'wait=>CC, public=>t';
+ count
+ -------
+ 2
+ (1 row)
+
+ select count(*) from testhstore where h ? 'public';
+ count
+ -------
+ 194
+ (1 row)
+
+ select count(*) from testhstore where h ?| ARRAY['public','disabled'];
+ count
+ -------
+ 337
+ (1 row)
+
+ select count(*) from testhstore where h ?& ARRAY['public','disabled'];
+ count
+ -------
+ 42
+ (1 row)
+
+ create index hidx on testhstore using gist(h);
+ set enable_seqscan=off;
+ select count(*) from testhstore where h @> 'wait=>NULL';
+ count
+ -------
+ 1
+ (1 row)
+
+ select count(*) from testhstore where h @> 'wait=>CC';
+ count
+ -------
+ 15
+ (1 row)
+
+ select count(*) from testhstore where h @> 'wait=>CC, public=>t';
+ count
+ -------
+ 2
+ (1 row)
+
+ select count(*) from testhstore where h ? 'public';
+ count
+ -------
+ 194
+ (1 row)
+
+ select count(*) from testhstore where h ?| ARRAY['public','disabled'];
+ count
+ -------
+ 337
+ (1 row)
+
+ select count(*) from testhstore where h ?& ARRAY['public','disabled'];
+ count
+ -------
+ 42
+ (1 row)
+
+ drop index hidx;
+ create index hidx on testhstore using gin (h);
+ set enable_seqscan=off;
+ select count(*) from testhstore where h @> 'wait=>NULL';
+ count
+ -------
+ 1
+ (1 row)
+
+ select count(*) from testhstore where h @> 'wait=>CC';
+ count
+ -------
+ 15
+ (1 row)
+
+ select count(*) from testhstore where h @> 'wait=>CC, public=>t';
+ count
+ -------
+ 2
+ (1 row)
+
+ select count(*) from testhstore where h ? 'public';
+ count
+ -------
+ 194
+ (1 row)
+
+ select count(*) from testhstore where h ?| ARRAY['public','disabled'];
+ count
+ -------
+ 337
+ (1 row)
+
+ select count(*) from testhstore where h ?& ARRAY['public','disabled'];
+ count
+ -------
+ 42
+ (1 row)
+
+ select count(*) from (select (each(h)).key from testhstore) as wow ;
+ count
+ -------
+ 4781
+ (1 row)
+
+ select key, count(*) from (select (each(h)).key from testhstore) as wow group by key order by count desc, key;
+ key | count
+ -----------+-------
+ line | 884
+ query | 207
+ pos | 203
+ node | 202
+ space | 197
+ status | 195
+ public | 194
+ title | 190
+ wait | 190
+ org | 189
+ user | 189
+ coauthors | 188
+ disabled | 185
+ indexed | 184
+ cleaned | 180
+ bad | 179
+ date | 179
+ world | 176
+ state | 172
+ subtitle | 169
+ auth | 168
+ abstract | 161
+ (22 rows)
+
+ -- sort/hash
+ select count(distinct h) from testhstore;
+ count
+ -------
+ 885
+ (1 row)
+
+ set enable_hashagg = false;
+ select count(*) from (select h from (select * from testhstore union all select * from testhstore) hs group by h) hs2;
+ count
+ -------
+ 885
+ (1 row)
+
+ set enable_hashagg = true;
+ set enable_sort = false;
+ select count(*) from (select h from (select * from testhstore union all select * from testhstore) hs group by h) hs2;
+ count
+ -------
+ 885
+ (1 row)
+
+ select distinct * from (values (hstore '' || ''),('')) v(h);
+ h
+ ---
+
+ (1 row)
+
+ set enable_sort = true;
+ -- btree
+ drop index hidx;
+ create index hidx on testhstore using btree (h);
+ set enable_seqscan=off;
+ select count(*) from testhstore where h #># 'p=>1';
+ count
+ -------
+ 125
+ (1 row)
+
+ select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t';
+ count
+ -------
+ 1
+ (1 row)
+
+ -- plpython integration
+ set plpython.hstore = 'public.hstore';
+ ERROR: unrecognized configuration parameter "plpython.hstore"
+ create language plpythonu;
+ create or replace function select_one(h hstore, idx text) returns text as $$
+ try:
+ return h.get(idx)
+ except AttributeError:
+ # h has not been transformed into a dict and is a string
+ return None
+ $$ language plpythonu;
+ select coalesce(select_one(h, 'node'), '<null>') as node from testhstore where select_one(h, 'line')::integer < 10;
+ node
+ ------
+ (0 rows)
+
+ reset plpython.hstore;
+ ERROR: unrecognized configuration parameter "plpython.hstore"
+ create or replace function select_one(h hstore, idx text) returns text as $$
+ try:
+ return h.get(idx)
+ except AttributeError:
+ # h has not been transformed into a dict and is a string
+ return None
+ $$ language plpythonu;
+ select coalesce(select_one(h, 'node'), '<null>') as node from testhstore where select_one(h, 'line')::integer < 10;
+ node
+ ------
+ (0 rows)
+
+ drop function select_one(hstore, text);
+ drop language plpythonu;
diff --git a/contrib/hstore/hstore.h b/contrib/hstore/hstore.h
index 8906397..6edfc70 100644
*** a/contrib/hstore/hstore.h
--- b/contrib/hstore/hstore.h
*************** extern Pairs *hstoreArrayToPairs(ArrayTy
*** 174,179 ****
--- 174,182 ----
#define HStoreExistsAllStrategyNumber 11
#define HStoreOldContainsStrategyNumber 13 /* backwards compatibility */
+ /* PL/Python support */
+ extern void hstore_plpython_init(void);
+
/*
* defining HSTORE_POLLUTE_NAMESPACE=0 will prevent use of old function names;
* for now, we default to on for the benefit of people restoring old dumps
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 0d6f0b6..92c8db9 100644
*** a/contrib/hstore/hstore_io.c
--- b/contrib/hstore/hstore_io.c
*************** PG_MODULE_MAGIC;
*** 20,25 ****
--- 20,26 ----
/* old names for C functions */
HSTORE_POLLUTE(hstore_from_text, tconvert);
+ void _PG_init(void);
typedef struct
{
*************** hstore_send(PG_FUNCTION_ARGS)
*** 1211,1213 ****
--- 1212,1220 ----
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
+
+ void
+ _PG_init(void)
+ {
+ hstore_plpython_init();
+ }
diff --git a/contrib/hstore/hstore_plpython.c b/contrib/hstore/hstore_plpython.c
index ...4bcdd83 .
*** a/contrib/hstore/hstore_plpython.c
--- b/contrib/hstore/hstore_plpython.c
***************
*** 0 ****
--- 1,247 ----
+ /*
+ * contrib/src/hstore_plpython.c
+ *
+ * bidirectional transformation between hstores and Python dictionary objects
+ */
+
+ /* Only build if PL/Python support is needed */
+ #if defined(HSTORE_PLPYTHON_SUPPORT)
+
+ #if defined(_MSC_VER) && defined(_DEBUG)
+ /* Python uses #pragma to bring in a non-default libpython on VC++ if
+ * _DEBUG is defined */
+ #undef _DEBUG
+ /* Also hide away errcode, since we load Python.h before postgres.h */
+ #define errcode __msvc_errcode
+ #include <Python.h>
+ #undef errcode
+ #define _DEBUG
+ #elif defined (_MSC_VER)
+ #define errcode __msvc_errcode
+ #include <Python.h>
+ #undef errcode
+ #else
+ #include <Python.h>
+ #endif
+
+ #include "postgres.h"
+ #include "utils/guc.h"
+ #include "utils/builtins.h"
+ #include "utils/syscache.h"
+ #include "catalog/namespace.h"
+
+ #include "plpython.h"
+ #include "hstore.h"
+
+ static Oid get_hstore_oid(const char *name);
+ static void set_hstore_parsers(Oid);
+
+ static PyObject *hstore_to_dict(void *, Datum);
+ static Datum dict_to_hstore(void *, int32, PyObject *);
+
+ /* GUC variables */
+
+ static char *hstore_name;
+
+ /* Previous hstore OID */
+
+ static Oid previous;
+
+ PLyParsers parsers;
+
+ static PyObject *
+ hstore_to_dict(void *ignored, Datum d)
+ {
+ HStore *hstore = DatumGetHStoreP(d);
+ char *base;
+ HEntry *entries;
+ int count;
+ int i;
+ PyObject *ret;
+
+ base = STRPTR(hstore);
+ entries = ARRPTR(hstore);
+
+ ret = PyDict_New();
+
+ count = HS_COUNT(hstore);
+
+ for (i = 0; i < count; i++)
+ {
+ PyObject *key, *val;
+
+ key = PyString_FromStringAndSize(HS_KEY(entries, base, i),
+ HS_KEYLEN(entries, i));
+ if (HS_VALISNULL(entries, i)) {
+ Py_INCREF(Py_None);
+ val = Py_None;
+ }
+ else {
+ val = PyString_FromStringAndSize(HS_VAL(entries, base, i),
+ HS_VALLEN(entries, i));
+ }
+
+ PyDict_SetItem(ret, key, val);
+ }
+
+ return ret;
+ }
+
+ static Datum
+ dict_to_hstore(void *ignored, int32 typmod, PyObject *dict)
+ {
+ HStore *hstore;
+ int pcount;
+ Pairs *pairs;
+ PyObject *key;
+ PyObject *value;
+ Py_ssize_t pos;
+ char *keys;
+ char *vals;
+ int keylen;
+ int vallen;
+ int buflen;
+ int i;
+
+ if (!PyDict_Check(dict))
+ ereport(ERROR,
+ (errmsg("hstores can only be constructed "
+ "from Python dictionaries")));
+
+ pcount = PyDict_Size(dict);
+ pairs = palloc(pcount * sizeof(Pairs));
+ pos = i = 0;
+ /* loop over the dictionary, creating a Pair for each key/value pair */
+ while (PyDict_Next(dict, &pos, &key, &value)) {
+ if (!PyString_Check(key))
+ elog(ERROR, "hstore keys have to be strings");
+
+ PyString_AsStringAndSize(key, &keys, &keylen);
+
+ if (strlen(keys) != keylen)
+ elog(ERROR, "hstore keys cannot contain NUL bytes");
+
+ pairs[i].key = pstrdup(keys);
+ pairs[i].keylen = hstoreCheckKeyLen(keylen);
+ pairs[i].needfree = true;
+
+ if (value == Py_None) {
+ pairs[i].val = NULL;
+ pairs[i].vallen = 0;
+ pairs[i].isnull = true;
+ }
+ else {
+ if (!PyString_Check(value))
+ elog(ERROR, "hstore values have to be strings");
+
+ PyString_AsStringAndSize(value, &vals, &vallen);
+
+ if (strlen(vals) != vallen)
+ elog(ERROR, "hstore values cannot contain NUL bytes");
+
+ pairs[i].val = pstrdup(vals);
+ pairs[i].vallen = hstoreCheckValLen(vallen);
+ pairs[i].isnull = false;
+ }
+
+ i++;
+ }
+ pcount = hstoreUniquePairs(pairs, pcount, &buflen);
+ hstore = hstorePairs(pairs, pcount, buflen);
+
+ return PointerGetDatum(hstore);
+ }
+
+ static const char *
+ recheck_hstore_oid(const char *newvalue, bool doit, GucSource source)
+ {
+ Oid hstore_oid;
+
+ hstore_oid = get_hstore_oid(newvalue);
+
+ if (*newvalue && !OidIsValid(hstore_oid))
+ return NULL;
+
+ if (doit)
+ set_hstore_parsers(hstore_oid);
+
+ return newvalue;
+ }
+
+ void
+ hstore_plpython_init(void)
+ {
+ DefineCustomStringVariable("plpython.hstore",
+ "The fully qualified name of the hstore type.",
+ NULL,
+ &hstore_name,
+ "",
+ PGC_SUSET,
+ 0,
+ recheck_hstore_oid,
+ NULL);
+
+ EmitWarningsOnPlaceholders("plpython");
+
+ previous = InvalidOid;
+ parsers.in = hstore_to_dict;
+ parsers.out = dict_to_hstore;
+
+ if (hstore_name && *hstore_name)
+ recheck_hstore_oid(hstore_name, true, PGC_S_FILE);
+ }
+
+ static Oid
+ get_hstore_oid(const char *name)
+ {
+ text *text_name;
+ List *hstore_name;
+ char *type_name;
+ Oid type_namespace;
+ Oid typoid;
+
+ Assert(name != NULL);
+
+ if (!(*name))
+ return InvalidOid;
+
+ text_name = cstring_to_text(name);
+ hstore_name = textToQualifiedNameList(text_name);
+ pfree(text_name);
+
+ type_namespace = QualifiedNameGetCreationNamespace(hstore_name, &type_name);
+
+ typoid = GetSysCacheOid2(TYPENAMENSP,
+ CStringGetDatum(type_name),
+ ObjectIdGetDatum(type_namespace));
+
+ return typoid;
+ }
+
+ static void
+ set_hstore_parsers(Oid hstore_oid)
+ {
+ char name[NAMEDATALEN];
+
+ if (OidIsValid(previous))
+ {
+ snprintf(name, NAMEDATALEN, PARSERS_VARIABLE_PATTERN, previous);
+ *find_rendezvous_variable(name) = NULL;
+ }
+
+ if (OidIsValid(hstore_oid))
+ {
+ snprintf(name, NAMEDATALEN, PARSERS_VARIABLE_PATTERN, hstore_oid);
+ *find_rendezvous_variable(name) = &parsers;
+ previous = hstore_oid;
+ }
+ }
+
+ #else /* !defined(HSTORE_PLPYTHON_SUPPORT) */
+
+ extern void hstore_plpython_init(void);
+
+ void
+ hstore_plpython_init(void) {};
+
+ #endif /* defined(HSTORE_PLPYTHON_SUPPORT) */
diff --git a/contrib/hstore/sql/hstore.sql b/contrib/hstore/sql/hstore.sql
index 58a7967..065e9a1 100644
*** a/contrib/hstore/sql/hstore.sql
--- b/contrib/hstore/sql/hstore.sql
*************** set enable_seqscan=off;
*** 338,340 ****
--- 338,369 ----
select count(*) from testhstore where h #># 'p=>1';
select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t';
+
+ -- plpython integration
+ set plpython.hstore = 'public.hstore';
+ create language plpythonu;
+
+ create or replace function select_one(h hstore, idx text) returns text as $$
+ try:
+ return h.get(idx)
+ except AttributeError:
+ # h has not been transformed into a dict and is a string
+ return None
+ $$ language plpythonu;
+
+ select coalesce(select_one(h, 'node'), '<null>') as node from testhstore where select_one(h, 'line')::integer < 10;
+
+ reset plpython.hstore;
+
+ create or replace function select_one(h hstore, idx text) returns text as $$
+ try:
+ return h.get(idx)
+ except AttributeError:
+ # h has not been transformed into a dict and is a string
+ return None
+ $$ language plpythonu;
+
+ select coalesce(select_one(h, 'node'), '<null>') as node from testhstore where select_one(h, 'line')::integer < 10;
+
+ drop function select_one(hstore, text);
+ drop language plpythonu;
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index fff7de7..2ed06f4 100644
*** a/src/pl/plpython/plpython.c
--- b/src/pl/plpython/plpython.c
*************** typedef int Py_ssize_t;
*** 90,95 ****
--- 90,97 ----
#include <fcntl.h>
/* postgreSQL stuff */
+ #include "plpython.h"
+
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
*************** static PyObject *PLyList_FromArray(PLyDa
*** 358,363 ****
--- 360,368 ----
static PyObject *PLyDict_FromTuple(PLyTypeInfo *, HeapTuple, TupleDesc);
+ static PLyParserIn PLy_get_custom_input_function(Oid oid);
+ static PLyParserOut PLy_get_custom_output_function(Oid oid);
+
static Datum PLyObject_ToBool(PLyObToDatum *, int32, PyObject *);
static Datum PLyObject_ToBytea(PLyObToDatum *, int32, PyObject *);
static Datum PLyObject_ToDatum(PLyObToDatum *, int32, PyObject *);
*************** PLy_output_datum_func2(PLyObToDatum *arg
*** 1819,1824 ****
--- 1824,1830 ----
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
Oid element_type;
+ Oid argument_type;
perm_fmgr_info(typeStruct->typinput, &arg->typfunc);
arg->typoid = HeapTupleGetOid(typeTup);
*************** PLy_output_datum_func2(PLyObToDatum *arg
*** 1826,1837 ****
arg->typbyval = typeStruct->typbyval;
element_type = get_element_type(arg->typoid);
/*
* Select a conversion function to convert Python objects to PostgreSQL
* datums. Most data types can go through the generic function.
*/
! switch (getBaseType(element_type ? element_type : arg->typoid))
{
case BOOLOID:
arg->func = PLyObject_ToBool;
--- 1832,1844 ----
arg->typbyval = typeStruct->typbyval;
element_type = get_element_type(arg->typoid);
+ argument_type = getBaseType(element_type ? element_type : arg->typoid);
/*
* Select a conversion function to convert Python objects to PostgreSQL
* datums. Most data types can go through the generic function.
*/
! switch (argument_type)
{
case BOOLOID:
arg->func = PLyObject_ToBool;
*************** PLy_output_datum_func2(PLyObToDatum *arg
*** 1840,1846 ****
arg->func = PLyObject_ToBytea;
break;
default:
! arg->func = PLyObject_ToDatum;
break;
}
--- 1847,1859 ----
arg->func = PLyObject_ToBytea;
break;
default:
! /* Last ditch effort of finding a rendezvous variable pointing to
! * a parser function, useful for extension modules plugging in
! * their own parsers
! */
! arg->func = (PLyObToDatumFunc) PLy_get_custom_output_function(argument_type);
! if (arg->func == NULL)
! arg->func = PLyObject_ToDatum;
break;
}
*************** PLy_input_datum_func2(PLyDatumToOb *arg,
*** 1882,1887 ****
--- 1895,1901 ----
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
Oid element_type = get_element_type(typeOid);
+ Oid argument_type;
/* Get the type's conversion information */
perm_fmgr_info(typeStruct->typoutput, &arg->typfunc);
*************** PLy_input_datum_func2(PLyDatumToOb *arg,
*** 1891,1898 ****
arg->typlen = typeStruct->typlen;
arg->typalign = typeStruct->typalign;
/* Determine which kind of Python object we will convert to */
! switch (getBaseType(element_type ? element_type : typeOid))
{
case BOOLOID:
arg->func = PLyBool_FromBool;
--- 1905,1914 ----
arg->typlen = typeStruct->typlen;
arg->typalign = typeStruct->typalign;
+ argument_type = getBaseType(element_type ? element_type : typeOid);
+
/* Determine which kind of Python object we will convert to */
! switch (argument_type)
{
case BOOLOID:
arg->func = PLyBool_FromBool;
*************** PLy_input_datum_func2(PLyDatumToOb *arg,
*** 1919,1925 ****
arg->func = PLyBytes_FromBytea;
break;
default:
! arg->func = PLyString_FromDatum;
break;
}
--- 1935,1947 ----
arg->func = PLyBytes_FromBytea;
break;
default:
! /* Last ditch effort of finding a rendezvous variable pointing to
! * a parser function, useful for extension modules plugging in
! * their own parsers
! */
! arg->func = (PLyDatumToObFunc) PLy_get_custom_input_function(argument_type);
! if (arg->func == NULL)
! arg->func = PLyString_FromDatum;
break;
}
*************** PLy_typeinfo_dealloc(PLyTypeInfo *arg)
*** 1960,1965 ****
--- 1982,2021 ----
}
}
+ /*
+ * Getting the parser functions from a rendezvous variable set by another
+ * extension.
+ */
+ static PLyParserIn
+ PLy_get_custom_input_function(Oid oid)
+ {
+ PLyParsers *parsers;
+ char name[NAMEDATALEN];
+
+ snprintf(name, NAMEDATALEN, PARSERS_VARIABLE_PATTERN, oid);
+ parsers = *find_rendezvous_variable(name);
+
+ if (parsers == NULL)
+ return NULL;
+
+ return parsers->in;
+ }
+
+ static PLyParserOut
+ PLy_get_custom_output_function(Oid oid)
+ {
+ PLyParsers *parsers;
+ char name[NAMEDATALEN];
+
+ snprintf(name, NAMEDATALEN, PARSERS_VARIABLE_PATTERN, oid);
+ parsers = *find_rendezvous_variable(name);
+
+ if (parsers == NULL)
+ return NULL;
+
+ return parsers->out;
+ }
+
static PyObject *
PLyBool_FromBool(PLyDatumToOb *arg, Datum d)
{
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index ...63c40c5 .
*** a/src/pl/plpython/plpython.h
--- b/src/pl/plpython/plpython.h
***************
*** 0 ****
--- 1,40 ----
+ /*
+ * src/pl/plpython/plpython.h
+ */
+ #ifndef __PLPYTHON_H__
+ #define __PLPYTHON_H__
+
+
+
+ /*
+ * Rendezvous variable pattern for parsers exported from other extensions
+ *
+ * An extension providing parsres for type X should look up the type's OID and
+ * set a rendezvous variable using this pattern that points to a PLyParsers
+ * structure. PL/Python will then use these parsers for arguments with that
+ * OID.
+ */
+ #define PARSERS_VARIABLE_PATTERN "plpython_%u_parsers"
+
+ /*
+ * Types for parsers functions that other modules can export to transform
+ * Datums into PyObjects and back. The types need to be compatible with
+ * PLyObToDatumFunc and PLyDatumToObFunc, but we don't want to expose too much
+ * of plpython.c's guts here, so the first arguments is mandated to be a void
+ * pointer that should not be touched. An extension should know exactly what
+ * it's dealing with, so there's no need for it to look at anything contained
+ * in PLyTypeInfo, which is what gets passed here.
+ *
+ * The output parser also gets the type's typmod, which might actually be
+ * useful.
+ */
+ typedef PyObject *(*PLyParserIn) (void *, Datum);
+ typedef Datum (*PLyParserOut) (void *, int32, PyObject *);
+
+ typedef struct PLyParsers
+ {
+ PLyParserIn in;
+ PLyParserOut out;
+ } PLyParsers;
+
+ #endif /* __PLPYTHON_H__ */
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers