Commit 147c2482 authored by Peter Eisentraut's avatar Peter Eisentraut

Split plpython.c into smaller pieces

This moves the code around from one huge file into hopefully logical
and more manageable modules.  For the most part, the code itself was
not touched, except: PLy_function_handler and PLy_trigger_handler were
renamed to PLy_exec_function and PLy_exec_trigger, because they were
not actually handlers in the PL handler sense, and it makes the naming
more similar to the way PL/pgSQL is organized.  The initialization of
the procedure caches was separated into a new function
init_procedure_caches to keep the hash tables private to
plpy_procedures.c.

Jan Urbański and Peter Eisentraut
parent 59e242a4
......@@ -38,7 +38,19 @@ rpathdir = $(python_libdir)
NAME = plpython$(python_majorversion)
OBJS = plpython.o
OBJS = \
plpy_cursorobject.o \
plpy_elog.o \
plpy_exec.o \
plpy_main.o \
plpy_planobject.o \
plpy_plpymodule.o \
plpy_procedure.o \
plpy_resultobject.o \
plpy_spi.o \
plpy_subxactobject.o \
plpy_typeio.o \
plpy_util.o
DATA = $(NAME)u.control $(NAME)u--1.0.sql $(NAME)u--unpackaged--1.0.sql
ifeq ($(python_majorversion),2)
......@@ -177,7 +189,7 @@ endif # can't build
# distprep and maintainer-clean rules should be run even if we can't build.
# Force this dependency to be known even without dependency info built:
plpython.o: spiexceptions.h
plpython_plpy.o: spiexceptions.h
spiexceptions.h: $(top_srcdir)/src/backend/utils/errcodes.txt generate-spiexceptions.pl
$(PERL) $(srcdir)/generate-spiexceptions.pl $< > $@
......
# src/pl/plpython/nls.mk
CATALOG_NAME = plpython
AVAIL_LANGUAGES = de es fr it ja pt_BR ro tr zh_CN zh_TW
GETTEXT_FILES = plpython.c
GETTEXT_FILES = plpy_cursorobject.c plpy_elog.c plpy_exec.c plpy_main.c plpy_planobject.c plpy_plpymodule.c \
plpy_procedure.c plpy_resultobject.c plpy_spi.c plpy_subxactobject.c plpy_typeio.c plpy_util.c
GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) PLy_elog:2 PLy_exception_set:2 PLy_exception_set_plural:2,3
GETTEXT_FLAGS = $(BACKEND_COMMON_GETTEXT_FLAGS) \
PLy_elog:2:c-format \
......
This diff is collapsed.
/*
* src/pl/plpython/plpy_cursorobject.h
*/
#ifndef PLPY_CURSOROBJECT_H
#define PLPY_CURSOROBJECT_H
#include "plpy_typeio.h"
typedef struct PLyCursorObject
{
PyObject_HEAD
char *portalname;
PLyTypeInfo result;
bool closed;
} PLyCursorObject;
extern void PLy_cursor_init_type(void);
extern PyObject *PLy_cursor(PyObject *, PyObject *);
#endif /* PLPY_CURSOROBJECT_H */
This diff is collapsed.
/*
* src/pl/plpython/plpy_elog.h
*/
#ifndef PLPY_ELOG_H
#define PLPY_ELOG_H
/* global exception classes */
extern PyObject *PLy_exc_error;
extern PyObject *PLy_exc_fatal;
extern PyObject *PLy_exc_spi_error;
extern void PLy_elog(int, const char *,...)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
extern void PLy_exception_set(PyObject *, const char *,...)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
extern void PLy_exception_set_plural(PyObject *, const char *, const char *,
unsigned long n,...)
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 5)))
__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 5)));
#endif /* PLPY_ELOG_H */
This diff is collapsed.
/*
* src/pl/plpython/plpy_exec.h
*/
#ifndef PLPY_EXEC_H
#define PLPY_EXEC_H
#include "plpy_procedure.h"
extern Datum PLy_exec_function(FunctionCallInfo, PLyProcedure *);
extern HeapTuple PLy_exec_trigger(FunctionCallInfo, PLyProcedure *);
#endif /* PLPY_EXEC_H */
/*
* PL/Python main entry points
*
* src/pl/plpython/plpy_main.c
*/
#include "postgres.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "miscadmin.h"
#include "utils/guc.h"
#include "utils/syscache.h"
#include "plpython.h"
#include "plpy_main.h"
#include "plpy_elog.h"
#include "plpy_exec.h"
#include "plpy_plpymodule.h"
#include "plpy_procedure.h"
#include "plpy_subxactobject.h"
/*
* exported functions
*/
#if PY_MAJOR_VERSION >= 3
/* Use separate names to avoid clash in pg_pltemplate */
#define plpython_validator plpython3_validator
#define plpython_call_handler plpython3_call_handler
#define plpython_inline_handler plpython3_inline_handler
#endif
extern void _PG_init(void);
extern Datum plpython_validator(PG_FUNCTION_ARGS);
extern Datum plpython_call_handler(PG_FUNCTION_ARGS);
extern Datum plpython_inline_handler(PG_FUNCTION_ARGS);
#if PY_MAJOR_VERSION < 3
/* Define aliases plpython2_call_handler etc */
extern Datum plpython2_validator(PG_FUNCTION_ARGS);
extern Datum plpython2_call_handler(PG_FUNCTION_ARGS);
extern Datum plpython2_inline_handler(PG_FUNCTION_ARGS);
#endif
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(plpython_validator);
PG_FUNCTION_INFO_V1(plpython_call_handler);
PG_FUNCTION_INFO_V1(plpython_inline_handler);
#if PY_MAJOR_VERSION < 3
PG_FUNCTION_INFO_V1(plpython2_validator);
PG_FUNCTION_INFO_V1(plpython2_call_handler);
PG_FUNCTION_INFO_V1(plpython2_inline_handler);
#endif
static bool PLy_procedure_is_trigger(Form_pg_proc);
static void plpython_error_callback(void *);
static void plpython_inline_error_callback(void *);
static void PLy_init_interp(void);
static const int plpython_python_version = PY_MAJOR_VERSION;
/* initialize global variables */
PyObject *PLy_interp_globals = NULL;
void
_PG_init(void)
{
/* Be sure we do initialization only once (should be redundant now) */
static bool inited = false;
const int **version_ptr;
if (inited)
return;
/* Be sure we don't run Python 2 and 3 in the same session (might crash) */
version_ptr = (const int **) find_rendezvous_variable("plpython_python_version");
if (!(*version_ptr))
*version_ptr = &plpython_python_version;
else
{
if (**version_ptr != plpython_python_version)
ereport(FATAL,
(errmsg("Python major version mismatch in session"),
errdetail("This session has previously used Python major version %d, and it is now attempting to use Python major version %d.",
**version_ptr, plpython_python_version),
errhint("Start a new session to use a different Python major version.")));
}
pg_bindtextdomain(TEXTDOMAIN);
#if PY_MAJOR_VERSION >= 3
PyImport_AppendInittab("plpy", PyInit_plpy);
#endif
Py_Initialize();
#if PY_MAJOR_VERSION >= 3
PyImport_ImportModule("plpy");
#endif
PLy_init_interp();
PLy_init_plpy();
if (PyErr_Occurred())
PLy_elog(FATAL, "untrapped error in initialization");
init_procedure_caches();
explicit_subtransactions = NIL;
inited = true;
}
/*
* This should only be called once from _PG_init. Initialize the Python
* interpreter and global data.
*/
void
PLy_init_interp(void)
{
static PyObject *PLy_interp_safe_globals = NULL;
PyObject *mainmod;
mainmod = PyImport_AddModule("__main__");
if (mainmod == NULL || PyErr_Occurred())
PLy_elog(ERROR, "could not import \"__main__\" module");
Py_INCREF(mainmod);
PLy_interp_globals = PyModule_GetDict(mainmod);
PLy_interp_safe_globals = PyDict_New();
PyDict_SetItemString(PLy_interp_globals, "GD", PLy_interp_safe_globals);
Py_DECREF(mainmod);
if (PLy_interp_globals == NULL || PyErr_Occurred())
PLy_elog(ERROR, "could not initialize globals");
}
Datum
plpython_validator(PG_FUNCTION_ARGS)
{
Oid funcoid = PG_GETARG_OID(0);
HeapTuple tuple;
Form_pg_proc procStruct;
bool is_trigger;
if (!check_function_bodies)
{
PG_RETURN_VOID();
}
/* Get the new function's pg_proc entry */
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for function %u", funcoid);
procStruct = (Form_pg_proc) GETSTRUCT(tuple);
is_trigger = PLy_procedure_is_trigger(procStruct);
ReleaseSysCache(tuple);
PLy_procedure_get(funcoid, is_trigger);
PG_RETURN_VOID();
}
#if PY_MAJOR_VERSION < 3
Datum
plpython2_validator(PG_FUNCTION_ARGS)
{
return plpython_validator(fcinfo);
}
#endif /* PY_MAJOR_VERSION < 3 */
Datum
plpython_call_handler(PG_FUNCTION_ARGS)
{
Datum retval;
PLyProcedure *save_curr_proc;
ErrorContextCallback plerrcontext;
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed");
save_curr_proc = PLy_curr_procedure;
/*
* Setup error traceback support for ereport()
*/
plerrcontext.callback = plpython_error_callback;
plerrcontext.previous = error_context_stack;
error_context_stack = &plerrcontext;
PG_TRY();
{
PLyProcedure *proc;
if (CALLED_AS_TRIGGER(fcinfo))
{
HeapTuple trv;
proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, true);
PLy_curr_procedure = proc;
trv = PLy_exec_trigger(fcinfo, proc);
retval = PointerGetDatum(trv);
}
else
{
proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, false);
PLy_curr_procedure = proc;
retval = PLy_exec_function(fcinfo, proc);
}
}
PG_CATCH();
{
PLy_curr_procedure = save_curr_proc;
PyErr_Clear();
PG_RE_THROW();
}
PG_END_TRY();
/* Pop the error context stack */
error_context_stack = plerrcontext.previous;
PLy_curr_procedure = save_curr_proc;
return retval;
}
#if PY_MAJOR_VERSION < 3
Datum
plpython2_call_handler(PG_FUNCTION_ARGS)
{
return plpython_call_handler(fcinfo);
}
#endif /* PY_MAJOR_VERSION < 3 */
Datum
plpython_inline_handler(PG_FUNCTION_ARGS)
{
InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
FunctionCallInfoData fake_fcinfo;
FmgrInfo flinfo;
PLyProcedure *save_curr_proc;
PLyProcedure proc;
ErrorContextCallback plerrcontext;
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed");
save_curr_proc = PLy_curr_procedure;
/*
* Setup error traceback support for ereport()
*/
plerrcontext.callback = plpython_inline_error_callback;
plerrcontext.previous = error_context_stack;
error_context_stack = &plerrcontext;
MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
MemSet(&flinfo, 0, sizeof(flinfo));
fake_fcinfo.flinfo = &flinfo;
flinfo.fn_oid = InvalidOid;
flinfo.fn_mcxt = CurrentMemoryContext;
MemSet(&proc, 0, sizeof(PLyProcedure));
proc.pyname = PLy_strdup("__plpython_inline_block");
proc.result.out.d.typoid = VOIDOID;
PG_TRY();
{
PLy_procedure_compile(&proc, codeblock->source_text);
PLy_curr_procedure = &proc;
PLy_exec_function(&fake_fcinfo, &proc);
}
PG_CATCH();
{
PLy_procedure_delete(&proc);
PLy_curr_procedure = save_curr_proc;
PyErr_Clear();
PG_RE_THROW();
}
PG_END_TRY();
PLy_procedure_delete(&proc);
/* Pop the error context stack */
error_context_stack = plerrcontext.previous;
PLy_curr_procedure = save_curr_proc;
PG_RETURN_VOID();
}
#if PY_MAJOR_VERSION < 3
Datum
plpython2_inline_handler(PG_FUNCTION_ARGS)
{
return plpython_inline_handler(fcinfo);
}
#endif /* PY_MAJOR_VERSION < 3 */
static bool PLy_procedure_is_trigger(Form_pg_proc procStruct)
{
return (procStruct->prorettype == TRIGGEROID ||
(procStruct->prorettype == OPAQUEOID &&
procStruct->pronargs == 0));
}
static void
plpython_error_callback(void *arg)
{
if (PLy_curr_procedure)
errcontext("PL/Python function \"%s\"",
PLy_procedure_name(PLy_curr_procedure));
}
static void
plpython_inline_error_callback(void *arg)
{
errcontext("PL/Python anonymous code block");
}
/*
* src/pl/plpython/plpy_main.h
*/
#ifndef PLPY_MAIN_H
#define PLPY_MAIN_H
#include "plpy_procedure.h"
/* the interpreter's globals dict */
extern PyObject *PLy_interp_globals;
#endif /* PLPY_MAIN_H */
/*
* the PLyPlan class
*
* src/pl/plpython/plpy_planobject.c
*/
#include "postgres.h"
#include "plpython.h"
#include "plpy_planobject.h"
#include "plpy_elog.h"
static void PLy_plan_dealloc(PyObject *);
static PyObject *PLy_plan_status(PyObject *, PyObject *);
static char PLy_plan_doc[] = {
"Store a PostgreSQL plan"
};
static PyMethodDef PLy_plan_methods[] = {
{"status", PLy_plan_status, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL}
};
static PyTypeObject PLy_PlanType = {
PyVarObject_HEAD_INIT(NULL, 0)
"PLyPlan", /* tp_name */
sizeof(PLyPlanObject), /* tp_size */
0, /* tp_itemsize */
/*
* methods
*/
PLy_plan_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
PLy_plan_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
PLy_plan_methods, /* tp_tpmethods */
};
void
PLy_plan_init_type(void)
{
if (PyType_Ready(&PLy_PlanType) < 0)
elog(ERROR, "could not initialize PLy_PlanType");
}
PyObject *
PLy_plan_new(void)
{
PLyPlanObject *ob;
if ((ob = PyObject_New(PLyPlanObject, &PLy_PlanType)) == NULL)
return NULL;
ob->plan = NULL;
ob->nargs = 0;
ob->types = NULL;
ob->values = NULL;
ob->args = NULL;
return (PyObject *) ob;
}
bool
is_PLyPlanObject(PyObject *ob)
{
return ob->ob_type == &PLy_PlanType;
}
static void
PLy_plan_dealloc(PyObject *arg)
{
PLyPlanObject *ob = (PLyPlanObject *) arg;
if (ob->plan)
SPI_freeplan(ob->plan);
if (ob->types)
PLy_free(ob->types);
if (ob->values)
PLy_free(ob->values);
if (ob->args)
{
int i;
for (i = 0; i < ob->nargs; i++)
PLy_typeinfo_dealloc(&ob->args[i]);
PLy_free(ob->args);
}
arg->ob_type->tp_free(arg);
}
static PyObject *
PLy_plan_status(PyObject *self, PyObject *args)
{
if (PyArg_ParseTuple(args, ""))
{
Py_INCREF(Py_True);
return Py_True;
/* return PyInt_FromLong(self->status); */
}
PLy_exception_set(PLy_exc_error, "plan.status takes no arguments");
return NULL;
}
/*
* src/pl/plpython/plpy_planobject.h
*/
#ifndef PLPY_PLANOBJECT_H
#define PLPY_PLANOBJECT_H
#include "executor/spi.h"
#include "plpy_typeio.h"
typedef struct PLyPlanObject
{
PyObject_HEAD
SPIPlanPtr plan;
int nargs;
Oid *types;
Datum *values;
PLyTypeInfo *args;
} PLyPlanObject;
extern void PLy_plan_init_type(void);
extern PyObject *PLy_plan_new(void);
extern bool is_PLyPlanObject(PyObject *);
#endif /* PLPY_PLANOBJECT_H */
/*
* the plpy module
*
* src/pl/plpython/plpy_plpymodule.c
*/
#include "postgres.h"
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
#include "plpython.h"
#include "plpy_plpymodule.h"
#include "plpy_cursorobject.h"
#include "plpy_elog.h"
#include "plpy_planobject.h"
#include "plpy_resultobject.h"
#include "plpy_spi.h"
#include "plpy_subxactobject.h"
HTAB *PLy_spi_exceptions = NULL;
static void PLy_add_exceptions(PyObject *);
static void PLy_generate_spi_exceptions(PyObject *, PyObject *);
/* module functions */
static PyObject *PLy_debug(PyObject *, PyObject *);
static PyObject *PLy_log(PyObject *, PyObject *);
static PyObject *PLy_info(PyObject *, PyObject *);
static PyObject *PLy_notice(PyObject *, PyObject *);
static PyObject *PLy_warning(PyObject *, PyObject *);
static PyObject *PLy_error(PyObject *, PyObject *);
static PyObject *PLy_fatal(PyObject *, PyObject *);
static PyObject *PLy_quote_literal(PyObject *, PyObject *);
static PyObject *PLy_quote_nullable(PyObject *, PyObject *);
static PyObject *PLy_quote_ident(PyObject *, PyObject *);
/* A list of all known exceptions, generated from backend/utils/errcodes.txt */
typedef struct ExceptionMap
{
char *name;
char *classname;
int sqlstate;
} ExceptionMap;
static const ExceptionMap exception_map[] = {
#include "spiexceptions.h"
{NULL, NULL, 0}
};
static PyMethodDef PLy_methods[] = {
/*
* logging methods
*/
{"debug", PLy_debug, METH_VARARGS, NULL},
{"log", PLy_log, METH_VARARGS, NULL},
{"info", PLy_info, METH_VARARGS, NULL},
{"notice", PLy_notice, METH_VARARGS, NULL},
{"warning", PLy_warning, METH_VARARGS, NULL},
{"error", PLy_error, METH_VARARGS, NULL},
{"fatal", PLy_fatal, METH_VARARGS, NULL},
/*
* create a stored plan
*/
{"prepare", PLy_spi_prepare, METH_VARARGS, NULL},
/*
* execute a plan or query
*/
{"execute", PLy_spi_execute, METH_VARARGS, NULL},
/*
* escaping strings
*/
{"quote_literal", PLy_quote_literal, METH_VARARGS, NULL},
{"quote_nullable", PLy_quote_nullable, METH_VARARGS, NULL},
{"quote_ident", PLy_quote_ident, METH_VARARGS, NULL},
/*
* create the subtransaction context manager
*/
{"subtransaction", PLy_subtransaction_new, METH_NOARGS, NULL},
/*
* create a cursor
*/
{"cursor", PLy_cursor, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL}
};
static PyMethodDef PLy_exc_methods[] = {
{NULL, NULL, 0, NULL}
};
#if PY_MAJOR_VERSION >= 3
static PyModuleDef PLy_module = {
PyModuleDef_HEAD_INIT, /* m_base */
"plpy", /* m_name */
NULL, /* m_doc */
-1, /* m_size */
PLy_methods, /* m_methods */
};
static PyModuleDef PLy_exc_module = {
PyModuleDef_HEAD_INIT, /* m_base */
"spiexceptions", /* m_name */
NULL, /* m_doc */
-1, /* m_size */
PLy_exc_methods, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL /* m_free */
};
/*
* Must have external linkage, because PyMODINIT_FUNC does dllexport on
* Windows-like platforms.
*/
PyMODINIT_FUNC
PyInit_plpy(void)
{
PyObject *m;
m = PyModule_Create(&PLy_module);
if (m == NULL)
return NULL;
PLy_add_exceptions(m);
return m;
}
#endif /* PY_MAJOR_VERSION >= 3 */
void
PLy_init_plpy(void)
{
PyObject *main_mod,
*main_dict,
*plpy_mod;
#if PY_MAJOR_VERSION < 3
PyObject *plpy;
#endif
/*
* initialize plpy module
*/
PLy_plan_init_type();
PLy_result_init_type();
PLy_subtransaction_init_type();
PLy_cursor_init_type();
#if PY_MAJOR_VERSION >= 3
PyModule_Create(&PLy_module);
/* for Python 3 we initialized the exceptions in PyInit_plpy */
#else
plpy = Py_InitModule("plpy", PLy_methods);
PLy_add_exceptions(plpy);
#endif
/* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */
/*
* initialize main module, and add plpy
*/
main_mod = PyImport_AddModule("__main__");
main_dict = PyModule_GetDict(main_mod);
plpy_mod = PyImport_AddModule("plpy");
PyDict_SetItemString(main_dict, "plpy", plpy_mod);
if (PyErr_Occurred())
elog(ERROR, "could not initialize plpy");
}
static void
PLy_add_exceptions(PyObject *plpy)
{
PyObject *excmod;
HASHCTL hash_ctl;
#if PY_MAJOR_VERSION < 3
excmod = Py_InitModule("spiexceptions", PLy_exc_methods);
#else
excmod = PyModule_Create(&PLy_exc_module);
#endif
if (PyModule_AddObject(plpy, "spiexceptions", excmod) < 0)
PLy_elog(ERROR, "could not add the spiexceptions module");
/*
* XXX it appears that in some circumstances the reference count of the
* spiexceptions module drops to zero causing a Python assert failure when
* the garbage collector visits the module. This has been observed on the
* buildfarm. To fix this, add an additional ref for the module here.
*
* This shouldn't cause a memory leak - we don't want this garbage
* collected, and this function shouldn't be called more than once per
* backend.
*/
Py_INCREF(excmod);
PLy_exc_error = PyErr_NewException("plpy.Error", NULL, NULL);
PLy_exc_fatal = PyErr_NewException("plpy.Fatal", NULL, NULL);
PLy_exc_spi_error = PyErr_NewException("plpy.SPIError", NULL, NULL);
Py_INCREF(PLy_exc_error);
PyModule_AddObject(plpy, "Error", PLy_exc_error);
Py_INCREF(PLy_exc_fatal);
PyModule_AddObject(plpy, "Fatal", PLy_exc_fatal);
Py_INCREF(PLy_exc_spi_error);
PyModule_AddObject(plpy, "SPIError", PLy_exc_spi_error);
memset(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(int);
hash_ctl.entrysize = sizeof(PLyExceptionEntry);
hash_ctl.hash = tag_hash;
PLy_spi_exceptions = hash_create("SPI exceptions", 256,
&hash_ctl, HASH_ELEM | HASH_FUNCTION);
PLy_generate_spi_exceptions(excmod, PLy_exc_spi_error);
}
/*
* Add all the autogenerated exceptions as subclasses of SPIError
*/
static void
PLy_generate_spi_exceptions(PyObject *mod, PyObject *base)
{
int i;
for (i = 0; exception_map[i].name != NULL; i++)
{
bool found;
PyObject *exc;
PLyExceptionEntry *entry;
PyObject *sqlstate;
PyObject *dict = PyDict_New();
sqlstate = PyString_FromString(unpack_sql_state(exception_map[i].sqlstate));
PyDict_SetItemString(dict, "sqlstate", sqlstate);
Py_DECREF(sqlstate);
exc = PyErr_NewException(exception_map[i].name, base, dict);
PyModule_AddObject(mod, exception_map[i].classname, exc);
entry = hash_search(PLy_spi_exceptions, &exception_map[i].sqlstate,
HASH_ENTER, &found);
entry->exc = exc;
Assert(!found);
}
}
/*
* the python interface to the elog function
* don't confuse these with PLy_elog
*/
static PyObject *PLy_output(volatile int, PyObject *, PyObject *);
PyObject *
PLy_debug(PyObject *self, PyObject *args)
{
return PLy_output(DEBUG2, self, args);
}
PyObject *
PLy_log(PyObject *self, PyObject *args)
{
return PLy_output(LOG, self, args);
}
PyObject *
PLy_info(PyObject *self, PyObject *args)
{
return PLy_output(INFO, self, args);
}
PyObject *
PLy_notice(PyObject *self, PyObject *args)
{
return PLy_output(NOTICE, self, args);
}
PyObject *
PLy_warning(PyObject *self, PyObject *args)
{
return PLy_output(WARNING, self, args);
}
PyObject *
PLy_error(PyObject *self, PyObject *args)
{
return PLy_output(ERROR, self, args);
}
PyObject *
PLy_fatal(PyObject *self, PyObject *args)
{
return PLy_output(FATAL, self, args);
}
PyObject *
PLy_quote_literal(PyObject *self, PyObject *args)
{
const char *str;
char *quoted;
PyObject *ret;
if (!PyArg_ParseTuple(args, "s", &str))
return NULL;
quoted = quote_literal_cstr(str);
ret = PyString_FromString(quoted);
pfree(quoted);
return ret;
}
PyObject *
PLy_quote_nullable(PyObject *self, PyObject *args)
{
const char *str;
char *quoted;
PyObject *ret;
if (!PyArg_ParseTuple(args, "z", &str))
return NULL;
if (str == NULL)
return PyString_FromString("NULL");
quoted = quote_literal_cstr(str);
ret = PyString_FromString(quoted);
pfree(quoted);
return ret;
}
PyObject *
PLy_quote_ident(PyObject *self, PyObject *args)
{
const char *str;
const char *quoted;
PyObject *ret;
if (!PyArg_ParseTuple(args, "s", &str))
return NULL;
quoted = quote_identifier(str);
ret = PyString_FromString(quoted);
return ret;
}
static PyObject *
PLy_output(volatile int level, PyObject *self, PyObject *args)
{
PyObject *volatile so;
char *volatile sv;
volatile MemoryContext oldcontext;
if (PyTuple_Size(args) == 1)
{
/*
* Treat single argument specially to avoid undesirable ('tuple',)
* decoration.
*/
PyObject *o;
PyArg_UnpackTuple(args, "plpy.elog", 1, 1, &o);
so = PyObject_Str(o);
}
else
so = PyObject_Str(args);
if (so == NULL || ((sv = PyString_AsString(so)) == NULL))
{
level = ERROR;
sv = dgettext(TEXTDOMAIN, "could not parse error message in plpy.elog");
}
oldcontext = CurrentMemoryContext;
PG_TRY();
{
pg_verifymbstr(sv, strlen(sv), false);
elog(level, "%s", sv);
}
PG_CATCH();
{
ErrorData *edata;
MemoryContextSwitchTo(oldcontext);
edata = CopyErrorData();
FlushErrorState();
/*
* Note: If sv came from PyString_AsString(), it points into storage
* owned by so. So free so after using sv.
*/
Py_XDECREF(so);
/* Make Python raise the exception */
PLy_exception_set(PLy_exc_error, "%s", edata->message);
return NULL;
}
PG_END_TRY();
Py_XDECREF(so);
/*
* return a legal object so the interpreter will continue on its merry way
*/
Py_INCREF(Py_None);
return Py_None;
}
/*
* src/pl/plpython/plpy_plpymodule.h
*/
#ifndef PLPY_PLPYMODULE_H
#define PLPY_PLPYMODULE_H
#include "utils/hsearch.h"
/* A hash table mapping sqlstates to exceptions, for speedy lookup */
extern HTAB *PLy_spi_exceptions;
#if PY_MAJOR_VERSION >= 3
PyMODINIT_FUNC PyInit_plpy(void);
#endif
extern void PLy_init_plpy(void);
#endif /* PLPY_PLPYMODULE_H */
This diff is collapsed.
/*
* src/pl/plpython/plpy_procedure.h
*/
#ifndef PLPY_PROCEDURE_H
#define PLPY_PROCEDURE_H
#include "plpy_typeio.h"
extern void init_procedure_caches(void);
/* cached procedure data */
typedef struct PLyProcedure
{
char *proname; /* SQL name of procedure */
char *pyname; /* Python name of procedure */
TransactionId fn_xmin;
ItemPointerData fn_tid;
bool fn_readonly;
PLyTypeInfo result; /* also used to store info for trigger tuple
* type */
bool is_setof; /* true, if procedure returns result set */
PyObject *setof; /* contents of result set. */
char *src; /* textual procedure code, after mangling */
char **argnames; /* Argument names */
PLyTypeInfo args[FUNC_MAX_ARGS];
int nargs;
PyObject *code; /* compiled procedure code */
PyObject *statics; /* data saved across calls, local scope */
PyObject *globals; /* data saved across calls, global scope */
} PLyProcedure;
/* the procedure cache entry */
typedef struct PLyProcedureEntry
{
Oid fn_oid; /* hash key */
PLyProcedure *proc;
} PLyProcedureEntry;
/* PLyProcedure manipulation */
extern char *PLy_procedure_name(PLyProcedure *);
extern PLyProcedure *PLy_procedure_get(Oid, bool);
extern void PLy_procedure_compile(PLyProcedure *, const char *);
extern void PLy_procedure_delete(PLyProcedure *);
/* currently active plpython function */
extern PLyProcedure *PLy_curr_procedure;
#endif /* PLPY_PROCEDURE_H */
/*
* the PLyResult class
*
* src/pl/plpython/plpy_resultobject.c
*/
#include "postgres.h"
#include "plpython.h"
#include "plpy_resultobject.h"
static void PLy_result_dealloc(PyObject *);
static PyObject *PLy_result_nrows(PyObject *, PyObject *);
static PyObject *PLy_result_status(PyObject *, PyObject *);
static Py_ssize_t PLy_result_length(PyObject *);
static PyObject *PLy_result_item(PyObject *, Py_ssize_t);
static PyObject *PLy_result_slice(PyObject *, Py_ssize_t, Py_ssize_t);
static int PLy_result_ass_item(PyObject *, Py_ssize_t, PyObject *);
static int PLy_result_ass_slice(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *);
static char PLy_result_doc[] = {
"Results of a PostgreSQL query"
};
static PySequenceMethods PLy_result_as_sequence = {
PLy_result_length, /* sq_length */
NULL, /* sq_concat */
NULL, /* sq_repeat */
PLy_result_item, /* sq_item */
PLy_result_slice, /* sq_slice */
PLy_result_ass_item, /* sq_ass_item */
PLy_result_ass_slice, /* sq_ass_slice */
};
static PyMethodDef PLy_result_methods[] = {
{"nrows", PLy_result_nrows, METH_VARARGS, NULL},
{"status", PLy_result_status, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL}
};
static PyTypeObject PLy_ResultType = {
PyVarObject_HEAD_INIT(NULL, 0)
"PLyResult", /* tp_name */
sizeof(PLyResultObject), /* tp_size */
0, /* tp_itemsize */
/*
* methods
*/
PLy_result_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
&PLy_result_as_sequence, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
PLy_result_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
PLy_result_methods, /* tp_tpmethods */
};
void
PLy_result_init_type(void)
{
if (PyType_Ready(&PLy_ResultType) < 0)
elog(ERROR, "could not initialize PLy_ResultType");
}
PyObject *
PLy_result_new(void)
{
PLyResultObject *ob;
if ((ob = PyObject_New(PLyResultObject, &PLy_ResultType)) == NULL)
return NULL;
/* ob->tuples = NULL; */
Py_INCREF(Py_None);
ob->status = Py_None;
ob->nrows = PyInt_FromLong(-1);
ob->rows = PyList_New(0);
return (PyObject *) ob;
}
static void
PLy_result_dealloc(PyObject *arg)
{
PLyResultObject *ob = (PLyResultObject *) arg;
Py_XDECREF(ob->nrows);
Py_XDECREF(ob->rows);
Py_XDECREF(ob->status);
arg->ob_type->tp_free(arg);
}
static PyObject *
PLy_result_nrows(PyObject *self, PyObject *args)
{
PLyResultObject *ob = (PLyResultObject *) self;
Py_INCREF(ob->nrows);
return ob->nrows;
}
static PyObject *
PLy_result_status(PyObject *self, PyObject *args)
{
PLyResultObject *ob = (PLyResultObject *) self;
Py_INCREF(ob->status);
return ob->status;
}
static Py_ssize_t
PLy_result_length(PyObject *arg)
{
PLyResultObject *ob = (PLyResultObject *) arg;
return PyList_Size(ob->rows);
}
static PyObject *
PLy_result_item(PyObject *arg, Py_ssize_t idx)
{
PyObject *rv;
PLyResultObject *ob = (PLyResultObject *) arg;
rv = PyList_GetItem(ob->rows, idx);
if (rv != NULL)
Py_INCREF(rv);
return rv;
}
static int
PLy_result_ass_item(PyObject *arg, Py_ssize_t idx, PyObject *item)
{
int rv;
PLyResultObject *ob = (PLyResultObject *) arg;
Py_INCREF(item);
rv = PyList_SetItem(ob->rows, idx, item);
return rv;
}
static PyObject *
PLy_result_slice(PyObject *arg, Py_ssize_t lidx, Py_ssize_t hidx)
{
PLyResultObject *ob = (PLyResultObject *) arg;
return PyList_GetSlice(ob->rows, lidx, hidx);
}
static int
PLy_result_ass_slice(PyObject *arg, Py_ssize_t lidx, Py_ssize_t hidx, PyObject *slice)
{
int rv;
PLyResultObject *ob = (PLyResultObject *) arg;
rv = PyList_SetSlice(ob->rows, lidx, hidx, slice);
return rv;
}
/*
* src/pl/plpython/plpy_resultobject.h
*/
#ifndef PLPY_RESULTOBJECT_H
#define PLPY_RESULTOBJECT_H
typedef struct PLyResultObject
{
PyObject_HEAD
/* HeapTuple *tuples; */
PyObject *nrows; /* number of rows returned by query */
PyObject *rows; /* data rows, or None if no data returned */
PyObject *status; /* query status, SPI_OK_*, or SPI_ERR_* */
} PLyResultObject;
extern void PLy_result_init_type(void);
extern PyObject *PLy_result_new(void);
#endif /* PLPY_RESULTOBJECT_H */
This diff is collapsed.
/*
* src/pl/plpython/plpy_spi.h
*/
#ifndef PLPY_SPI_H
#define PLPY_SPI_H
#include "utils/palloc.h"
#include "utils/resowner.h"
extern PyObject *PLy_spi_prepare(PyObject *, PyObject *);
extern PyObject *PLy_spi_execute(PyObject *, PyObject *);
typedef struct PLyExceptionEntry
{
int sqlstate; /* hash key, must be first */
PyObject *exc; /* corresponding exception */
} PLyExceptionEntry;
/* handling of SPI operations inside subtransactions */
extern void PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner);
extern void PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner);
extern void PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner);
#endif /* PLPY_SPI_H */
/*
* the PLySubtransaction class
*
* src/pl/plpython/plpy_subxactobject.c
*/
#include "postgres.h"
#include "access/xact.h"
#include "executor/spi.h"
#include "plpython.h"
#include "plpy_subxactobject.h"
#include "plpy_elog.h"
List *explicit_subtransactions = NIL;
static void PLy_subtransaction_dealloc(PyObject *);
static PyObject *PLy_subtransaction_enter(PyObject *, PyObject *);
static PyObject *PLy_subtransaction_exit(PyObject *, PyObject *);
static char PLy_subtransaction_doc[] = {
"PostgreSQL subtransaction context manager"
};
static PyMethodDef PLy_subtransaction_methods[] = {
{"__enter__", PLy_subtransaction_enter, METH_VARARGS, NULL},
{"__exit__", PLy_subtransaction_exit, METH_VARARGS, NULL},
/* user-friendly names for Python <2.6 */
{"enter", PLy_subtransaction_enter, METH_VARARGS, NULL},
{"exit", PLy_subtransaction_exit, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL}
};
static PyTypeObject PLy_SubtransactionType = {
PyVarObject_HEAD_INIT(NULL, 0)
"PLySubtransaction", /* tp_name */
sizeof(PLySubtransactionObject), /* tp_size */
0, /* tp_itemsize */
/*
* methods
*/
PLy_subtransaction_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
PLy_subtransaction_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
PLy_subtransaction_methods, /* tp_tpmethods */
};
void
PLy_subtransaction_init_type(void)
{
if (PyType_Ready(&PLy_SubtransactionType) < 0)
elog(ERROR, "could not initialize PLy_SubtransactionType");
}
/* s = plpy.subtransaction() */
PyObject *
PLy_subtransaction_new(PyObject *self, PyObject *unused)
{
PLySubtransactionObject *ob;
ob = PyObject_New(PLySubtransactionObject, &PLy_SubtransactionType);
if (ob == NULL)
return NULL;
ob->started = false;
ob->exited = false;
return (PyObject *) ob;
}
/* Python requires a dealloc function to be defined */
static void
PLy_subtransaction_dealloc(PyObject *subxact)
{
}
/*
* subxact.__enter__() or subxact.enter()
*
* Start an explicit subtransaction. SPI calls within an explicit
* subtransaction will not start another one, so you can atomically
* execute many SPI calls and still get a controllable exception if
* one of them fails.
*/
static PyObject *
PLy_subtransaction_enter(PyObject *self, PyObject *unused)
{
PLySubtransactionData *subxactdata;
MemoryContext oldcontext;
PLySubtransactionObject *subxact = (PLySubtransactionObject *) self;
if (subxact->started)
{
PLy_exception_set(PyExc_ValueError, "this subtransaction has already been entered");
return NULL;
}
if (subxact->exited)
{
PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
return NULL;
}
subxact->started = true;
oldcontext = CurrentMemoryContext;
subxactdata = PLy_malloc(sizeof(*subxactdata));
subxactdata->oldcontext = oldcontext;
subxactdata->oldowner = CurrentResourceOwner;
BeginInternalSubTransaction(NULL);
/* Do not want to leave the previous memory context */
MemoryContextSwitchTo(oldcontext);
explicit_subtransactions = lcons(subxactdata, explicit_subtransactions);
Py_INCREF(self);
return self;
}
/*
* subxact.__exit__(exc_type, exc, tb) or subxact.exit(exc_type, exc, tb)
*
* Exit an explicit subtransaction. exc_type is an exception type, exc
* is the exception object, tb is the traceback. If exc_type is None,
* commit the subtransactiony, if not abort it.
*
* The method signature is chosen to allow subtransaction objects to
* be used as context managers as described in
* <http://www.python.org/dev/peps/pep-0343/>.
*/
static PyObject *
PLy_subtransaction_exit(PyObject *self, PyObject *args)
{
PyObject *type;
PyObject *value;
PyObject *traceback;
PLySubtransactionData *subxactdata;
PLySubtransactionObject *subxact = (PLySubtransactionObject *) self;
if (!PyArg_ParseTuple(args, "OOO", &type, &value, &traceback))
return NULL;
if (!subxact->started)
{
PLy_exception_set(PyExc_ValueError, "this subtransaction has not been entered");
return NULL;
}
if (subxact->exited)
{
PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
return NULL;
}
if (explicit_subtransactions == NIL)
{
PLy_exception_set(PyExc_ValueError, "there is no subtransaction to exit from");
return NULL;
}
subxact->exited = true;
if (type != Py_None)
{
/* Abort the inner transaction */
RollbackAndReleaseCurrentSubTransaction();
}
else
{
ReleaseCurrentSubTransaction();
}
subxactdata = (PLySubtransactionData *) linitial(explicit_subtransactions);
explicit_subtransactions = list_delete_first(explicit_subtransactions);
MemoryContextSwitchTo(subxactdata->oldcontext);
CurrentResourceOwner = subxactdata->oldowner;
PLy_free(subxactdata);
/*
* AtEOSubXact_SPI() should not have popped any SPI context, but just in
* case it did, make sure we remain connected.
*/
SPI_restore_connection();
Py_INCREF(Py_None);
return Py_None;
}
/*
* src/pl/plpython/plpy_subxactobject.h
*/
#ifndef PLPY_SUBXACTOBJECT
#define PLPY_SUBXACTOBJECT
/* a list of nested explicit subtransactions */
extern List *explicit_subtransactions;
typedef struct PLySubtransactionObject
{
PyObject_HEAD
bool started;
bool exited;
} PLySubtransactionObject;
/* explicit subtransaction data */
typedef struct PLySubtransactionData
{
MemoryContext oldcontext;
ResourceOwner oldowner;
} PLySubtransactionData;
extern void PLy_subtransaction_init_type(void);
extern PyObject *PLy_subtransaction_new(PyObject *, PyObject *);
#endif /* PLPY_SUBXACTOBJECT */
This diff is collapsed.
/*
* src/pl/plpython/plpy_typeio.h
*/
#ifndef PLPY_TYPEIO_H
#define PLPY_TYPEIO_H
#include "access/htup.h"
#include "fmgr.h"
#include "storage/itemptr.h"
struct PLyDatumToOb;
typedef PyObject *(*PLyDatumToObFunc) (struct PLyDatumToOb *, Datum);
typedef struct PLyDatumToOb
{
PLyDatumToObFunc func;
FmgrInfo typfunc; /* The type's output function */
Oid typoid; /* The OID of the type */
int32 typmod; /* The typmod of the type */
Oid typioparam;
bool typbyval;
int16 typlen;
char typalign;
struct PLyDatumToOb *elm;
} PLyDatumToOb;
typedef struct PLyTupleToOb
{
PLyDatumToOb *atts;
int natts;
} PLyTupleToOb;
typedef union PLyTypeInput
{
PLyDatumToOb d;
PLyTupleToOb r;
} PLyTypeInput;
/* convert PyObject to a Postgresql Datum or tuple.
* output from Python
*/
struct PLyObToDatum;
typedef Datum (*PLyObToDatumFunc) (struct PLyObToDatum *, int32, PyObject *);
typedef struct PLyObToDatum
{
PLyObToDatumFunc func;
FmgrInfo typfunc; /* The type's input function */
Oid typoid; /* The OID of the type */
int32 typmod; /* The typmod of the type */
Oid typioparam;
bool typbyval;
int16 typlen;
char typalign;
struct PLyObToDatum *elm;
} PLyObToDatum;
typedef struct PLyObToTuple
{
PLyObToDatum *atts;
int natts;
} PLyObToTuple;
typedef union PLyTypeOutput
{
PLyObToDatum d;
PLyObToTuple r;
} PLyTypeOutput;
/* all we need to move Postgresql data to Python objects,
* and vice versa
*/
typedef struct PLyTypeInfo
{
PLyTypeInput in;
PLyTypeOutput out;
/*
* 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;
/* used to check if the type has been modified */
Oid typ_relid;
TransactionId typrel_xmin;
ItemPointerData typrel_tid;
} PLyTypeInfo;
extern void PLy_typeinfo_init(PLyTypeInfo *);
extern void PLy_typeinfo_dealloc(PLyTypeInfo *);
extern void PLy_input_datum_func(PLyTypeInfo *, Oid, HeapTuple);
extern void PLy_output_datum_func(PLyTypeInfo *, HeapTuple);
extern void PLy_input_tuple_funcs(PLyTypeInfo *, TupleDesc);
extern void PLy_output_tuple_funcs(PLyTypeInfo *, TupleDesc);
extern void PLy_output_record_funcs(PLyTypeInfo *, TupleDesc);
/* conversion from Python objects to heap tuples */
extern HeapTuple PLyObject_ToTuple(PLyTypeInfo *, TupleDesc, PyObject *);
/* conversion from heap tuples to Python dictionaries */
extern PyObject *PLyDict_FromTuple(PLyTypeInfo *, HeapTuple, TupleDesc);
#endif /* PLPY_TYPEIO_H */
/*
* utility functions
*
* src/pl/plpython/plpy_util.c
*/
#include "postgres.h"
#include "mb/pg_wchar.h"
#include "utils/memutils.h"
#include "utils/palloc.h"
#include "plpython.h"
#include "plpy_util.h"
#include "plpy_elog.h"
void *
PLy_malloc(size_t bytes)
{
/* We need our allocations to be long-lived, so use TopMemoryContext */
return MemoryContextAlloc(TopMemoryContext, bytes);
}
void *
PLy_malloc0(size_t bytes)
{
void *ptr = PLy_malloc(bytes);
MemSet(ptr, 0, bytes);
return ptr;
}
char *
PLy_strdup(const char *str)
{
char *result;
size_t len;
len = strlen(str) + 1;
result = PLy_malloc(len);
memcpy(result, str, len);
return result;
}
/* define this away */
void
PLy_free(void *ptr)
{
pfree(ptr);
}
/*
* Convert a Python unicode object to a Python string/bytes object in
* PostgreSQL server encoding. Reference ownership is passed to the
* caller.
*/
PyObject *
PLyUnicode_Bytes(PyObject *unicode)
{
PyObject *rv;
const char *serverenc;
/*
* Python understands almost all PostgreSQL encoding names, but it doesn't
* know SQL_ASCII.
*/
if (GetDatabaseEncoding() == PG_SQL_ASCII)
serverenc = "ascii";
else
serverenc = GetDatabaseEncodingName();
rv = PyUnicode_AsEncodedString(unicode, serverenc, "strict");
if (rv == NULL)
PLy_elog(ERROR, "could not convert Python Unicode object to PostgreSQL server encoding");
return rv;
}
/*
* Convert a Python unicode object to a C string in PostgreSQL server
* encoding. No Python object reference is passed out of this
* function. The result is palloc'ed.
*
* Note that this function is disguised as PyString_AsString() when
* using Python 3. That function retuns a pointer into the internal
* memory of the argument, which isn't exactly the interface of this
* function. But in either case you get a rather short-lived
* reference that you ought to better leave alone.
*/
char *
PLyUnicode_AsString(PyObject *unicode)
{
PyObject *o = PLyUnicode_Bytes(unicode);
char *rv = pstrdup(PyBytes_AsString(o));
Py_XDECREF(o);
return rv;
}
#if PY_MAJOR_VERSION >= 3
/*
* Convert a C string in the PostgreSQL server encoding to a Python
* unicode object. Reference ownership is passed to the caller.
*/
PyObject *
PLyUnicode_FromString(const char *s)
{
char *utf8string;
PyObject *o;
utf8string = (char *) pg_do_encoding_conversion((unsigned char *) s,
strlen(s),
GetDatabaseEncoding(),
PG_UTF8);
o = PyUnicode_FromString(utf8string);
if (utf8string != s)
pfree(utf8string);
return o;
}
#endif /* PY_MAJOR_VERSION >= 3 */
/*--------------------------
* common utility functions
*--------------------------
*/
#ifndef PLPY_UTIL_H
#define PLPY_UTIL_H
extern void *PLy_malloc(size_t);
extern void *PLy_malloc0(size_t);
extern char *PLy_strdup(const char *);
extern void PLy_free(void *);
extern PyObject *PLyUnicode_Bytes(PyObject *unicode);
extern char *PLyUnicode_AsString(PyObject *unicode);
#if PY_MAJOR_VERSION >= 3
extern PyObject *PLyUnicode_FromString(const char *s);
#endif
#endif /* PLPY_UTIL_H */
This diff is collapsed.
/*-------------------------------------------------------------------------
*
* plpython.h - Python as a procedural language for PostgreSQL
*
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/pl/plpython/plpython.h
*
*-------------------------------------------------------------------------
*/
#ifndef PLPYTHON_H
#define PLPYTHON_H
/*
* Include order should be: postgres.h, other postgres headers, plpython.h,
* other plpython headers
*/
#ifndef POSTGRES_H
#error postgres.h must be included before plpython.h
#endif
/*
* Undefine some things that get (re)defined in the Python headers. They aren't
* used by the PL/Python code, and all PostgreSQL headers should be included
* earlier, so this should be pretty safe.
*/
#undef _POSIX_C_SOURCE
#undef _XOPEN_SOURCE
#undef HAVE_STRERROR
#undef HAVE_TZNAME
/*
* Sometimes python carefully scribbles on our *printf macros.
* So we undefine them here and redefine them after it's done its dirty deed.
*/
#ifdef USE_REPL_SNPRINTF
#undef snprintf
#undef vsnprintf
#endif
#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
/*
* Py_ssize_t compat for Python <= 2.4
*/
#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
typedef int Py_ssize_t;
#define PY_SSIZE_T_MAX INT_MAX
#define PY_SSIZE_T_MIN INT_MIN
#endif
/*
* PyBool_FromLong is supported from 2.3.
*/
#if PY_VERSION_HEX < 0x02030000
#define PyBool_FromLong(x) PyInt_FromLong(x)
#endif
/*
* Python 2/3 strings/unicode/bytes handling. Python 2 has strings
* and unicode, Python 3 has strings, which are unicode on the C
* level, and bytes. The porting convention, which is similarly used
* in Python 2.6, is that "Unicode" is always unicode, and "Bytes" are
* bytes in Python 3 and strings in Python 2. Since we keep
* supporting Python 2 and its usual strings, we provide a
* compatibility layer for Python 3 that when asked to convert a C
* string to a Python string it converts the C string from the
* PostgreSQL server encoding to a Python Unicode object.
*/
#if PY_VERSION_HEX < 0x02060000
/* This is exactly the compatibility layer that Python 2.6 uses. */
#define PyBytes_AsString PyString_AsString
#define PyBytes_FromStringAndSize PyString_FromStringAndSize
#define PyBytes_Size PyString_Size
#define PyObject_Bytes PyObject_Str
#endif
#if PY_MAJOR_VERSION >= 3
#define PyString_Check(x) 0
#define PyString_AsString(x) PLyUnicode_AsString(x)
#define PyString_FromString(x) PLyUnicode_FromString(x)
#endif
/*
* Python 3 only has long.
*/
#if PY_MAJOR_VERSION >= 3
#define PyInt_FromLong(x) PyLong_FromLong(x)
#define PyInt_AsLong(x) PyLong_AsLong(x)
#endif
/*
* PyVarObject_HEAD_INIT was added in Python 2.6. Its use is
* necessary to handle both Python 2 and 3. This replacement
* definition is for Python <=2.5
*/
#ifndef PyVarObject_HEAD_INIT
#define PyVarObject_HEAD_INIT(type, size) \
PyObject_HEAD_INIT(type) size,
#endif
/* Python 3 removed the Py_TPFLAGS_HAVE_ITER flag */
#if PY_MAJOR_VERSION >= 3
#define Py_TPFLAGS_HAVE_ITER 0
#endif
/* define our text domain for translations */
#undef TEXTDOMAIN
#define TEXTDOMAIN PG_TEXTDOMAIN("plpython")
#include <compile.h>
#include <eval.h>
/* put back our snprintf and vsnprintf */
#ifdef USE_REPL_SNPRINTF
#ifdef snprintf
#undef snprintf
#endif
#ifdef vsnprintf
#undef vsnprintf
#endif
#ifdef __GNUC__
#define vsnprintf(...) pg_vsnprintf(__VA_ARGS__)
#define snprintf(...) pg_snprintf(__VA_ARGS__)
#else
#define vsnprintf pg_vsnprintf
#define snprintf pg_snprintf
#endif /* __GNUC__ */
#endif /* USE_REPL_SNPRINTF */
/*
* Used throughout, and also by the Python 2/3 porting layer, so it's easier to
* just include it everywhere.
*/
#include "plpy_util.h"
#endif /* PLPYTHON_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment