Index: plpython.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/pl/plpython/plpython.c,v
retrieving revision 1.120
diff -c -r1.120 plpython.c
*** plpython.c	3 Apr 2009 16:59:42 -0000	1.120
--- plpython.c	26 May 2009 22:58:52 -0000
***************
*** 78,84 ****
   * objects.
   */
  
! typedef PyObject *(*PLyDatumToObFunc) (const char *);
  
  typedef struct PLyDatumToOb
  {
--- 78,85 ----
   * objects.
   */
  
! struct PLyDatumToOb;
! typedef PyObject *(*PLyDatumToObFunc) (struct PLyDatumToOb*, Datum);
  
  typedef struct PLyDatumToOb
  {
***************
*** 104,111 ****
--- 105,120 ----
  /* convert PyObject to a Postgresql Datum or tuple.
   * output from Python
   */
+ 
+ struct PLyObToDatum;
+ struct PLyProcedure;
+ typedef Datum (*PLyObToDatumFunc) (struct PLyProcedure*, 
+ 								   struct PLyObToDatum*, 
+ 								   PyObject *, bool *isnull);
+ 
  typedef struct PLyObToDatum
  {
+ 	PLyObToDatumFunc func;
  	FmgrInfo	typfunc;		/* The type's input function */
  	Oid			typoid;			/* The OID of the type */
  	Oid			typioparam;
***************
*** 255,270 ****
  static void PLy_input_tuple_funcs(PLyTypeInfo *, TupleDesc);
  
  /* conversion functions */
  static PyObject *PLyDict_FromTuple(PLyTypeInfo *, HeapTuple, TupleDesc);
! static PyObject *PLyBool_FromString(const char *);
! static PyObject *PLyFloat_FromString(const char *);
! static PyObject *PLyInt_FromString(const char *);
! static PyObject *PLyLong_FromString(const char *);
! static PyObject *PLyString_FromString(const char *);
! 
! static HeapTuple PLyMapping_ToTuple(PLyTypeInfo *, PyObject *);
! static HeapTuple PLySequence_ToTuple(PLyTypeInfo *, PyObject *);
! static HeapTuple PLyObject_ToTuple(PLyTypeInfo *, PyObject *);
  
  /*
   * Currently active plpython function
--- 264,295 ----
  static void PLy_input_tuple_funcs(PLyTypeInfo *, TupleDesc);
  
  /* conversion functions */
+ static PyObject *PLyBool_FromBool(PLyDatumToOb *arg, Datum d);
+ static PyObject *PLyFloat_FromFloat4(PLyDatumToOb *arg, Datum d);
+ static PyObject *PLyFloat_FromFloat8(PLyDatumToOb *arg, Datum d);
+ static PyObject *PLyFloat_FromNumeric(PLyDatumToOb *arg, Datum d);
+ static PyObject *PLyInt_FromInt16(PLyDatumToOb *arg, Datum d);
+ static PyObject *PLyInt_FromInt32(PLyDatumToOb *arg, Datum d);
+ static PyObject *PLyLong_FromInt64(PLyDatumToOb *arg, Datum d);
+ static PyObject *PLyString_FromText(PLyDatumToOb *arg, Datum d);
+ static PyObject *PLyString_FromDatum(PLyDatumToOb *arg, Datum d);
+ 
  static PyObject *PLyDict_FromTuple(PLyTypeInfo *, HeapTuple, TupleDesc);
! 
! static Datum PLyObject_ToVoid(PLyProcedure *, PLyObToDatum *, 
! 							  PyObject *, bool *isnull);
! static Datum PLyObject_ToBool(PLyProcedure *, PLyObToDatum *, 
! 							  PyObject *, bool *isnull);
! static Datum PLyObject_ToBytea(PLyProcedure *, PLyObToDatum *, 
! 							   PyObject *, bool *isnull);
! static Datum PLyObject_ToText(PLyProcedure *, PLyObToDatum *, 
! 							  PyObject *, bool *isnull);
! static Datum PLyObject_ToDatum(PLyProcedure *, PLyObToDatum *, 
! 							   PyObject *, bool *isnull);
! 
! static HeapTuple PLyMapping_ToTuple(PLyProcedure *, PyObject *);
! static HeapTuple PLySequence_ToTuple(PLyProcedure *, PyObject *);
! static HeapTuple PLyObject_ToTuple(PLyProcedure *, PyObject *);
  
  /*
   * Currently active plpython function
***************
*** 507,514 ****
  
  		for (i = 0; i < natts; i++)
  		{
- 			char	   *src;
- 
  			platt = PyList_GetItem(plkeys, i);
  			if (!PyString_Check(platt))
  				ereport(ERROR,
--- 532,537 ----
***************
*** 533,564 ****
  				modvalues[i] = (Datum) 0;
  				modnulls[i] = 'n';
  			}
! 			else if (plval != Py_None)
  			{
! 				plstr = PyObject_Str(plval);
! 				if (!plstr)
! 					PLy_elog(ERROR, "could not compute string representation of Python object in PL/Python function \"%s\" while modifying trigger row",
! 							 proc->proname);
! 				src = PyString_AsString(plstr);
! 
! 				modvalues[i] =
! 					InputFunctionCall(&proc->result.out.r.atts[atti].typfunc,
! 									  src,
! 									proc->result.out.r.atts[atti].typioparam,
! 									  tupdesc->attrs[atti]->atttypmod);
! 				modnulls[i] = ' ';
! 
! 				Py_DECREF(plstr);
! 				plstr = NULL;
! 			}
! 			else
! 			{
! 				modvalues[i] =
! 					InputFunctionCall(&proc->result.out.r.atts[atti].typfunc,
! 									  NULL,
! 									proc->result.out.r.atts[atti].typioparam,
! 									  tupdesc->attrs[atti]->atttypmod);
! 				modnulls[i] = 'n';
  			}
  
  			Py_DECREF(plval);
--- 556,565 ----
  				modvalues[i] = (Datum) 0;
  				modnulls[i] = 'n';
  			}
! 			else 
  			{
! 				PLyObToDatum *att = &proc->result.out.r.atts[atti];
! 				modvalues[i] = (att->func) (proc, att, plval, &modnulls[i]);
  			}
  
  			Py_DECREF(plval);
***************
*** 784,791 ****
  	Datum		rv;
  	PyObject   *volatile plargs = NULL;
  	PyObject   *volatile plrv = NULL;
- 	PyObject   *volatile plrv_so = NULL;
- 	char	   *plrv_sc;
  
  	PG_TRY();
  	{
--- 785,790 ----
***************
*** 862,868 ****
  
  				Py_XDECREF(plargs);
  				Py_XDECREF(plrv);
- 				Py_XDECREF(plrv_so);
  
  				PLy_function_delete_args(proc);
  
--- 861,866 ----
***************
*** 876,922 ****
  			}
  		}
  
! 		/*
! 		 * If the function is declared to return void, the Python return value
! 		 * must be None. For void-returning functions, we also treat a None
! 		 * return value as a special "void datum" rather than NULL (as is the
! 		 * case for non-void-returning functions).
! 		 */
! 		if (proc->result.out.d.typoid == VOIDOID)
! 		{
! 			if (plrv != Py_None)
! 				ereport(ERROR,
! 						(errcode(ERRCODE_DATATYPE_MISMATCH),
! 					   errmsg("PL/Python function with return type \"void\" did not return None")));
! 
! 			fcinfo->isnull = false;
! 			rv = (Datum) 0;
! 		}
! 		else if (plrv == Py_None)
! 		{
! 			fcinfo->isnull = true;
! 			if (proc->result.is_rowtype < 1)
! 				rv = InputFunctionCall(&proc->result.out.d.typfunc,
! 									   NULL,
! 									   proc->result.out.d.typioparam,
! 									   -1);
! 			else
! 				/* Tuple as None */
! 				rv = (Datum) NULL;
! 		}
! 		else if (proc->result.is_rowtype >= 1)
  		{
  			HeapTuple	tuple = NULL;
  
! 			if (PySequence_Check(plrv))
  				/* composite type as sequence (tuple, list etc) */
! 				tuple = PLySequence_ToTuple(&proc->result, plrv);
  			else if (PyMapping_Check(plrv))
  				/* composite type as mapping (currently only dict) */
! 				tuple = PLyMapping_ToTuple(&proc->result, plrv);
  			else
  				/* returned as smth, must provide method __getattr__(name) */
! 				tuple = PLyObject_ToTuple(&proc->result, plrv);
  
  			if (tuple != NULL)
  			{
--- 874,895 ----
  			}
  		}
  
! 		/* Convert python return value into postgres datatypes */
! 		if (proc->result.is_rowtype >= 1)
  		{
  			HeapTuple	tuple = NULL;
  
! 			if (plrv == Py_None)
! 				tuple = NULL;
! 			else if (PySequence_Check(plrv))
  				/* composite type as sequence (tuple, list etc) */
! 				tuple = PLySequence_ToTuple(proc, plrv);
  			else if (PyMapping_Check(plrv))
  				/* composite type as mapping (currently only dict) */
! 				tuple = PLyMapping_ToTuple(proc, plrv);
  			else
  				/* returned as smth, must provide method __getattr__(name) */
! 				tuple = PLyObject_ToTuple(proc, plrv);
  
  			if (tuple != NULL)
  			{
***************
*** 931,952 ****
  		}
  		else
  		{
! 			fcinfo->isnull = false;
! 			plrv_so = PyObject_Str(plrv);
! 			if (!plrv_so)
! 				PLy_elog(ERROR, "could not create string representation of Python object in PL/Python function \"%s\" while creating return value", proc->proname);
! 			plrv_sc = PyString_AsString(plrv_so);
! 			rv = InputFunctionCall(&proc->result.out.d.typfunc,
! 								   plrv_sc,
! 								   proc->result.out.d.typioparam,
! 								   -1);
  		}
  	}
  	PG_CATCH();
  	{
  		Py_XDECREF(plargs);
  		Py_XDECREF(plrv);
- 		Py_XDECREF(plrv_so);
  
  		PG_RE_THROW();
  	}
--- 904,919 ----
  		}
  		else
  		{
! 			rv = (proc->result.out.d.func) (proc,
! 											&proc->result.out.d, 
! 											plrv,
! 											&fcinfo->isnull);
  		}
  	}
  	PG_CATCH();
  	{
  		Py_XDECREF(plargs);
  		Py_XDECREF(plrv);
  
  		PG_RE_THROW();
  	}
***************
*** 954,960 ****
  
  	Py_XDECREF(plargs);
  	Py_DECREF(plrv);
- 	Py_XDECREF(plrv_so);
  
  	return rv;
  }
