mirror of
https://git.postgresql.org/git/postgresql.git
synced 2026-02-06 20:27:34 +08:00
Add transforms feature
This provides a mechanism for specifying conversions between SQL data types and procedural languages. As examples, there are transforms for hstore and ltree for PL/Perl and PL/Python. reviews by Pavel Stěhule and Andres Freund
This commit is contained in:
@ -123,54 +123,22 @@ all: all-lib
|
||||
install: all install-lib install-data
|
||||
|
||||
installdirs: installdirs-lib
|
||||
$(MKDIR_P) '$(DESTDIR)$(datadir)/extension'
|
||||
$(MKDIR_P) '$(DESTDIR)$(datadir)/extension' '$(DESTDIR)$(includedir_server)'
|
||||
|
||||
uninstall: uninstall-lib uninstall-data
|
||||
|
||||
install-data: installdirs
|
||||
$(INSTALL_DATA) $(addprefix $(srcdir)/, $(DATA)) '$(DESTDIR)$(datadir)/extension/'
|
||||
$(INSTALL_DATA) $(srcdir)/plpython.h $(srcdir)/plpy_util.h '$(DESTDIR)$(includedir_server)'
|
||||
|
||||
uninstall-data:
|
||||
rm -f $(addprefix '$(DESTDIR)$(datadir)/extension'/, $(notdir $(DATA)))
|
||||
rm -f $(addprefix '$(DESTDIR)$(includedir_server)'/, plpython.h plpy_util.h)
|
||||
|
||||
.PHONY: install-data uninstall-data
|
||||
|
||||
|
||||
ifeq ($(python_majorversion),3)
|
||||
# Adjust regression tests for Python 3 compatibility
|
||||
#
|
||||
# Mention those regression test files that need to be mangled in the
|
||||
# variable REGRESS_PLPYTHON3_MANGLE. They will be copied to a
|
||||
# subdirectory python3/ and have their Python syntax and other bits
|
||||
# adjusted to work with Python 3.
|
||||
|
||||
# Note that the order of the tests needs to be preserved in this
|
||||
# expression.
|
||||
REGRESS := $(foreach test,$(REGRESS),$(if $(filter $(test),$(REGRESS_PLPYTHON3_MANGLE)),python3/$(test),$(test)))
|
||||
|
||||
.PHONY: pgregress-python3-mangle
|
||||
pgregress-python3-mangle:
|
||||
$(MKDIR_P) sql/python3 expected/python3 results/python3
|
||||
for file in $(patsubst %,$(srcdir)/sql/%.sql,$(REGRESS_PLPYTHON3_MANGLE)) $(patsubst %,$(srcdir)/expected/%*.out,$(REGRESS_PLPYTHON3_MANGLE)); do \
|
||||
sed -e 's/except \([[:alpha:]][[:alpha:].]*\), *\([[:alpha:]][[:alpha:]]*\):/except \1 as \2:/g' \
|
||||
-e "s/<type 'exceptions\.\([[:alpha:]]*\)'>/<class '\1'>/g" \
|
||||
-e "s/<type 'long'>/<class 'int'>/g" \
|
||||
-e "s/\([0-9][0-9]*\)L/\1/g" \
|
||||
-e 's/\([ [{]\)u"/\1"/g' \
|
||||
-e "s/\([ [{]\)u'/\1'/g" \
|
||||
-e "s/def next/def __next__/g" \
|
||||
-e "s/LANGUAGE plpythonu/LANGUAGE plpython3u/g" \
|
||||
-e "s/LANGUAGE plpython2u/LANGUAGE plpython3u/g" \
|
||||
-e "s/EXTENSION plpythonu/EXTENSION plpython3u/g" \
|
||||
-e "s/EXTENSION plpython2u/EXTENSION plpython3u/g" \
|
||||
$$file >`echo $$file | sed 's,^.*/\([^/][^/]*/\)\([^/][^/]*\)$$,\1python3/\2,'` || exit; \
|
||||
done
|
||||
|
||||
check installcheck: pgregress-python3-mangle
|
||||
|
||||
pg_regress_clean_files += sql/python3/ expected/python3/ results/python3/
|
||||
|
||||
endif # Python 3
|
||||
include $(srcdir)/regress-python3-mangle.mk
|
||||
|
||||
|
||||
check: all submake
|
||||
|
||||
@ -278,6 +278,7 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
|
||||
|
||||
MemSet(&proc, 0, sizeof(PLyProcedure));
|
||||
proc.pyname = PLy_strdup("__plpython_inline_block");
|
||||
proc.langid = codeblock->langOid;
|
||||
proc.result.out.d.typoid = VOIDOID;
|
||||
|
||||
/*
|
||||
|
||||
@ -10,9 +10,12 @@
|
||||
#include "access/transam.h"
|
||||
#include "funcapi.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_proc_fn.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/hsearch.h"
|
||||
#include "utils/inval.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
#include "plpython.h"
|
||||
@ -26,6 +29,7 @@
|
||||
static HTAB *PLy_procedure_cache = NULL;
|
||||
|
||||
static PLyProcedure *PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger);
|
||||
static void invalidate_procedure_caches(Datum arg, int cacheid, uint32 hashvalue);
|
||||
static bool PLy_procedure_argument_valid(PLyTypeInfo *arg);
|
||||
static bool PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup);
|
||||
static char *PLy_procedure_munge_source(const char *name, const char *src);
|
||||
@ -41,6 +45,29 @@ init_procedure_caches(void)
|
||||
hash_ctl.entrysize = sizeof(PLyProcedureEntry);
|
||||
PLy_procedure_cache = hash_create("PL/Python procedures", 32, &hash_ctl,
|
||||
HASH_ELEM | HASH_BLOBS);
|
||||
CacheRegisterSyscacheCallback(TRFTYPELANG,
|
||||
invalidate_procedure_caches,
|
||||
(Datum) 0);
|
||||
}
|
||||
|
||||
static void
|
||||
invalidate_procedure_caches(Datum arg, int cacheid, uint32 hashvalue)
|
||||
{
|
||||
HASH_SEQ_STATUS status;
|
||||
PLyProcedureEntry *hentry;
|
||||
|
||||
Assert(PLy_procedure_cache != NULL);
|
||||
|
||||
/* flush all entries */
|
||||
hash_seq_init(&status, PLy_procedure_cache);
|
||||
|
||||
while ((hentry = (PLyProcedureEntry *) hash_seq_search(&status)))
|
||||
{
|
||||
if (hash_search(PLy_procedure_cache,
|
||||
(void *) &hentry->key,
|
||||
HASH_REMOVE, NULL) == NULL)
|
||||
elog(ERROR, "hash table corrupted");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -165,6 +192,16 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
|
||||
for (i = 0; i < FUNC_MAX_ARGS; i++)
|
||||
PLy_typeinfo_init(&proc->args[i]);
|
||||
proc->nargs = 0;
|
||||
proc->langid = procStruct->prolang;
|
||||
{
|
||||
MemoryContext oldcxt;
|
||||
|
||||
Datum protrftypes_datum = SysCacheGetAttr(PROCOID, procTup,
|
||||
Anum_pg_proc_protrftypes, &isnull);
|
||||
oldcxt = MemoryContextSwitchTo(TopMemoryContext);
|
||||
proc->trftypes = isnull ? NIL : oid_array_to_list(protrftypes_datum);
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
}
|
||||
proc->code = proc->statics = NULL;
|
||||
proc->globals = NULL;
|
||||
proc->is_setof = procStruct->proretset;
|
||||
@ -219,7 +256,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
|
||||
else
|
||||
{
|
||||
/* do the real work */
|
||||
PLy_output_datum_func(&proc->result, rvTypeTup);
|
||||
PLy_output_datum_func(&proc->result, rvTypeTup, proc->langid, proc->trftypes);
|
||||
}
|
||||
|
||||
ReleaseSysCache(rvTypeTup);
|
||||
@ -293,7 +330,9 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
|
||||
default:
|
||||
PLy_input_datum_func(&(proc->args[pos]),
|
||||
types[i],
|
||||
argTypeTup);
|
||||
argTypeTup,
|
||||
proc->langid,
|
||||
proc->trftypes);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -27,6 +27,8 @@ typedef struct PLyProcedure
|
||||
char **argnames; /* Argument names */
|
||||
PLyTypeInfo args[FUNC_MAX_ARGS];
|
||||
int nargs;
|
||||
Oid langid; /* OID of plpython pg_language entry */
|
||||
List *trftypes; /* OID list of transform types */
|
||||
PyObject *code; /* compiled procedure code */
|
||||
PyObject *statics; /* data saved across calls, local scope */
|
||||
PyObject *globals; /* data saved across calls, global scope */
|
||||
|
||||
@ -76,6 +76,7 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
|
||||
PG_TRY();
|
||||
{
|
||||
int i;
|
||||
PLyExecutionContext *exec_ctx = PLy_current_execution_context();
|
||||
|
||||
/*
|
||||
* the other loop might throw an exception, if PLyTypeInfo member
|
||||
@ -128,7 +129,7 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
|
||||
optr = NULL;
|
||||
|
||||
plan->types[i] = typeId;
|
||||
PLy_output_datum_func(&plan->args[i], typeTup);
|
||||
PLy_output_datum_func(&plan->args[i], typeTup, exec_ctx->curr_proc->langid, exec_ctx->curr_proc->trftypes);
|
||||
ReleaseSysCache(typeTup);
|
||||
}
|
||||
|
||||
|
||||
@ -29,8 +29,8 @@
|
||||
|
||||
|
||||
/* I/O function caching */
|
||||
static void PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup);
|
||||
static void PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup);
|
||||
static void PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup, Oid langid, List *trftypes);
|
||||
static void PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup, Oid langid, List *trftypes);
|
||||
|
||||
/* conversion from Datums to Python objects */
|
||||
static PyObject *PLyBool_FromBool(PLyDatumToOb *arg, Datum d);
|
||||
@ -43,6 +43,7 @@ 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_FromDatum(PLyDatumToOb *arg, Datum d);
|
||||
static PyObject *PLyObject_FromTransform(PLyDatumToOb *arg, Datum d);
|
||||
static PyObject *PLyList_FromArray(PLyDatumToOb *arg, Datum d);
|
||||
|
||||
/* conversion from Python objects to Datums */
|
||||
@ -50,6 +51,7 @@ static Datum PLyObject_ToBool(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
|
||||
static Datum PLyObject_ToBytea(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
|
||||
static Datum PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
|
||||
static Datum PLyObject_ToDatum(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
|
||||
static Datum PLyObject_ToTransform(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
|
||||
static Datum PLySequence_ToArray(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
|
||||
|
||||
/* conversion from Python objects to composite Datums (used by triggers and SRFs) */
|
||||
@ -102,27 +104,28 @@ PLy_typeinfo_dealloc(PLyTypeInfo *arg)
|
||||
* PostgreSQL, and vice versa.
|
||||
*/
|
||||
void
|
||||
PLy_input_datum_func(PLyTypeInfo *arg, Oid typeOid, HeapTuple typeTup)
|
||||
PLy_input_datum_func(PLyTypeInfo *arg, Oid typeOid, HeapTuple typeTup, Oid langid, List *trftypes)
|
||||
{
|
||||
if (arg->is_rowtype > 0)
|
||||
elog(ERROR, "PLyTypeInfo struct is initialized for Tuple");
|
||||
arg->is_rowtype = 0;
|
||||
PLy_input_datum_func2(&(arg->in.d), typeOid, typeTup);
|
||||
PLy_input_datum_func2(&(arg->in.d), typeOid, typeTup, langid, trftypes);
|
||||
}
|
||||
|
||||
void
|
||||
PLy_output_datum_func(PLyTypeInfo *arg, HeapTuple typeTup)
|
||||
PLy_output_datum_func(PLyTypeInfo *arg, HeapTuple typeTup, Oid langid, List *trftypes)
|
||||
{
|
||||
if (arg->is_rowtype > 0)
|
||||
elog(ERROR, "PLyTypeInfo struct is initialized for a Tuple");
|
||||
arg->is_rowtype = 0;
|
||||
PLy_output_datum_func2(&(arg->out.d), typeTup);
|
||||
PLy_output_datum_func2(&(arg->out.d), typeTup, langid, trftypes);
|
||||
}
|
||||
|
||||
void
|
||||
PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
|
||||
{
|
||||
int i;
|
||||
PLyExecutionContext *exec_ctx = PLy_current_execution_context();
|
||||
|
||||
if (arg->is_rowtype == 0)
|
||||
elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
|
||||
@ -181,7 +184,9 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
|
||||
|
||||
PLy_input_datum_func2(&(arg->in.r.atts[i]),
|
||||
desc->attrs[i]->atttypid,
|
||||
typeTup);
|
||||
typeTup,
|
||||
exec_ctx->curr_proc->langid,
|
||||
exec_ctx->curr_proc->trftypes);
|
||||
|
||||
ReleaseSysCache(typeTup);
|
||||
}
|
||||
@ -191,6 +196,7 @@ void
|
||||
PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
|
||||
{
|
||||
int i;
|
||||
PLyExecutionContext *exec_ctx = PLy_current_execution_context();
|
||||
|
||||
if (arg->is_rowtype == 0)
|
||||
elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
|
||||
@ -243,7 +249,9 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
|
||||
elog(ERROR, "cache lookup failed for type %u",
|
||||
desc->attrs[i]->atttypid);
|
||||
|
||||
PLy_output_datum_func2(&(arg->out.r.atts[i]), typeTup);
|
||||
PLy_output_datum_func2(&(arg->out.r.atts[i]), typeTup,
|
||||
exec_ctx->curr_proc->langid,
|
||||
exec_ctx->curr_proc->trftypes);
|
||||
|
||||
ReleaseSysCache(typeTup);
|
||||
}
|
||||
@ -362,10 +370,12 @@ PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv)
|
||||
}
|
||||
|
||||
static void
|
||||
PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup)
|
||||
PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup, Oid langid, List *trftypes)
|
||||
{
|
||||
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
|
||||
Oid element_type;
|
||||
Oid base_type;
|
||||
Oid funcid;
|
||||
|
||||
perm_fmgr_info(typeStruct->typinput, &arg->typfunc);
|
||||
arg->typoid = HeapTupleGetOid(typeTup);
|
||||
@ -374,12 +384,24 @@ PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup)
|
||||
arg->typbyval = typeStruct->typbyval;
|
||||
|
||||
element_type = get_base_element_type(arg->typoid);
|
||||
base_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.
|
||||
* datums.
|
||||
*/
|
||||
switch (getBaseType(element_type ? element_type : arg->typoid))
|
||||
|
||||
if ((funcid = get_transform_tosql(base_type, langid, trftypes)))
|
||||
{
|
||||
arg->func = PLyObject_ToTransform;
|
||||
perm_fmgr_info(funcid, &arg->typtransform);
|
||||
}
|
||||
else if (typeStruct->typtype == TYPTYPE_COMPOSITE)
|
||||
{
|
||||
arg->func = PLyObject_ToComposite;
|
||||
}
|
||||
else
|
||||
switch (base_type)
|
||||
{
|
||||
case BOOLOID:
|
||||
arg->func = PLyObject_ToBool;
|
||||
@ -392,12 +414,6 @@ PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Composite types need their own input routine, though */
|
||||
if (typeStruct->typtype == TYPTYPE_COMPOSITE)
|
||||
{
|
||||
arg->func = PLyObject_ToComposite;
|
||||
}
|
||||
|
||||
if (element_type)
|
||||
{
|
||||
char dummy_delim;
|
||||
@ -408,6 +424,7 @@ PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup)
|
||||
|
||||
arg->elm = PLy_malloc0(sizeof(*arg->elm));
|
||||
arg->elm->func = arg->func;
|
||||
arg->elm->typtransform = arg->typtransform;
|
||||
arg->func = PLySequence_ToArray;
|
||||
|
||||
arg->elm->typoid = element_type;
|
||||
@ -420,12 +437,12 @@ PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup)
|
||||
}
|
||||
|
||||
static void
|
||||
PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup)
|
||||
PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup, Oid langid, List *trftypes)
|
||||
{
|
||||
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
|
||||
|
||||
/* It's safe to handle domains of array types as its base array type. */
|
||||
Oid element_type = get_base_element_type(typeOid);
|
||||
Oid element_type;
|
||||
Oid base_type;
|
||||
Oid funcid;
|
||||
|
||||
/* Get the type's conversion information */
|
||||
perm_fmgr_info(typeStruct->typoutput, &arg->typfunc);
|
||||
@ -437,7 +454,17 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup)
|
||||
arg->typalign = typeStruct->typalign;
|
||||
|
||||
/* Determine which kind of Python object we will convert to */
|
||||
switch (getBaseType(element_type ? element_type : typeOid))
|
||||
|
||||
element_type = get_base_element_type(typeOid);
|
||||
base_type = getBaseType(element_type ? element_type : typeOid);
|
||||
|
||||
if ((funcid = get_transform_fromsql(base_type, langid, trftypes)))
|
||||
{
|
||||
arg->func = PLyObject_FromTransform;
|
||||
perm_fmgr_info(funcid, &arg->typtransform);
|
||||
}
|
||||
else
|
||||
switch (base_type)
|
||||
{
|
||||
case BOOLOID:
|
||||
arg->func = PLyBool_FromBool;
|
||||
@ -478,6 +505,7 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup)
|
||||
|
||||
arg->elm = PLy_malloc0(sizeof(*arg->elm));
|
||||
arg->elm->func = arg->func;
|
||||
arg->elm->typtransform = arg->typtransform;
|
||||
arg->func = PLyList_FromArray;
|
||||
arg->elm->typoid = element_type;
|
||||
arg->elm->typmod = -1;
|
||||
@ -596,6 +624,12 @@ PLyString_FromDatum(PLyDatumToOb *arg, Datum d)
|
||||
return r;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
PLyObject_FromTransform(PLyDatumToOb *arg, Datum d)
|
||||
{
|
||||
return (PyObject *) DatumGetPointer(FunctionCall1(&arg->typtransform, d));
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
PLyList_FromArray(PLyDatumToOb *arg, Datum d)
|
||||
{
|
||||
@ -747,16 +781,15 @@ PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
|
||||
|
||||
|
||||
/*
|
||||
* Generic conversion function: Convert PyObject to cstring and
|
||||
* cstring into PostgreSQL type.
|
||||
* Convert Python object to C string in server encoding.
|
||||
*/
|
||||
static Datum
|
||||
PLyObject_ToDatum(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
|
||||
char *
|
||||
PLyObject_AsString(PyObject *plrv)
|
||||
{
|
||||
PyObject *volatile plrv_bo = NULL;
|
||||
Datum rv;
|
||||
|
||||
Assert(plrv != Py_None);
|
||||
PyObject *plrv_bo;
|
||||
char *plrv_sc;
|
||||
size_t plen;
|
||||
size_t slen;
|
||||
|
||||
if (PyUnicode_Check(plrv))
|
||||
plrv_bo = PLyUnicode_Bytes(plrv);
|
||||
@ -786,36 +819,47 @@ PLyObject_ToDatum(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
|
||||
if (!plrv_bo)
|
||||
PLy_elog(ERROR, "could not create string representation of Python object");
|
||||
|
||||
PG_TRY();
|
||||
{
|
||||
char *plrv_sc = PyBytes_AsString(plrv_bo);
|
||||
size_t plen = PyBytes_Size(plrv_bo);
|
||||
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");
|
||||
pg_verifymbstr(plrv_sc, slen, false);
|
||||
rv = InputFunctionCall(&arg->typfunc,
|
||||
plrv_sc,
|
||||
arg->typioparam,
|
||||
typmod);
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
Py_XDECREF(plrv_bo);
|
||||
PG_RE_THROW();
|
||||
}
|
||||
PG_END_TRY();
|
||||
plrv_sc = pstrdup(PyBytes_AsString(plrv_bo));
|
||||
plen = PyBytes_Size(plrv_bo);
|
||||
slen = strlen(plrv_sc);
|
||||
|
||||
Py_XDECREF(plrv_bo);
|
||||
|
||||
return rv;
|
||||
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");
|
||||
pg_verifymbstr(plrv_sc, slen, false);
|
||||
|
||||
return plrv_sc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generic conversion function: Convert PyObject to cstring and
|
||||
* cstring into PostgreSQL type.
|
||||
*/
|
||||
static Datum
|
||||
PLyObject_ToDatum(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
|
||||
{
|
||||
Assert(plrv != Py_None);
|
||||
|
||||
return InputFunctionCall(&arg->typfunc,
|
||||
PLyObject_AsString(plrv),
|
||||
arg->typioparam,
|
||||
typmod);
|
||||
}
|
||||
|
||||
|
||||
static Datum
|
||||
PLyObject_ToTransform(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
|
||||
{
|
||||
return FunctionCall1(&arg->typtransform, PointerGetDatum(plrv));
|
||||
}
|
||||
|
||||
|
||||
static Datum
|
||||
PLySequence_ToArray(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
|
||||
{
|
||||
@ -869,12 +913,15 @@ static Datum
|
||||
PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string)
|
||||
{
|
||||
HeapTuple typeTup;
|
||||
PLyExecutionContext *exec_ctx = PLy_current_execution_context();
|
||||
|
||||
typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(desc->tdtypeid));
|
||||
if (!HeapTupleIsValid(typeTup))
|
||||
elog(ERROR, "cache lookup failed for type %u", desc->tdtypeid);
|
||||
|
||||
PLy_output_datum_func2(&info->out.d, typeTup);
|
||||
PLy_output_datum_func2(&info->out.d, typeTup,
|
||||
exec_ctx->curr_proc->langid,
|
||||
exec_ctx->curr_proc->trftypes);
|
||||
|
||||
ReleaseSysCache(typeTup);
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ typedef struct PLyDatumToOb
|
||||
{
|
||||
PLyDatumToObFunc func;
|
||||
FmgrInfo typfunc; /* The type's output function */
|
||||
FmgrInfo typtransform; /* from-SQL transform */
|
||||
Oid typoid; /* The OID of the type */
|
||||
int32 typmod; /* The typmod of the type */
|
||||
Oid typioparam;
|
||||
@ -48,6 +49,7 @@ typedef struct PLyObToDatum
|
||||
{
|
||||
PLyObToDatumFunc func;
|
||||
FmgrInfo typfunc; /* The type's input function */
|
||||
FmgrInfo typtransform; /* to-SQL transform */
|
||||
Oid typoid; /* The OID of the type */
|
||||
int32 typmod; /* The typmod of the type */
|
||||
Oid typioparam;
|
||||
@ -91,8 +93,8 @@ typedef struct PLyTypeInfo
|
||||
extern void PLy_typeinfo_init(PLyTypeInfo *arg);
|
||||
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(PLyTypeInfo *arg, Oid typeOid, HeapTuple typeTup, Oid langid, List *trftypes);
|
||||
extern void PLy_output_datum_func(PLyTypeInfo *arg, HeapTuple typeTup, Oid langid, List *trftypes);
|
||||
|
||||
extern void PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc);
|
||||
extern void PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc);
|
||||
@ -105,4 +107,7 @@ extern Datum PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObj
|
||||
/* conversion from heap tuples to Python dictionaries */
|
||||
extern PyObject *PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc);
|
||||
|
||||
/* conversion from Python objects to C strings */
|
||||
extern char *PLyObject_AsString(PyObject *plrv);
|
||||
|
||||
#endif /* PLPY_TYPEIO_H */
|
||||
|
||||
@ -142,19 +142,30 @@ PLyUnicode_AsString(PyObject *unicode)
|
||||
* unicode object. Reference ownership is passed to the caller.
|
||||
*/
|
||||
PyObject *
|
||||
PLyUnicode_FromString(const char *s)
|
||||
PLyUnicode_FromStringAndSize(const char *s, Py_ssize_t size)
|
||||
{
|
||||
char *utf8string;
|
||||
PyObject *o;
|
||||
|
||||
utf8string = pg_server_to_any(s, strlen(s), PG_UTF8);
|
||||
utf8string = pg_server_to_any(s, size, PG_UTF8);
|
||||
|
||||
o = PyUnicode_FromString(utf8string);
|
||||
|
||||
if (utf8string != s)
|
||||
if (utf8string == s)
|
||||
{
|
||||
o = PyUnicode_FromStringAndSize(s, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
o = PyUnicode_FromString(utf8string);
|
||||
pfree(utf8string);
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PLyUnicode_FromString(const char *s)
|
||||
{
|
||||
return PLyUnicode_FromStringAndSize(s, strlen(s));
|
||||
}
|
||||
|
||||
#endif /* PY_MAJOR_VERSION >= 3 */
|
||||
|
||||
@ -16,6 +16,7 @@ extern char *PLyUnicode_AsString(PyObject *unicode);
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
extern PyObject *PLyUnicode_FromString(const char *s);
|
||||
extern PyObject *PLyUnicode_FromStringAndSize(const char *s, Py_ssize_t size);
|
||||
#endif
|
||||
|
||||
#endif /* PLPY_UTIL_H */
|
||||
|
||||
@ -91,6 +91,7 @@ typedef int Py_ssize_t;
|
||||
#define PyString_Check(x) 0
|
||||
#define PyString_AsString(x) PLyUnicode_AsString(x)
|
||||
#define PyString_FromString(x) PLyUnicode_FromString(x)
|
||||
#define PyString_FromStringAndSize(x, size) PLyUnicode_FromStringAndSize(x, size)
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
||||
35
src/pl/plpython/regress-python3-mangle.mk
Normal file
35
src/pl/plpython/regress-python3-mangle.mk
Normal file
@ -0,0 +1,35 @@
|
||||
ifeq ($(python_majorversion),3)
|
||||
# Adjust regression tests for Python 3 compatibility
|
||||
#
|
||||
# Mention those regression test files that need to be mangled in the
|
||||
# variable REGRESS_PLPYTHON3_MANGLE. They will be copied to a
|
||||
# subdirectory python3/ and have their Python syntax and other bits
|
||||
# adjusted to work with Python 3.
|
||||
|
||||
# Note that the order of the tests needs to be preserved in this
|
||||
# expression.
|
||||
REGRESS := $(foreach test,$(REGRESS),$(if $(filter $(test),$(REGRESS_PLPYTHON3_MANGLE)),python3/$(test),$(test)))
|
||||
|
||||
.PHONY: pgregress-python3-mangle
|
||||
pgregress-python3-mangle:
|
||||
$(MKDIR_P) sql/python3 expected/python3 results/python3
|
||||
for file in $(patsubst %,$(srcdir)/sql/%.sql,$(REGRESS_PLPYTHON3_MANGLE)) $(patsubst %,$(srcdir)/expected/%*.out,$(REGRESS_PLPYTHON3_MANGLE)); do \
|
||||
sed -e 's/except \([[:alpha:]][[:alpha:].]*\), *\([[:alpha:]][[:alpha:]]*\):/except \1 as \2:/g' \
|
||||
-e "s/<type 'exceptions\.\([[:alpha:]]*\)'>/<class '\1'>/g" \
|
||||
-e "s/<type 'long'>/<class 'int'>/g" \
|
||||
-e "s/\([0-9][0-9]*\)L/\1/g" \
|
||||
-e 's/\([ [{]\)u"/\1"/g' \
|
||||
-e "s/\([ [{]\)u'/\1'/g" \
|
||||
-e "s/def next/def __next__/g" \
|
||||
-e "s/LANGUAGE plpythonu/LANGUAGE plpython3u/g" \
|
||||
-e "s/LANGUAGE plpython2u/LANGUAGE plpython3u/g" \
|
||||
-e "s/EXTENSION \([^ ]*_\)*plpythonu/EXTENSION \1plpython3u/g" \
|
||||
-e "s/EXTENSION \([^ ]*_\)*plpython2u/EXTENSION \1plpython3u/g" \
|
||||
$$file >`echo $$file | sed 's,^.*/\([^/][^/]*/\)\([^/][^/]*\)$$,\1python3/\2,'` || exit; \
|
||||
done
|
||||
|
||||
check installcheck: pgregress-python3-mangle
|
||||
|
||||
pg_regress_clean_files += sql/python3/ expected/python3/ results/python3/
|
||||
|
||||
endif # Python 3
|
||||
Reference in New Issue
Block a user