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.
diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile
index 1d533fd..4e6ba7b 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,17 ----
  # 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
! 
! ifeq ($(with_python),yes)
! 
! PG_CPPFLAGS := -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
  
  DATA_built = hstore.sql
  DATA = uninstall_hstore.sql
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 ...4ba111a .
*** a/contrib/hstore/hstore_plpython.c
--- b/contrib/hstore/hstore_plpython.c
***************
*** 0 ****
--- 1,251 ----
+ /*
+  * 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 = {
+ 	.in = hstore_to_dict,
+ 	.out = dict_to_hstore
+ };
+ 
+ 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;
+ 
+ 	if (newvalue == NULL)
+ 		return NULL;
+ 
+ 	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,
+ 							   NULL,
+ 							   PGC_SUSET,
+ 							   0,
+ 							   recheck_hstore_oid,
+ 							   NULL);
+ 
+ 	EmitWarningsOnPlaceholders("plpython");
+ 
+ 	previous = InvalidOid;
+ 
+ 	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/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index aafe556..8841af1 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
*** 354,359 ****
--- 356,364 ----
  
  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
*** 1779,1784 ****
--- 1784,1790 ----
  {
  	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
*** 1786,1797 ****
  	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;
--- 1792,1804 ----
  	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
*** 1800,1806 ****
  			arg->func = PLyObject_ToBytea;
  			break;
  		default:
! 			arg->func = PLyObject_ToDatum;
  			break;
  	}
  
--- 1807,1819 ----
  			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,
*** 1842,1847 ****
--- 1855,1861 ----
  {
  	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,
*** 1851,1858 ****
  	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;
--- 1865,1874 ----
  	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,
*** 1879,1885 ****
  			arg->func = PLyBytes_FromBytea;
  			break;
  		default:
! 			arg->func = PLyString_FromDatum;
  			break;
  	}
  
--- 1895,1907 ----
  			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)
*** 1920,1925 ****
--- 1942,1981 ----
  	}
  }
  
+ /*
+  * 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 ...53d25b7 .
*** 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 parsres 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 (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to