--- 921,926 ----
***************
*** 1037,1048 ****
  					arg = NULL;
  				else
  				{
! 					char	   *ct;
! 
! 					ct = OutputFunctionCall(&(proc->args[i].in.d.typfunc),
! 											fcinfo->arg[i]);
! 					arg = (proc->args[i].in.d.func) (ct);
! 					pfree(ct);
  				}
  			}
  
--- 1003,1010 ----
  					arg = NULL;
  				else
  				{
! 					arg = (proc->args[i].in.d.func) (&(proc->args[i].in.d),
! 													 fcinfo->arg[i]);
  				}
  			}
  
***************
*** 1593,1598 ****
--- 1555,1589 ----
  	arg->typoid = HeapTupleGetOid(typeTup);
  	arg->typioparam = getTypeIOParam(typeTup);
  	arg->typbyval = typeStruct->typbyval;
+ 
+ 	/* Determine which kind of Python object we will convert to */
+ 	switch (arg->typoid)
+ 	{
+ 		case VOIDOID:
+ 			arg->func = PLyObject_ToVoid;
+ 			break;
+ 		case BOOLOID:
+ 			arg->func = PLyObject_ToBool;
+ 			break;
+ 		case BYTEAOID:
+ 			arg->func = PLyObject_ToBytea;
+ 			break;
+ 		case BPCHAROID:
+ 		case VARCHAROID:
+ 		case TEXTOID:
+ 			arg->func = PLyObject_ToText;
+ 			break;
+ 
+ 		case FLOAT4OID:
+ 		case FLOAT8OID:
+ 		case NUMERICOID:
+ 		case INT2OID:
+ 		case INT4OID:
+ 		case INT8OID:
+ 		default:
+ 			arg->func = PLyObject_ToDatum;
+ 			break;
+ 	}
  }
  
  static void
