plpython-cstring-and-typeio-WIP.diff
text/x-patch
Filename: plpython-cstring-and-typeio-WIP.diff
Type: text/x-patch
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/pl/plpython/plpy_procedure.c | 38 | 1 |
| src/pl/plpython/plpy_typeio.c | 173 | 0 |
| src/pl/plpython/plpy_typeio.h | 3 | 0 |
diff --git a/src/pl/plpython/plpy_procedure.c b/src/pl/plpython/plpy_procedure.c
index a28cb9a..17e9dd1 100644
--- a/src/pl/plpython/plpy_procedure.c
+++ b/src/pl/plpython/plpy_procedure.c
@@ -193,6 +193,16 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("trigger functions can only be called as triggers")));
+ else if (procStruct->prorettype == CSTRINGOID){
+ /* do the real work */
+ PLy_output_datum_func(&proc->result, rvTypeTup);
+ }
+ else if (rvTypeStruct->typisdefined == false){
+ /* leave shell type as uninitialised */
+ proc->result.out.d.typoid = procStruct->prorettype;
+ proc->result.out.d.typmod = -1;
+ proc->result.is_rowtype = -1;
+ }
else if (procStruct->prorettype != VOIDOID &&
procStruct->prorettype != RECORDOID)
ereport(ERROR,
@@ -212,6 +222,10 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
proc->result.out.d.typmod = -1;
proc->result.is_rowtype = 2;
}
+ else if ((fn_oid == rvTypeStruct->typinput)||
+ (fn_oid == rvTypeStruct->typreceive)) {
+ PLy_output_datum_func_direct(&proc->result, rvTypeTup);
+ }
else
{
/* do the real work */
@@ -276,7 +290,22 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
switch (argTypeStruct->typtype)
{
case TYPTYPE_PSEUDO:
- /* Disallow pseudotype argument */
+ if ((types[i] == CSTRINGOID) ||
+ (types[i] == INTERNALOID)){
+ PLy_input_datum_func(&(proc->args[pos]),
+ types[i],
+ argTypeTup);
+ break;
+ }
+ if (argTypeStruct->typisdefined == false) {
+ /*
+ * should be safe to leave shell type uninitialised
+ * by the time the function is called, it will be a full type
+ */
+ proc->args[pos].is_rowtype = -1;
+ break;
+ }
+ /* Disallow other pseudotype arguments */
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("PL/Python functions cannot accept type %s",
@@ -287,6 +316,14 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
proc->args[pos].is_rowtype = 2;
break;
default:
+ /* type io functions do direct conversion */
+ if ((fn_oid == argTypeStruct->typoutput) ||
+ (fn_oid == argTypeStruct->typsend)) {
+ PLy_input_datum_func_direct(&(proc->args[pos]),
+ types[i],
+ argTypeTup);
+ break;
+ }
PLy_input_datum_func(&(proc->args[pos]),
types[i],
argTypeTup);
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index 8f2367d..bb09472 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -41,8 +41,11 @@ static PyObject *PLyInt_FromInt32(PLyDatumToOb *arg, Datum d);
static PyObject *PLyLong_FromInt64(PLyDatumToOb *arg, Datum d);
static PyObject *PLyLong_FromOid(PLyDatumToOb *arg, Datum d);
static PyObject *PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d);
+static PyObject *PLyString_FromCString(PLyDatumToOb *arg, Datum d);
+static PyObject *PLyLong_FromInternal(PLyDatumToOb *arg, Datum d);
static PyObject *PLyString_FromDatum(PLyDatumToOb *arg, Datum d);
static PyObject *PLyList_FromArray(PLyDatumToOb *arg, Datum d);
+static PyObject *PLyBytes_FromRawType(PLyDatumToOb *arg, Datum d);
/* conversion from Python objects to Datums */
static Datum PLyObject_ToBool(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
@@ -51,6 +54,9 @@ static Datum PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *pl
static Datum PLyObject_ToDatum(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
static Datum PLySequence_ToArray(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
+static Datum PLyBytes_ToRawType(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
+
+
/* conversion from Python objects to composite Datums (used by triggers and SRFs) */
static Datum PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string);
static Datum PLyMapping_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping);
@@ -360,6 +366,107 @@ PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv)
return datum;
}
+void
+PLy_output_datum_func_direct(PLyTypeInfo *ti, HeapTuple typeTup)
+{
+ PLyObToDatum *arg = &(ti->out.d);
+ Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
+
+ perm_fmgr_info(typeStruct->typinput, &arg->typfunc);
+ arg->typoid = HeapTupleGetOid(typeTup);
+ arg->typmod = -1;
+ arg->typioparam = getTypeIOParam(typeTup);
+ arg->typbyval = typeStruct->typbyval;
+ arg->typlen = typeStruct->typlen;
+ arg->typalign = typeStruct->typalign;
+
+ arg->func = PLyBytes_ToRawType;
+
+}
+
+/*
+ * Convert a Python bytes 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
+PLyBytes_ToRawType(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
+{
+ // copy of PLyObjecttoBytea now, will change if basic functioning is checked ok
+ PyObject *volatile plrv_so = NULL;
+ Datum rv;
+
+ Assert(plrv != Py_None);
+
+ plrv_so = PyObject_Bytes(plrv);
+ if (!plrv_so)
+ PLy_elog(ERROR, "could not create bytes representation of Python object");
+
+ PG_TRY();
+ {
+ char *plrv_sc = PyBytes_AsString(plrv_so);
+ size_t len = PyBytes_Size(plrv_so);
+ size_t size;
+ void *result;
+
+ /* return varlen types as bytea */
+ if (arg->typlen == -1) {
+ size = len + VARHDRSZ;
+ result = palloc(size);
+ SET_VARSIZE(result, size);
+ memcpy(VARDATA(result), plrv_sc, len);
+ rv = PointerGetDatum(result);
+ }
+ else {
+ size = arg->typlen;
+ if (len != size)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("PL/Python type output conversion data length mismatch"),
+ errdetail("Python length %d, PostgreSQL type length %d", (int)len, (int)size)));
+ if (arg->typbyval){
+ switch (size) {
+ case 1:
+ rv = ((Datum) SET_1_BYTE(*(uint8*)plrv_sc));
+ break;
+ case 2:
+ rv = ((Datum) SET_2_BYTES(*(int16*)plrv_sc));
+ break;
+ case 4:
+ rv = ((Datum) SET_4_BYTES(*(int32*)plrv_sc));
+ break;
+ case 8:
+ rv = ((Datum) SET_8_BYTES(*(int64*)plrv_sc));
+ break;
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("PL/Python type output conversion not supported for pass-by-value length: %d", (int)size)
+ ));
+ }
+ }
+ else {
+ result = palloc(size);
+ memcpy(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) // ToDo: this should be probably cached as arg->typtype
+ domain_check(rv, false, arg->typoid, &arg->typfunc.fn_extra, arg->typfunc.fn_mcxt);
+
+ return rv;
+}
+
static void
PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup)
{
@@ -422,6 +529,49 @@ PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup)
}
}
+/*
+ * convert PostgreSQL binary representation directly to python.
+ * This is used by types output() and send() functions which
+ * must manage the actual conversion
+ */
+
+void
+PLy_input_datum_func_direct(PLyTypeInfo *ti, Oid typeOid, HeapTuple typeTup)
+{
+ PLyDatumToOb *arg = &(ti->in.d);
+ Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
+
+ /* Get the type's conversion information */
+ perm_fmgr_info(typeStruct->typoutput, &arg->typfunc);
+ arg->typoid = HeapTupleGetOid(typeTup);
+ arg->typmod = -1;
+ arg->typioparam = getTypeIOParam(typeTup);
+ arg->typbyval = typeStruct->typbyval;
+ arg->typlen = typeStruct->typlen;
+ arg->typalign = typeStruct->typalign;
+
+ arg->func = PLyBytes_FromRawType;
+
+}
+
+/*
+ * convert postgresql datum directly to python bytes()
+ * used in type-io functions where real conversion is done in python
+ */
+static PyObject *
+PLyBytes_FromRawType(PLyDatumToOb *arg, Datum d)
+{
+ if (arg->typbyval) {
+ return PyBytes_FromStringAndSize((char*) &d, arg->typlen);
+ }
+ else {
+ bytea *var = DatumGetByteaP(d);
+ char *bytes = VARDATA(var);
+ size_t size = (arg->typlen > 0)? arg->typlen: VARSIZE(var) - VARHDRSZ;
+ return PyBytes_FromStringAndSize(bytes, size);
+ }
+}
+
static void
PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup)
{
@@ -467,6 +617,12 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup)
case BYTEAOID:
arg->func = PLyBytes_FromBytea;
break;
+ case CSTRINGOID:
+ arg->func = PLyString_FromCString;
+ break;
+ case INTERNALOID:
+ arg->func = PLyLong_FromInternal;
+ break;
default:
arg->func = PLyString_FromDatum;
break;
@@ -567,6 +723,23 @@ PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d)
}
static PyObject *
+PLyString_FromCString(PLyDatumToOb *arg, Datum d)
+{
+ const char *txt = DatumGetCString(d);
+
+ return PyBytes_FromString(txt);
+}
+
+static PyObject *
+PLyLong_FromInternal(PLyDatumToOb *arg, Datum d)
+{
+ /* convert pgsql type 'internal' to integer so it can be used by ctypes */
+ void* p = DatumGetPointer(d);
+
+ return PyLong_FromVoidPtr(p);
+}
+
+static PyObject *
PLyString_FromDatum(PLyDatumToOb *arg, Datum d)
{
char *x = OutputFunctionCall(&arg->typfunc, d);
diff --git a/src/pl/plpython/plpy_typeio.h b/src/pl/plpython/plpy_typeio.h
index 82e472a..4648ddf 100644
--- a/src/pl/plpython/plpy_typeio.h
+++ b/src/pl/plpython/plpy_typeio.h
@@ -94,6 +94,9 @@ extern void PLy_typeinfo_dealloc(PLyTypeInfo *arg);
extern void PLy_input_datum_func(PLyTypeInfo *arg, Oid typeOid, HeapTuple typeTup);
extern void PLy_output_datum_func(PLyTypeInfo *arg, HeapTuple typeTup);
+extern void PLy_input_datum_func_direct(PLyTypeInfo *arg, Oid typeOid, HeapTuple typeTup);
+extern void PLy_output_datum_func_direct(PLyTypeInfo *arg, HeapTuple typeTup);
+
extern void PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc);
extern void PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc);