aboutsummaryrefslogtreecommitdiff
path: root/src/pl/plpython/plpython.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pl/plpython/plpython.c')
-rw-r--r--src/pl/plpython/plpython.c379
1 files changed, 247 insertions, 132 deletions
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index 4aba4ba95ad..909eab033b8 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -1,7 +1,7 @@
/**********************************************************************
* plpython.c - python as a procedural language for PostgreSQL
*
- * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.127 2009/08/25 12:44:59 petere Exp $
+ * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.128 2009/09/09 19:00:09 petere Exp $
*
*********************************************************************
*/
@@ -78,7 +78,8 @@ PG_MODULE_MAGIC;
* objects.
*/
-typedef PyObject *(*PLyDatumToObFunc) (const char *);
+struct PLyDatumToOb;
+typedef PyObject *(*PLyDatumToObFunc) (struct PLyDatumToOb*, Datum);
typedef struct PLyDatumToOb
{
@@ -104,8 +105,16 @@ typedef union PLyTypeInput
/* convert PyObject to a Postgresql Datum or tuple.
* output from Python
*/
+
+struct PLyObToDatum;
+struct PLyTypeInfo;
+typedef Datum (*PLyObToDatumFunc) (struct PLyTypeInfo*,
+ struct PLyObToDatum*,
+ PyObject *);
+
typedef struct PLyObToDatum
{
+ PLyObToDatumFunc func;
FmgrInfo typfunc; /* The type's input function */
Oid typoid; /* The OID of the type */
Oid typioparam;
@@ -131,12 +140,11 @@ typedef struct PLyTypeInfo
{
PLyTypeInput in;
PLyTypeOutput out;
- int is_rowtype;
-
/*
- * is_rowtype can be: -1 not known yet (initial state) 0 scalar datatype
- * 1 rowtype 2 rowtype, but I/O functions not set up yet
+ * is_rowtype can be: -1 = not known yet (initial state); 0 = scalar datatype;
+ * 1 = rowtype; 2 = rowtype, but I/O functions not set up yet
*/
+ int is_rowtype;
} PLyTypeInfo;
@@ -263,12 +271,24 @@ static void PLy_output_tuple_funcs(PLyTypeInfo *, TupleDesc);
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_FromBytea(PLyDatumToOb *arg, Datum d);
+static PyObject *PLyString_FromDatum(PLyDatumToOb *arg, Datum d);
+
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 Datum PLyObject_ToBool(PLyTypeInfo *, PLyObToDatum *,
+ PyObject *);
+static Datum PLyObject_ToBytea(PLyTypeInfo *, PLyObToDatum *,
+ PyObject *);
+static Datum PLyObject_ToDatum(PLyTypeInfo *, PLyObToDatum *,
+ PyObject *);
static HeapTuple PLyMapping_ToTuple(PLyTypeInfo *, PyObject *);
static HeapTuple PLySequence_ToTuple(PLyTypeInfo *, PyObject *);
@@ -552,8 +572,6 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
for (i = 0; i < natts; i++)
{
- char *src;
-
platt = PyList_GetItem(plkeys, i);
if (!PyString_Check(platt))
ereport(ERROR,
@@ -580,20 +598,9 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
}
else if (plval != Py_None)
{
- plstr = PyObject_Str(plval);
- if (!plstr)
- PLy_elog(ERROR, "could not create string representation of Python object");
- 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);
+ PLyObToDatum *att = &proc->result.out.r.atts[atti];
+ modvalues[i] = (att->func) (&proc->result, att, plval);
modnulls[i] = ' ';
-
- Py_DECREF(plstr);
- plstr = NULL;
}
else
{
@@ -830,8 +837,6 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
Datum rv;
PyObject *volatile plargs = NULL;
PyObject *volatile plrv = NULL;
- PyObject *volatile plrv_so = NULL;
- char *plrv_sc;
ErrorContextCallback plerrcontext;
PG_TRY();
@@ -909,7 +914,6 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
Py_XDECREF(plargs);
Py_XDECREF(plrv);
- Py_XDECREF(plrv_so);
PLy_function_delete_args(proc);
@@ -983,21 +987,15 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
else
{
fcinfo->isnull = false;
- plrv_so = PyObject_Str(plrv);
- if (!plrv_so)
- PLy_elog(ERROR, "could not create string representation of Python object");
- plrv_sc = PyString_AsString(plrv_so);
- rv = InputFunctionCall(&proc->result.out.d.typfunc,
- plrv_sc,
- proc->result.out.d.typioparam,
- -1);
+ rv = (proc->result.out.d.func) (&proc->result,
+ &proc->result.out.d,
+ plrv);
}
}
PG_CATCH();
{
Py_XDECREF(plargs);
Py_XDECREF(plrv);
- Py_XDECREF(plrv_so);
PG_RE_THROW();
}
@@ -1007,7 +1005,6 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
Py_XDECREF(plargs);
Py_DECREF(plrv);
- Py_XDECREF(plrv_so);
return rv;
}
@@ -1090,12 +1087,8 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc)
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);
+ arg = (proc->args[i].in.d.func) (&(proc->args[i].in.d),
+ fcinfo->arg[i]);
}
}
@@ -1646,6 +1639,24 @@ PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup)
arg->typoid = HeapTupleGetOid(typeTup);
arg->typioparam = getTypeIOParam(typeTup);
arg->typbyval = typeStruct->typbyval;
+
+ /*
+ * Select a conversion function to convert Python objects to
+ * PostgreSQL datums. Most data types can go through the generic
+ * function.
+ */
+ switch (getBaseType(arg->typoid))
+ {
+ case BOOLOID:
+ arg->func = PLyObject_ToBool;
+ break;
+ case BYTEAOID:
+ arg->func = PLyObject_ToBytea;
+ break;
+ default:
+ arg->func = PLyObject_ToDatum;
+ break;
+ }
}
static void
@@ -1672,22 +1683,31 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup)
switch (getBaseType(typeOid))
{
case BOOLOID:
- arg->func = PLyBool_FromString;
+ arg->func = PLyBool_FromBool;
break;
case FLOAT4OID:
+ arg->func = PLyFloat_FromFloat4;
+ break;
case FLOAT8OID:
+ arg->func = PLyFloat_FromFloat8;
+ break;
case NUMERICOID:
- arg->func = PLyFloat_FromString;
+ arg->func = PLyFloat_FromNumeric;
break;
case INT2OID:
+ arg->func = PLyInt_FromInt16;
+ break;
case INT4OID:
- arg->func = PLyInt_FromString;
+ arg->func = PLyInt_FromInt32;
break;
case INT8OID:
- arg->func = PLyLong_FromString;
+ arg->func = PLyLong_FromInt64;
+ break;
+ case BYTEAOID:
+ arg->func = PLyString_FromBytea;
break;
default:
- arg->func = PLyString_FromString;
+ arg->func = PLyString_FromDatum;
break;
}
}
@@ -1713,9 +1733,8 @@ PLy_typeinfo_dealloc(PLyTypeInfo *arg)
}
}
-/* assumes that a bool is always returned as a 't' or 'f' */
static PyObject *
-PLyBool_FromString(const char *src)
+PLyBool_FromBool(PLyDatumToOb *arg, Datum d)
{
/*
* We would like to use Py_RETURN_TRUE and Py_RETURN_FALSE here for
@@ -1723,47 +1742,75 @@ PLyBool_FromString(const char *src)
* Python >= 2.3, and we support older versions.
* http://docs.python.org/api/boolObjects.html
*/
- if (src[0] == 't')
+ if (DatumGetBool(d))
return PyBool_FromLong(1);
return PyBool_FromLong(0);
}
static PyObject *
-PLyFloat_FromString(const char *src)
+PLyFloat_FromFloat4(PLyDatumToOb *arg, Datum d)
{
- double v;
- char *eptr;
+ return PyFloat_FromDouble(DatumGetFloat4(d));
+}
- errno = 0;
- v = strtod(src, &eptr);
- if (*eptr != '\0' || errno)
- return NULL;
- return PyFloat_FromDouble(v);
+static PyObject *
+PLyFloat_FromFloat8(PLyDatumToOb *arg, Datum d)
+{
+ return PyFloat_FromDouble(DatumGetFloat8(d));
}
static PyObject *
-PLyInt_FromString(const char *src)
+PLyFloat_FromNumeric(PLyDatumToOb *arg, Datum d)
{
- long v;
- char *eptr;
+ /*
+ * 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);
+ return PyFloat_FromDouble(x);
+}
- errno = 0;
- v = strtol(src, &eptr, 0);
- if (*eptr != '\0' || errno)
- return NULL;
- return PyInt_FromLong(v);
+static PyObject *
+PLyInt_FromInt16(PLyDatumToOb *arg, Datum d)
+{
+ return PyInt_FromLong(DatumGetInt16(d));
}
static PyObject *
-PLyLong_FromString(const char *src)
+PLyInt_FromInt32(PLyDatumToOb *arg, Datum d)
{
- return PyLong_FromString((char *) src, NULL, 0);
+ return PyInt_FromLong(DatumGetInt32(d));
}
static PyObject *
-PLyString_FromString(const char *src)
+PLyLong_FromInt64(PLyDatumToOb *arg, Datum d)
{
- return PyString_FromString(src);
+ /* 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_FromBytea(PLyDatumToOb *arg, Datum d)
+{
+ text *txt = DatumGetByteaP(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 *
@@ -1783,8 +1830,7 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc)
{
for (i = 0; i < info->in.r.natts; i++)
{
- char *key,
- *vsrc;
+ char *key;
Datum vattr;
bool is_null;
PyObject *value;
@@ -1799,14 +1845,7 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc)
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);
+ value = (info->in.r.atts[i].func) (&info->in.r.atts[i], vattr);
PyDict_SetItemString(dict, key, value);
Py_DECREF(value);
}
@@ -1822,6 +1861,116 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc)
return dict;
}
+/*
+ * Convert a Python object to a PostgreSQL bool datum. This can't go
+ * through the generic conversion function, because Python attaches a
+ * Boolean value to everything, more things than the PostgreSQL bool
+ * type can parse.
+ */
+static Datum
+PLyObject_ToBool(PLyTypeInfo *info,
+ PLyObToDatum *arg,
+ PyObject *plrv)
+{
+ Datum rv;
+
+ Assert(plrv != Py_None);
+ rv = BoolGetDatum(PyObject_IsTrue(plrv));
+
+ if (get_typtype(arg->typoid) == TYPTYPE_DOMAIN)
+ domain_check(rv, false, arg->typoid, &arg->typfunc.fn_extra, arg->typfunc.fn_mcxt);
+
+ return rv;
+}
+
+/*
+ * Convert a Python object to a PostgreSQL bytea datum. This doesn't
+ * go through the generic conversion function to circumvent problems
+ * with embedded nulls. And it's faster this way.
+ */
+static Datum
+PLyObject_ToBytea(PLyTypeInfo *info,
+ PLyObToDatum *arg,
+ PyObject *plrv)
+{
+ PyObject *volatile plrv_so = NULL;
+ Datum rv;
+
+ Assert(plrv != Py_None);
+
+ plrv_so = PyObject_Str(plrv);
+ if (!plrv_so)
+ PLy_elog(ERROR, "could not create string representation of Python object");
+
+ PG_TRY();
+ {
+ char *plrv_sc = PyString_AsString(plrv_so);
+ size_t len = PyString_Size(plrv_so);
+ size_t size = len + VARHDRSZ;
+ bytea *result = 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);
+
+ if (get_typtype(arg->typoid) == TYPTYPE_DOMAIN)
+ domain_check(rv, false, arg->typoid, &arg->typfunc.fn_extra, arg->typfunc.fn_mcxt);
+
+ return rv;
+}
+
+/*
+ * Generic conversion function: Convert PyObject to cstring and
+ * cstring into PostgreSQL type.
+ */
+static Datum
+PLyObject_ToDatum(PLyTypeInfo *info,
+ PLyObToDatum *arg,
+ PyObject *plrv)
+{
+ PyObject *volatile plrv_so = NULL;
+ Datum rv;
+
+ Assert(plrv != Py_None);
+
+ plrv_so = PyObject_Str(plrv);
+ if (!plrv_so)
+ PLy_elog(ERROR, "could not create string representation of Python object");
+
+ PG_TRY();
+ {
+ char *plrv_sc = PyString_AsString(plrv_so);
+ size_t plen = PyString_Size(plrv_so);
+ size_t slen = strlen(plrv_sc);
+
+ if (slen < plen)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("could not convert Python object into cstring: Python string representation appears to contain null bytes")));
+ else if (slen > plen)
+ elog(ERROR, "could not convert Python object into cstring: Python string longer than reported length");
+ 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);
+
+ return rv;
+}
static HeapTuple
PLyMapping_ToTuple(PLyTypeInfo *info, PyObject *mapping)
@@ -1845,11 +1994,12 @@ PLyMapping_ToTuple(PLyTypeInfo *info, PyObject *mapping)
for (i = 0; i < desc->natts; ++i)
{
char *key;
- PyObject *volatile value,
- *volatile so;
+ PyObject *volatile value;
+ PLyObToDatum *att;
key = NameStr(desc->attrs[i]->attname);
- value = so = NULL;
+ value = NULL;
+ att = &info->out.r.atts[i];
PG_TRY();
{
value = PyMapping_GetItemString(mapping, key);
@@ -1860,19 +2010,7 @@ PLyMapping_ToTuple(PLyTypeInfo *info, PyObject *mapping)
}
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;
+ values[i] = (att->func) (info, att, value);
nulls[i] = false;
}
else
@@ -1887,7 +2025,6 @@ PLyMapping_ToTuple(PLyTypeInfo *info, PyObject *mapping)
}
PG_CATCH();
{
- Py_XDECREF(so);
Py_XDECREF(value);
PG_RE_THROW();
}
@@ -1934,10 +2071,11 @@ PLySequence_ToTuple(PLyTypeInfo *info, PyObject *sequence)
nulls = palloc(sizeof(bool) * desc->natts);
for (i = 0; i < desc->natts; ++i)
{
- PyObject *volatile value,
- *volatile so;
+ PyObject *volatile value;
+ PLyObToDatum *att;
- value = so = NULL;
+ value = NULL;
+ att = &info->out.r.atts[i];
PG_TRY();
{
value = PySequence_GetItem(sequence, i);
@@ -1949,18 +2087,7 @@ PLySequence_ToTuple(PLyTypeInfo *info, PyObject *sequence)
}
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;
+ values[i] = (att->func) (info, att, value);
nulls[i] = false;
}
@@ -1969,7 +2096,6 @@ PLySequence_ToTuple(PLyTypeInfo *info, PyObject *sequence)
}
PG_CATCH();
{
- Py_XDECREF(so);
Py_XDECREF(value);
PG_RE_THROW();
}
@@ -2005,11 +2131,12 @@ PLyObject_ToTuple(PLyTypeInfo *info, PyObject *object)
for (i = 0; i < desc->natts; ++i)
{
char *key;
- PyObject *volatile value,
- *volatile so;
+ PyObject *volatile value;
+ PLyObToDatum *att;
key = NameStr(desc->attrs[i]->attname);
- value = so = NULL;
+ value = NULL;
+ att = &info->out.r.atts[i];
PG_TRY();
{
value = PyObject_GetAttrString(object, key);
@@ -2020,18 +2147,7 @@ PLyObject_ToTuple(PLyTypeInfo *info, PyObject *object)
}
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;
+ values[i] = (att->func) (info, att, value);
nulls[i] = false;
}
else
@@ -2047,7 +2163,6 @@ PLyObject_ToTuple(PLyTypeInfo *info, PyObject *object)
}
PG_CATCH();
{
- Py_XDECREF(so);
Py_XDECREF(value);
PG_RE_THROW();
}