***************
*** 1619,1644 ****
  	switch (typeOid)
  	{
  		case BOOLOID:
! 			arg->func = PLyBool_FromString;
  			break;
  		case FLOAT4OID:
  		case FLOAT8OID:
  		case NUMERICOID:
! 			arg->func = PLyFloat_FromString;
  			break;
  		case INT2OID:
  		case INT4OID:
! 			arg->func = PLyInt_FromString;
  			break;
  		case INT8OID:
! 			arg->func = PLyLong_FromString;
  			break;
  		default:
! 			arg->func = PLyString_FromString;
  			break;
  	}
  }
  
  static void
  PLy_typeinfo_init(PLyTypeInfo * arg)
  {
--- 1610,1648 ----
  	switch (typeOid)
  	{
  		case BOOLOID:
! 			arg->func = PLyBool_FromBool;
  			break;
  		case FLOAT4OID:
+ 			arg->func = PLyFloat_FromFloat4;
+ 			break;
  		case FLOAT8OID:
+ 			arg->func = PLyFloat_FromFloat8;
+ 			break;
  		case NUMERICOID:
! 			arg->func = PLyFloat_FromNumeric;
  			break;
  		case INT2OID:
+ 			arg->func = PLyInt_FromInt16;
+ 			break;
  		case INT4OID:
! 			arg->func = PLyInt_FromInt32;
  			break;
  		case INT8OID:
! 			arg->func = PLyLong_FromInt64;
! 			break;
! 		case BPCHAROID:
! 		case VARCHAROID:
! 		case TEXTOID:
! 		case BYTEAOID:
! 			arg->func = PLyString_FromText;
  			break;
  		default:
! 			arg->func = PLyString_FromDatum;
  			break;
  	}
  }
  
+ 
  static void
  PLy_typeinfo_init(PLyTypeInfo * arg)
  {
***************
*** 1660,1716 ****
  	}
  }
  
- /* assumes that a bool is always returned as a 't' or 'f' */
  static PyObject *
! PLyBool_FromString(const char *src)
  {
  	/*
  	 * We would like to use Py_RETURN_TRUE and Py_RETURN_FALSE here for
  	 * generating SQL from trigger functions, but those are only supported in
  	 * Python >= 2.3, and we support older versions.
  	 * http://docs.python.org/api/boolObjects.html
  	 */
! 	if (src[0] == 't')
  		return PyBool_FromLong(1);
! 	return PyBool_FromLong(0);
  }
  
  static PyObject *
! PLyFloat_FromString(const char *src)
  {
! 	double		v;
! 	char	   *eptr;
  
! 	errno = 0;
! 	v = strtod(src, &eptr);
! 	if (*eptr != '\0' || errno)
! 		return NULL;
! 	return PyFloat_FromDouble(v);
  }
  
  static PyObject *
! PLyInt_FromString(const char *src)
  {
! 	long		v;
! 	char	   *eptr;
  
! 	errno = 0;
! 	v = strtol(src, &eptr, 0);
! 	if (*eptr != '\0' || errno)
! 		return NULL;
! 	return PyInt_FromLong(v);
  }
  
  static PyObject *
! PLyLong_FromString(const char *src)
  {
! 	return PyLong_FromString((char *) src, NULL, 0);
  }
  
  static PyObject *
! PLyString_FromString(const char *src)
  {
! 	return PyString_FromString(src);
  }
  
  static PyObject *
--- 1664,1758 ----
  	}
  }
  
  static PyObject *
! PLyBool_FromBool(PLyDatumToOb *arg, Datum d)
  {
+ 	bool x = DatumGetBool(d);
+ 	arg = 0;  /* unused */
+ 
  	/*
  	 * We would like to use Py_RETURN_TRUE and Py_RETURN_FALSE here for
  	 * generating SQL from trigger functions, but those are only supported in
  	 * Python >= 2.3, and we support older versions.
  	 * http://docs.python.org/api/boolObjects.html
  	 */
! 	if (x)
  		return PyBool_FromLong(1);
! 	else
! 		return PyBool_FromLong(0);
  }
  
  static PyObject *
! PLyFloat_FromFloat4(PLyDatumToOb *arg, Datum d)
  {
! 	arg = 0;  /* unused */
! 	return PyFloat_FromDouble(DatumGetFloat4(d));
! }
  
! static PyObject *
! PLyFloat_FromFloat8(PLyDatumToOb *arg, Datum d)
! {
! 	arg = 0;  /* unused */
! 	return PyFloat_FromDouble(DatumGetFloat8(d));
  }
  
  static PyObject *
! PLyFloat_FromNumeric(PLyDatumToOb *arg, Datum d)
  {
! 	/* 
! 	 * Numeric is cast to a PyFloat: 
! 	 *   This results in a loss of precision
! 	 *   Would it be better to cast to PyString? 
! 	 */
! 	Datum  f = DirectFunctionCall1(numeric_float8, d);
! 	double x = DatumGetFloat8(f);
! 	arg = 0;  /* unused */
! 	return PyFloat_FromDouble(x);
! }
  
! static PyObject *
! PLyInt_FromInt16(PLyDatumToOb *arg, Datum d)
! {
! 	arg = 0;  /* unused */
! 	return PyInt_FromLong(DatumGetInt16(d));
  }
  
  static PyObject *
! PLyInt_FromInt32(PLyDatumToOb *arg, Datum d)
  {
! 	arg = 0;  /* unused */
! 	return PyInt_FromLong(DatumGetInt32(d));
  }
  
  static PyObject *
! PLyLong_FromInt64(PLyDatumToOb *arg, Datum d)
  {
! 	arg = 0;  /* unused */
! 
! 	/* on 32 bit platforms "long" may be too small */
! 	if (sizeof(int64) > sizeof(long))
! 		return PyLong_FromLongLong(DatumGetInt64(d));
! 	else
! 		return PyLong_FromLong(DatumGetInt64(d));
! }
! 
! static PyObject *
! PLyString_FromText(PLyDatumToOb *arg, Datum d)
! {
! 	text     *txt = DatumGetTextP(d);
! 	char     *str = VARDATA(txt);
! 	size_t    size = VARSIZE(txt) - VARHDRSZ;
! 
! 	return PyString_FromStringAndSize(str, size);
! }
! 
! static PyObject *
! PLyString_FromDatum(PLyDatumToOb *arg, Datum d)
! {
! 	char     *x = OutputFunctionCall(&arg->typfunc, d);
! 	PyObject *r = PyString_FromString(x);
! 	pfree(x);
! 	return r;
  }
  
  static PyObject *
***************
*** 1730,1737 ****
  	{
  		for (i = 0; i < info->in.r.natts; i++)
  		{
! 			char	   *key,
! 					   *vsrc;
  			Datum		vattr;
  			bool		is_null;
  			PyObject   *value;
--- 1772,1778 ----
  	{
  		for (i = 0; i < info->in.r.natts; i++)
  		{
! 			char	   *key;
  			Datum		vattr;
  			bool		is_null;
  			PyObject   *value;
***************
*** 1746,1759 ****
  				PyDict_SetItemString(dict, key, Py_None);
  			else
  			{
! 				vsrc = OutputFunctionCall(&info->in.r.atts[i].typfunc,
! 										  vattr);
! 
! 				/*
! 				 * no exceptions allowed
! 				 */
! 				value = info->in.r.atts[i].func(vsrc);
! 				pfree(vsrc);
  				PyDict_SetItemString(dict, key, value);
  				Py_DECREF(value);
  			}
--- 1787,1793 ----
  				PyDict_SetItemString(dict, key, Py_None);
  			else
  			{
! 				value = (info->in.r.atts[i].func) (&info->in.r.atts[i], vattr);
  				PyDict_SetItemString(dict, key, value);
  				Py_DECREF(value);
  			}
***************
*** 1769,1777 ****
  	return dict;
  }
  
  
  static HeapTuple
! PLyMapping_ToTuple(PLyTypeInfo * info, PyObject * mapping)
  {
  	TupleDesc	desc;
  	HeapTuple	tuple;
--- 1803,2017 ----
  	return dict;
  }
  
+ static Datum 
+ PLyObject_ToVoid(PLyProcedure *proc, 
+ 				 PLyObToDatum *arg, 
+ 				 PyObject *plrv, 
+ 				 bool *isnull)
+ {
+ 	/* 
+ 	 * If the function is declared to return void, the Python return value must
+ 	 * be None.  For void-returning functions, we also treat a None return value
+ 	 * as a special "void datum" rather than NULL (as is the case for the 
+ 	 * non-void-returning functions).
+ 	 */
+ 	if (plrv != Py_None)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 				 errmsg("PL/Python function with return type \"void\" did not "
+ 						"return None")));
+ 
+ 	*isnull = false;
+ 	return (Datum) 0;
+ }
+ 
+ static Datum 
+ PLyObject_ToBool(PLyProcedure *proc, 
+ 				 PLyObToDatum *arg, 
+ 				 PyObject *plrv, 
+ 				 bool *isnull)
+ {
+ 	bool rv; 
+ 
+ 	if (plrv == Py_None)
+ 	{
+ 		*isnull = true;
+ 		return (Datum) 0;
+ 	}
+ 
+ 	rv = PyObject_IsTrue(plrv);
+ 	*isnull = false;
+ 	return BoolGetDatum(rv);
+ }
+ 
+ 
+ static Datum 
+ PLyObject_ToBytea(PLyProcedure *proc, 
+ 				  PLyObToDatum *arg, 
+ 				  PyObject *plrv, 
+ 				  bool *isnull)
+ {
+ 	PyObject   *volatile plrv_so = NULL;
+ 	Datum       rv;
+ 
+ 	if (plrv == Py_None)
+ 	{
+ 		*isnull = true;
+ 		return (Datum) 0;
+ 	}
+ 
+ 	plrv_so = PyObject_Str(plrv);
+ 	if (!plrv_so)
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 				 errmsg("could not create string representation of Python "
+ 						"object in PL/Python function \"%s\" while creating "
+ 						"return value", proc->proname)));
+ 	}
+ 
+ 	PG_TRY();
+ 	{
+ 		char *plrv_sc = PyString_AsString(plrv_so);
+ 		size_t len = PyString_Size(plrv_so);
+ 		size_t size = len + VARHDRSZ;
+ 		bytea *result = (bytea*) palloc(size);
+ 
+ 		SET_VARSIZE(result, size);
+ 		memcpy(VARDATA(result), plrv_sc, len);
+ 		rv = PointerGetDatum(result);
+ 	}
+ 	PG_CATCH();
+ 	{
+ 		Py_XDECREF(plrv_so);
+ 		PG_RE_THROW();
+ 	}
+ 	PG_END_TRY();
+ 
+ 	Py_XDECREF(plrv_so);
+ 
+ 	*isnull = false;
+ 	return rv;
+ }
+ 
+ static Datum 
+ PLyObject_ToText(PLyProcedure *proc, 
+ 				 PLyObToDatum *arg, 
+ 				 PyObject *plrv, 
+ 				 bool *isnull)
+ {
+ 	PyObject   *volatile plrv_so = NULL;
+ 	Datum       rv;
+ 
+ 	if (plrv == Py_None)
+ 	{
+ 		*isnull = true;
+ 		return (Datum) 0;
+ 	}
+ 
+ 	plrv_so = PyObject_Str(plrv);
+ 	if (!plrv_so)
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 				 errmsg("could not create string representation of Python "
+ 						"object in PL/Python function \"%s\" while creating "
+ 						"return value", proc->proname)));
+ 	}
+ 
+ 	PG_TRY();
+ 	{
+ 		char *plrv_sc = PyString_AsString(plrv_so);
+ 		size_t len    = PyString_Size(plrv_so);
+ 		size_t size   = len + VARHDRSZ;
+ 		text *result;
+ 
+ 		if (strlen(plrv_sc) != (size_t) len)
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 					 errmsg("PL/Python function \"%s\" could not convert "
+ 							"Python object into text: expected string without "
+ 							"null bytes", proc->proname)));
+ 		}
+ 
+ 		result = (bytea*) palloc(size);
+ 		SET_VARSIZE(result, size);
+ 		memcpy(VARDATA(result), plrv_sc, len);
+ 		rv = PointerGetDatum(result);
+ 	}
+ 	PG_CATCH();
+ 	{
+ 		Py_XDECREF(plrv_so);
+ 		PG_RE_THROW();
+ 	}
+ 	PG_END_TRY();
+ 
+ 	Py_XDECREF(plrv_so);
+ 
+ 	*isnull = false;
+ 	return rv;
+ }
+ 
+ /* 
+  * Generic conversion function:
+  *  - Cast PyObject to cstring and cstring into postgres type.
+  */
+ static Datum 
+ PLyObject_ToDatum(PLyProcedure *proc, 
+ 				  PLyObToDatum *arg, 
+ 				  PyObject *plrv, 
+ 				  bool *isnull)
+ {
+ 	PyObject *volatile plrv_so = NULL;
+ 	Datum     rv;
+ 
+ 	if (plrv == Py_None)
+ 	{
+ 		*isnull = true;
+ 		return (Datum) 0;
+ 	}
+ 
+ 	plrv_so = PyObject_Str(plrv);
+ 	if (!plrv_so)
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 				 errmsg("could not create string representation of Python "
+ 						"object in PL/Python function \"%s\" while creating "
+ 						"return value", proc->proname)));
+ 	}
+ 
+ 	PG_TRY();
+ 	{
+ 		char *plrv_sc = PyString_AsString(plrv_so);
+ 		size_t len    = PyString_Size(plrv_so);		
+ 
+ 		if (strlen(plrv_sc) != (size_t) len)
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 					 errmsg("PL/Python function \"%s\" could not convert "
+ 							"Python object into cstring: expected string without "
+ 							"null bytes", proc->proname)));
+ 		}
+ 		rv = InputFunctionCall(&arg->typfunc, plrv_sc, arg->typioparam, -1);
+ 	}
+ 	PG_CATCH();
+ 	{
+ 		Py_XDECREF(plrv_so);
+ 		PG_RE_THROW();
+ 	}
+ 	PG_END_TRY();
+ 
+ 	Py_XDECREF(plrv_so);
+ 
+ 	*isnull = false;
+ 	return rv;
+ }
  
  static HeapTuple
! PLyMapping_ToTuple(PLyProcedure *proc, PyObject *mapping)
  {
  	TupleDesc	desc;
  	HeapTuple	tuple;
***************
*** 1781,1840 ****
  
  	Assert(PyMapping_Check(mapping));
  
! 	desc = lookup_rowtype_tupdesc(info->out.d.typoid, -1);
! 	if (info->is_rowtype == 2)
! 		PLy_output_tuple_funcs(info, desc);
! 	Assert(info->is_rowtype == 1);
  
  	/* Build tuple */
  	values = palloc(sizeof(Datum) * desc->natts);
  	nulls = palloc(sizeof(bool) * desc->natts);
  	for (i = 0; i < desc->natts; ++i)
  	{
! 		char	   *key;
! 		PyObject   *volatile value,
! 				   *volatile so;
  
  		key = NameStr(desc->attrs[i]->attname);
! 		value = so = NULL;
  		PG_TRY();
  		{
  			value = PyMapping_GetItemString(mapping, key);
! 			if (value == Py_None)
  			{
- 				values[i] = (Datum) NULL;
- 				nulls[i] = true;
- 			}
- 			else if (value)
- 			{
- 				char	   *valuestr;
- 
- 				so = PyObject_Str(value);
- 				if (so == NULL)
- 					PLy_elog(ERROR, "could not compute string representation of Python object");
- 				valuestr = PyString_AsString(so);
- 
- 				values[i] = InputFunctionCall(&info->out.r.atts[i].typfunc
- 											  ,valuestr
- 											  ,info->out.r.atts[i].typioparam
- 											  ,-1);
- 				Py_DECREF(so);
- 				so = NULL;
- 				nulls[i] = false;
- 			}
- 			else
  				ereport(ERROR,
  						(errcode(ERRCODE_UNDEFINED_COLUMN),
  						 errmsg("key \"%s\" not found in mapping", key),
  						 errhint("To return null in a column, "
! 					  "add the value None to the mapping with the key named after the column.")));
  
  			Py_XDECREF(value);
  			value = NULL;
  		}
  		PG_CATCH();
  		{
- 			Py_XDECREF(so);
  			Py_XDECREF(value);
  			PG_RE_THROW();
  		}
--- 2021,2062 ----
  
  	Assert(PyMapping_Check(mapping));
  
! 	desc = lookup_rowtype_tupdesc(proc->result.out.d.typoid, -1);
! 	if (proc->result.is_rowtype == 2)
! 		PLy_output_tuple_funcs(&proc->result, desc);
! 	Assert(proc->result.is_rowtype == 1);
  
  	/* Build tuple */
  	values = palloc(sizeof(Datum) * desc->natts);
  	nulls = palloc(sizeof(bool) * desc->natts);
  	for (i = 0; i < desc->natts; ++i)
  	{
! 		char	     *key;
! 		PLyObToDatum *att;
! 		PyObject     *volatile value;
  
+ 		att = &proc->result.out.r.atts[i];
  		key = NameStr(desc->attrs[i]->attname);
! 		value = NULL;
  		PG_TRY();
  		{
  			value = PyMapping_GetItemString(mapping, key);
! 			if (!value)
  			{
  				ereport(ERROR,
  						(errcode(ERRCODE_UNDEFINED_COLUMN),
  						 errmsg("key \"%s\" not found in mapping", key),
  						 errhint("To return null in a column, "
! 								 "add the value None to the mapping with the "
! 								 "key named after the column.")));
! 			}
! 			values[i] = (att->func) (proc, att, value, &nulls[i]);
  
  			Py_XDECREF(value);
  			value = NULL;
  		}
  		PG_CATCH();
  		{
  			Py_XDECREF(value);
  			PG_RE_THROW();
  		}
***************
*** 1851,1857 ****
  
  
  static HeapTuple
! PLySequence_ToTuple(PLyTypeInfo * info, PyObject * sequence)
  {
  	TupleDesc	desc;
  	HeapTuple	tuple;
--- 2073,2079 ----
  
  
  static HeapTuple
! PLySequence_ToTuple(PLyProcedure *proc, PyObject *sequence)
  {
  	TupleDesc	desc;
  	HeapTuple	tuple;
***************
*** 1866,1922 ****
  	 * can ignore exceeding items or assume missing ones as null but to avoid
  	 * plpython developer's errors we are strict here
  	 */
! 	desc = lookup_rowtype_tupdesc(info->out.d.typoid, -1);
  	if (PySequence_Length(sequence) != desc->natts)
  		ereport(ERROR,
  				(errcode(ERRCODE_DATATYPE_MISMATCH),
  		errmsg("length of returned sequence did not match number of columns in row")));
  
! 	if (info->is_rowtype == 2)
! 		PLy_output_tuple_funcs(info, desc);
! 	Assert(info->is_rowtype == 1);
  
  	/* Build tuple */
  	values = palloc(sizeof(Datum) * desc->natts);
  	nulls = palloc(sizeof(bool) * desc->natts);
  	for (i = 0; i < desc->natts; ++i)
  	{
! 		PyObject   *volatile value,
! 				   *volatile so;
  
! 		value = so = NULL;
  		PG_TRY();
  		{
  			value = PySequence_GetItem(sequence, i);
  			Assert(value);
! 			if (value == Py_None)
! 			{
! 				values[i] = (Datum) NULL;
! 				nulls[i] = true;
! 			}
! 			else if (value)
! 			{
! 				char	   *valuestr;
! 
! 				so = PyObject_Str(value);
! 				if (so == NULL)
! 					PLy_elog(ERROR, "could not compute string representation of Python object");
! 				valuestr = PyString_AsString(so);
! 				values[i] = InputFunctionCall(&info->out.r.atts[i].typfunc
! 											  ,valuestr
! 											  ,info->out.r.atts[i].typioparam
! 											  ,-1);
! 				Py_DECREF(so);
! 				so = NULL;
! 				nulls[i] = false;
! 			}
  
  			Py_XDECREF(value);
  			value = NULL;
  		}
  		PG_CATCH();
  		{
- 			Py_XDECREF(so);
  			Py_XDECREF(value);
  			PG_RE_THROW();
  		}
--- 2088,2124 ----
  	 * can ignore exceeding items or assume missing ones as null but to avoid
  	 * plpython developer's errors we are strict here
  	 */
! 	desc = lookup_rowtype_tupdesc(proc->result.out.d.typoid, -1);
  	if (PySequence_Length(sequence) != desc->natts)
  		ereport(ERROR,
  				(errcode(ERRCODE_DATATYPE_MISMATCH),
  		errmsg("length of returned sequence did not match number of columns in row")));
  
! 	if (proc->result.is_rowtype == 2)
! 		PLy_output_tuple_funcs(&proc->result, desc);
! 	Assert(proc->result.is_rowtype == 1);
  
  	/* Build tuple */
  	values = palloc(sizeof(Datum) * desc->natts);
  	nulls = palloc(sizeof(bool) * desc->natts);
  	for (i = 0; i < desc->natts; ++i)
  	{
! 		PLyObToDatum *att;
! 		PyObject     *volatile value;
  
! 		att = &proc->result.out.r.atts[i];
! 		value = NULL;
  		PG_TRY();
  		{
  			value = PySequence_GetItem(sequence, i);
  			Assert(value);
! 			values[i] = (att->func) (proc, att, value, &nulls[i]);
  
  			Py_XDECREF(value);
  			value = NULL;
  		}
  		PG_CATCH();
  		{
  			Py_XDECREF(value);
  			PG_RE_THROW();
  		}
***************
*** 1933,1939 ****
  
  
  static HeapTuple
! PLyObject_ToTuple(PLyTypeInfo * info, PyObject * object)
  {
  	TupleDesc	desc;
  	HeapTuple	tuple;
--- 2135,2141 ----
  
  
  static HeapTuple
! PLyObject_ToTuple(PLyProcedure *proc, PyObject *object)
  {
  	TupleDesc	desc;
  	HeapTuple	tuple;
***************
*** 1941,1962 ****
  	bool	   *nulls;
  	volatile int i;
  
! 	desc = lookup_rowtype_tupdesc(info->out.d.typoid, -1);
! 	if (info->is_rowtype == 2)
! 		PLy_output_tuple_funcs(info, desc);
! 	Assert(info->is_rowtype == 1);
  
  	/* Build tuple */
  	values = palloc(sizeof(Datum) * desc->natts);
  	nulls = palloc(sizeof(bool) * desc->natts);
  	for (i = 0; i < desc->natts; ++i)
  	{
! 		char	   *key;
! 		PyObject   *volatile value,
! 				   *volatile so;
  
  		key = NameStr(desc->attrs[i]->attname);
! 		value = so = NULL;
  		PG_TRY();
  		{
  			value = PyObject_GetAttrString(object, key);
--- 2143,2165 ----
  	bool	   *nulls;
  	volatile int i;
  
! 	desc = lookup_rowtype_tupdesc(proc->result.out.d.typoid, -1);
! 	if (proc->result.is_rowtype == 2)
! 		PLy_output_tuple_funcs(&proc->result, desc);
! 	Assert(proc->result.is_rowtype == 1);
  
  	/* Build tuple */
  	values = palloc(sizeof(Datum) * desc->natts);
  	nulls = palloc(sizeof(bool) * desc->natts);
  	for (i = 0; i < desc->natts; ++i)
  	{
! 		char	     *key;
! 		PLyObToDatum *att;
! 		PyObject     *volatile value;
  
+ 		att = &proc->result.out.r.atts[i];
  		key = NameStr(desc->attrs[i]->attname);
! 		value = NULL;
  		PG_TRY();
  		{
  			value = PyObject_GetAttrString(object, key);
***************
*** 1965,2000 ****
  				values[i] = (Datum) NULL;
  				nulls[i] = true;
  			}
! 			else if (value)
  			{
- 				char	   *valuestr;
- 
- 				so = PyObject_Str(value);
- 				if (so == NULL)
- 					PLy_elog(ERROR, "could not compute string representation of Python object");
- 				valuestr = PyString_AsString(so);
- 				values[i] = InputFunctionCall(&info->out.r.atts[i].typfunc
- 											  ,valuestr
- 											  ,info->out.r.atts[i].typioparam
- 											  ,-1);
- 				Py_DECREF(so);
- 				so = NULL;
- 				nulls[i] = false;
- 			}
- 			else
  				ereport(ERROR,
  						(errcode(ERRCODE_UNDEFINED_COLUMN),
! 						 errmsg("attribute \"%s\" does not exist in Python object", key),
  						 errhint("To return null in a column, "
! 								 "let the returned object have an attribute named "
! 								 "after column with value None.")));
  
  			Py_XDECREF(value);
  			value = NULL;
  		}
  		PG_CATCH();
  		{
- 			Py_XDECREF(so);
  			Py_XDECREF(value);
  			PG_RE_THROW();
  		}
--- 2168,2190 ----
  				values[i] = (Datum) NULL;
  				nulls[i] = true;
  			}
! 			else if (!value)
  			{
  				ereport(ERROR,
  						(errcode(ERRCODE_UNDEFINED_COLUMN),
! 						 errmsg("key \"%s\" not found in object", key),
  						 errhint("To return null in a column, "
! 								 "add the value None to the mapping with the "
! 								 "key named after the column.")));
! 			}
! 			else
! 				values[i] = (att->func) (proc, att, value, &nulls[i]);
  
  			Py_XDECREF(value);
  			value = NULL;
  		}
  		PG_CATCH();
  		{
  			Py_XDECREF(value);
  			PG_RE_THROW();
  		}
Index: expected/plpython_function.out
===================================================================
RCS file: /projects/cvsroot/pgsql/src/pl/plpython/expected/plpython_function.out,v
retrieving revision 1.12
diff -c -r1.12 plpython_function.out
*** expected/plpython_function.out	3 Apr 2009 16:59:42 -0000	1.12
--- expected/plpython_function.out	26 May 2009 22:58:52 -0000
***************
*** 450,452 ****
--- 450,470 ----
  CREATE FUNCTION test_inout_params(first inout text) AS $$
  return first + '_inout';
  $$ LANGUAGE plpythonu;
+ CREATE FUNCTION test_type_conversion_bool(x bool) returns bool AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_char(x char) returns char AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_int2(x int2) returns int2 AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_int4(x int4) returns int4 AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_int8(x int8) returns int8 AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_float4(x float4) returns float4 AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_float8(x float8) returns float8 AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_numeric(x numeric) returns numeric AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_text(x text) returns text AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_bytea(x bytea) returns bytea AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_marshal() returns bytea AS $$ 
+ import marshal
+ return marshal.dumps('hello world')
+ $$ language plpythonu;
+ CREATE FUNCTION test_type_unmarshal(x bytea) returns text AS $$
+ import marshal
+ return marshal.loads(x)
+ $$ language plpythonu;
Index: expected/plpython_test.out
===================================================================
RCS file: /projects/cvsroot/pgsql/src/pl/plpython/expected/plpython_test.out,v
retrieving revision 1.8
diff -c -r1.8 plpython_test.out
*** expected/plpython_test.out	3 Apr 2009 16:59:42 -0000	1.8
--- expected/plpython_test.out	26 May 2009 22:58:52 -0000
***************
*** 559,561 ****
--- 559,693 ----
   test_in_inout
  (1 row)
  
+ SELECT * FROM test_type_conversion_bool(true);
+  test_type_conversion_bool 
+ ---------------------------
+  t
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_bool(false);
+  test_type_conversion_bool 
+ ---------------------------
+  f
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_bool(null);
+  test_type_conversion_bool 
+ ---------------------------
+  
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_char('a');
+  test_type_conversion_char 
+ ---------------------------
+  a
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_char(null);
+  test_type_conversion_char 
+ ---------------------------
+  
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_int2(100::int2);
+  test_type_conversion_int2 
+ ---------------------------
+                        100
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_int2(null);
+  test_type_conversion_int2 
+ ---------------------------
+                           
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_int4(100);
+  test_type_conversion_int4 
+ ---------------------------
+                        100
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_int4(null);
+  test_type_conversion_int4 
+ ---------------------------
+                           
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_int8(100);
+  test_type_conversion_int8 
+ ---------------------------
+                        100
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_int8(null);
+  test_type_conversion_int8 
+ ---------------------------
+                           
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_float4(100);
+  test_type_conversion_float4 
+ -----------------------------
+                          100
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_float4(null);
+  test_type_conversion_float4 
+ -----------------------------
+                             
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_float8(100);
+  test_type_conversion_float8 
+ -----------------------------
+                          100
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_float8(null);
+  test_type_conversion_float8 
+ -----------------------------
+                             
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_numeric(100);
+  test_type_conversion_numeric 
+ ------------------------------
+                         100.0
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_numeric(null);
+  test_type_conversion_numeric 
+ ------------------------------
+                              
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_text('hello world');
+  test_type_conversion_text 
+ ---------------------------
+  hello world
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_text(null);
+  test_type_conversion_text 
+ ---------------------------
+  
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_bytea('hello world');
+  test_type_conversion_bytea 
+ ----------------------------
+  hello world
+ (1 row)
+ 
+ SELECT * FROM test_type_conversion_bytea(null);
+  test_type_conversion_bytea 
+ ----------------------------
+  
+ (1 row)
+ 
+ SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
+  test_type_unmarshal 
+ ---------------------
+  hello world
+ (1 row)
+ 
Index: sql/plpython_function.sql
===================================================================
RCS file: /projects/cvsroot/pgsql/src/pl/plpython/sql/plpython_function.sql,v
retrieving revision 1.12
diff -c -r1.12 plpython_function.sql
*** sql/plpython_function.sql	3 Apr 2009 16:59:43 -0000	1.12
--- sql/plpython_function.sql	26 May 2009 22:58:52 -0000
***************
*** 497,499 ****
--- 497,518 ----
  CREATE FUNCTION test_inout_params(first inout text) AS $$
  return first + '_inout';
  $$ LANGUAGE plpythonu;
+ 
+ CREATE FUNCTION test_type_conversion_bool(x bool) returns bool AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_char(x char) returns char AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_int2(x int2) returns int2 AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_int4(x int4) returns int4 AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_int8(x int8) returns int8 AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_float4(x float4) returns float4 AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_float8(x float8) returns float8 AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_numeric(x numeric) returns numeric AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_text(x text) returns text AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_conversion_bytea(x bytea) returns bytea AS $$ return x $$ language plpythonu;
+ CREATE FUNCTION test_type_marshal() returns bytea AS $$ 
+ import marshal
+ return marshal.dumps('hello world')
+ $$ language plpythonu;
+ CREATE FUNCTION test_type_unmarshal(x bytea) returns text AS $$
+ import marshal
+ return marshal.loads(x)
+ $$ language plpythonu;
Index: sql/plpython_test.sql
===================================================================
RCS file: /projects/cvsroot/pgsql/src/pl/plpython/sql/plpython_test.sql,v
retrieving revision 1.5
diff -c -r1.5 plpython_test.sql
*** sql/plpython_test.sql	3 Apr 2009 16:59:43 -0000	1.5
--- sql/plpython_test.sql	26 May 2009 22:58:52 -0000
***************
*** 149,151 ****
--- 149,176 ----
  -- this doesn't work yet :-(
  SELECT * FROM test_in_out_params_multi('test_in');
  SELECT * FROM test_inout_params('test_in');
+ 
+ SELECT * FROM test_type_conversion_bool(true);
+ SELECT * FROM test_type_conversion_bool(false);
+ SELECT * FROM test_type_conversion_bool(null);
+ SELECT * FROM test_type_conversion_char('a');
+ SELECT * FROM test_type_conversion_char(null);
+ SELECT * FROM test_type_conversion_int2(100::int2);
+ SELECT * FROM test_type_conversion_int2(null);
+ SELECT * FROM test_type_conversion_int4(100);
+ SELECT * FROM test_type_conversion_int4(null);
+ SELECT * FROM test_type_conversion_int8(100);
+ SELECT * FROM test_type_conversion_int8(null);
+ SELECT * FROM test_type_conversion_float4(100);
+ SELECT * FROM test_type_conversion_float4(null);
+ SELECT * FROM test_type_conversion_float8(100);
+ SELECT * FROM test_type_conversion_float8(null);
+ SELECT * FROM test_type_conversion_numeric(100);
+ SELECT * FROM test_type_conversion_numeric(null);
+ SELECT * FROM test_type_conversion_text('hello world');
+ SELECT * FROM test_type_conversion_text(null);
+ SELECT * FROM test_type_conversion_bytea('hello world');
+ SELECT * FROM test_type_conversion_bytea(null);
+ SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
+ 
+ 
