openGauss supports plpython

This commit is contained in:
JinLiOnline
2020-10-26 23:53:37 +08:00
parent b27c38a64e
commit 043eeab8da
24 changed files with 699 additions and 274 deletions

13
configure vendored
View File

@ -28697,6 +28697,19 @@ fi
if test "$with_python" = yes; then
ac_save_CPPFLAGS=$CPPFLAGS
CPPFLAGS="$python_includespec $CPPFLAGS"
if test $python_majorversion = 2; then
cat >>confdefs.h <<\_ACEOF
#define ENABLE_PYTHON2 1
_ACEOF
fi
if test $python_majorversion = 3; then
cat >>confdefs.h <<\_ACEOF
#define ENABLE_PYTHON3 1
_ACEOF
fi
if test "${ac_cv_header_Python_h+set}" = set; then
{ $as_echo "$as_me:$LINENO: checking for Python.h" >&5
$as_echo_n "checking for Python.h... " >&6; }

View File

@ -265,6 +265,15 @@ static void load_dist_fdw(void);
static void load_hdfs_fdw(void);
#endif
#ifdef ENABLE_PYTHON2
static void load_plpython2u_extension(void);
static void load_plpythonu_extension(void);
#endif
#ifdef ENABLE_PYTHON3
static void load_plpython3u_extension(void);
#endif
static void load_mot_fdw(void); /* load MOT fdw */
#ifdef ENABLE_MULTIPLE_NODES
static void load_hstore_extension(void);
@ -2536,6 +2545,71 @@ static void load_log_extension(void)
}
#endif
#ifdef ENABLE_PYTHON2
static void load_plpython2u_extension()
{
PG_CMD_DECL;
int nRet = 0;
fputs(_("loading plpython2u extension ... "), stdout);
(void)fflush(stdout);
nRet = snprintf_s(
cmd, sizeof(cmd), sizeof(cmd) - 1, "\"%s\" %s template1 >%s", backend_exec, backend_options, DEVNULL);
securec_check_ss_c(nRet, "\0", "\0");
PG_CMD_OPEN;
PG_CMD_PUTS("CREATE EXTENSION plpython2u;\n");
PG_CMD_CLOSE;
check_ok();
}
#endif
#ifdef ENABLE_PYTHON3
static void load_plpython3u_extension()
{
PG_CMD_DECL;
int nRet = 0;
fputs(_("loading plpython3u extension ... "), stdout);
(void)fflush(stdout);
nRet = snprintf_s(
cmd, sizeof(cmd), sizeof(cmd) - 1, "\"%s\" %s template1 >%s", backend_exec, backend_options, DEVNULL);
securec_check_ss_c(nRet, "\0", "\0");
PG_CMD_OPEN;
PG_CMD_PUTS("CREATE EXTENSION plpython3u;\n");
PG_CMD_CLOSE;
check_ok();
}
#endif
#ifdef ENABLE_PYTHON2
static void load_plpythonu_extension()
{
PG_CMD_DECL;
int nRet = 0;
fputs(_("loading plpythonu extension ... "), stdout);
(void)fflush(stdout);
nRet = snprintf_s(
cmd, sizeof(cmd), sizeof(cmd) - 1, "\"%s\" %s template1 >%s", backend_exec, backend_options, DEVNULL);
securec_check_ss_c(nRet, "\0", "\0");
PG_CMD_OPEN;
PG_CMD_PUTS("CREATE EXTENSION plpythonu;\n");
PG_CMD_CLOSE;
check_ok();
}
#endif
static void load_supported_extension(void)
{
@ -2563,6 +2637,16 @@ static void load_supported_extension(void)
load_hstore_extension();
#endif
/* loading plpy extension */
#ifdef ENABLE_PYTHON2
load_plpython2u_extension();
load_plpythonu_extension();
#endif
#ifdef ENABLE_PYTHON3
load_plpython3u_extension();
#endif
/* loading foreign-data wrapper for mot in-memory data access*/
load_mot_fdw();
}

16
src/common/backend/utils/errcodes.txt Executable file → Normal file
View File

@ -185,7 +185,7 @@ Section: Class 22 - Data Exception
22004 E ERRCODE_NULL_VALUE_NOT_ALLOWED null_value_not_allowed
22002 E ERRCODE_NULL_VALUE_NO_INDICATOR_PARAMETER null_value_no_indicator_parameter
22003 E ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE numeric_value_out_of_range
22005 E ERRCODE_DOP_VALUE_OUT_OF_RANGE dop_value_out_of_range
22017 E ERRCODE_DOP_VALUE_OUT_OF_RANGE dop_value_out_of_range
22026 E ERRCODE_STRING_DATA_LENGTH_MISMATCH string_data_length_mismatch
22028 E ERRCODE_REGEXP_MISMATCH regexp_mismatch
22001 E ERRCODE_STRING_DATA_RIGHT_TRUNCATION string_data_right_truncation
@ -251,7 +251,7 @@ Section: Class 28 - Invalid Authorization Specification
28P01 E ERRCODE_INVALID_PASSWORD invalid_password
28P02 E ERRCODE_INITIAL_PASSWORD_NOT_MODIFIED initial_password_not_modified
Section: Class 29 - Invalid or Unexpected Status
Section: Class 29 - Invalid or Unexpected Status
29000 E ERRCODE_INVALID_STATUS invalid_status
29001 E ERRCODE_INVALID_TABLESAMPLE_ARGUMENT invalid_tablesample_argument
29002 E ERRCODE_INVALID_TABLESAMPLE_REPEAT invalid_tablesample_repeat
@ -479,7 +479,7 @@ HV00L E ERRCODE_FDW_UNABLE_TO_CREATE_EXECUTION fdw
HV00M E ERRCODE_FDW_UNABLE_TO_CREATE_REPLY fdw_unable_to_create_reply
HV00N E ERRCODE_FDW_UNABLE_TO_ESTABLISH_CONNECTION fdw_unable_to_establish_connection
HV00O E ERRCODE_FDW_INVALID_LIST_LENGTH fdw_invalid_list_length
HV00P E ERRCODE_FDW_INVALID_SERVER_TYPE fdw_invalid_server_type
HV00S E ERRCODE_FDW_INVALID_SERVER_TYPE fdw_invalid_server_type
HV025 E ERRCODE_FDW_OPERATION_NOT_SUPPORTED fdw_operation_not_supported
HV026 E ERRCODE_FDW_CROSS_STORAGE_ENGINE_TRANSACTION_NOT_SUPPORTED fdw_cross_storage_engine_transaction_not_supported
@ -555,12 +555,12 @@ Section: Class SI - SPI Interface Error
SP000 E ERRCODE_SPI_ERROR spi_error
SP001 E ERRCODE_SPI_CONNECTION_FAILURE spi_connection_failure
SP002 E ERRCODE_SPI_FINISH_FAILURE spi_finish_failure
SP003 E ERRCODE_SPI_PREPARE_FAILURE spi_prepare_failure
SP003 E ERRCODE_SPI_PREPARE_FAILURE spi_prepare_failure
SP004 E ERRCODE_SPI_CURSOR_OPEN_FAILURE spi_cursor_open_failure
SP005 E ERRCODE_SPI_EXECUTE_FAILURE spi_execute_failure
SP006 E ERRORCODE_SPI_IMPROPER_CALL spi_improper_call_function
# Section: Class PD - PL Debugger Error
# Section: Class PD - PL Debugger Error
D0000 E ERRCODE_PLDEBUGGER_ERROR pldebugger_internal_error
D0001 E ERRCODE_DUPLICATE_BREAKPOINT duplicate_breakpoint
D0002 E ERRCODE_FUNCTION_HASH_NOT_INITED function_hash_is_not_initialized
@ -577,7 +577,7 @@ D0012 E ERRCODE_MAX_DEBUG_SESSIONS_REACHED max
D0013 E ERRCODE_MAX_BREAKPOINTS_REACHED maximum_number_of_breakpoints_reached
D0014 E ERRCODE_INITIALIZE_FAILED initialization_failed
# Section: Class RB - RBTree Error
# Section: Class RB - RBTree Error
RB001 E ERRCODE_RBTREE_INVALID_NODE_STATE rbtree_invalid_node_state
RB002 E ERRCODE_RBTREE_INVALID_ITERATOR_ORDER rbtree_invalid_iterator_order
@ -585,13 +585,13 @@ RB002 E ERRCODE_RBTREE_INVALID_ITERATOR_ORDER rbt
DB001 W ERRCODE_DEBUG debug
# Section: Class DB - Log info
DB001 W ERRCODE_LOG log
DB010 W ERRCODE_LOG log
# Section: Class DB - Operate Error & Warning
OP001 E ERRCODE_OPERATE_FAILED operate_failed
OP002 E ERRCODE_OPERATE_RESULT_NOT_EXPECTED operate_result_not_expected
OP003 E ERRCODE_OPERATE_NOT_SUPPORTED operate_not_supported
OP003 E ERRCODE_OPERATE_INVALID_PARAM operate_invalid_param
OP0A3 E ERRCODE_OPERATE_INVALID_PARAM operate_invalid_param
OP004 E ERRCODE_INDEX_OPERATOR_MISMATCH index_operator_mismacth
OP005 E ERRCODE_NO_FUNCTION_PROVIDED no_function_provided
# this is for logicaldecode

26
src/common/backend/utils/fmgr/dfmgr.cpp Executable file → Normal file
View File

@ -213,6 +213,32 @@ void* internal_load_library(const char* libname)
/*
* Call pg_dlopen.
*/
#if ((defined ENABLE_PYTHON2) || (defined ENABLE_PYTHON3))
#ifdef ENABLE_PYTHON2
#define PYTHON_LIB_NAME "libpython2.7.so"
#endif
#ifdef ENABLE_PYTHON3
#define PYTHON_LIB_NAME "libpython3.so"
#endif
/*
* In C++, when you try to open a shared library use dlopen with flag RTLD_NOW,
* it will search dependent shared library of main program for the undefined symbol.
*/
if (strstr(file_scanner->filename, "plpython") != NULL) {
/*
* dlopen will find *.so in LD_LIBRARY_PATH, /etc/ld.so.cache, /lib, /usr/lib ..
* And we must set the Flag to "RTLD_NOW | RTLD_GLOBAL"
*/
if (dlopen(PYTHON_LIB_NAME, RTLD_NOW | RTLD_GLOBAL) == NULL) {
pfree((char*)file_scanner);
file_scanner = NULL;
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not load library \"%s\", get error report failed", PYTHON_LIB_NAME)));
}
}
#endif
file_scanner->handle = pg_dlopen(file_scanner->filename);
if (file_scanner->handle == NULL) {

View File

@ -32,7 +32,7 @@ endif
# to work without, we have to skip it.
ifneq (,$(findstring yes, $(shared_libpython)$(allow_nonpic_in_shlib)))
override CPPFLAGS := -I. -I$(srcdir) $(python_includespec) $(CPPFLAGS)
override CPPFLAGS := -I. -I$(srcdir) $(python_includespec) $(filter-out -fPIE, $(CPPFLAGS)) -fPIC -O0
rpathdir = $(python_libdir)

View File

@ -1,7 +1,7 @@
/*
* the PLyCursor class
*
* src/pl/plpython/plpy_cursorobject.c
* src/common/pl/plpython/plpy_cursorobject.cpp
*/
#include "postgres.h"
@ -91,7 +91,7 @@ PyObject* PLy_cursor(PyObject* self, PyObject* args)
return PLy_cursor_plan(plan, planargs);
}
PLy_exception_set(PLy_exc_error, "plpy.cursor expected a query or a plan");
PLy_exception_set(plpy_t_context.PLy_exc_error, "plpy.cursor expected a query or a plan");
return NULL;
}
@ -209,7 +209,7 @@ static PyObject* PLy_cursor_plan(PyObject* ob, PyObject* args)
volatile int j;
if (nargs > 0) {
nulls = palloc(nargs * sizeof(char));
nulls = (char*)palloc(nargs * sizeof(char));
} else {
nulls = NULL;
}
@ -255,7 +255,7 @@ static PyObject* PLy_cursor_plan(PyObject* ob, PyObject* args)
/* cleanup plan->values array */
for (k = 0; k < nargs; k++) {
if (plan->args[k].out.d.typbyval == NULL && (plan->values[k] != PointerGetDatum(NULL))) {
if (!plan->args[k].out.d.typbyval && (plan->values[k] != PointerGetDatum(NULL))) {
pfree(DatumGetPointer(plan->values[k]));
plan->values[k] = PointerGetDatum(NULL);
}
@ -406,12 +406,10 @@ static PyObject* PLy_cursor_fetch(PyObject* self, PyObject* args)
ret->nrows = PyInt_FromLong(SPI_processed);
if (SPI_processed != 0) {
int i;
Py_DECREF(ret->rows);
ret->rows = PyList_New(SPI_processed);
for (i = 0; i < SPI_processed; i++) {
for (uint32 i = 0; i < SPI_processed; i++) {
PyObject* row = PLyDict_FromTuple(&cursor->result, SPI_tuptable->vals[i], SPI_tuptable->tupdesc);
PyList_SetItem(ret->rows, i, row);

75
src/common/pl/plpython/plpy_elog.cpp Executable file → Normal file
View File

@ -16,10 +16,6 @@
#include "plpy_main.h"
#include "plpy_procedure.h"
PyObject* PLy_exc_error = NULL;
PyObject* PLy_exc_fatal = NULL;
PyObject* PLy_exc_spi_error = NULL;
static void PLy_traceback(char** xmsg, char** tbmsg, int* tb_depth);
static void PLy_get_spi_error_data(
PyObject* exc, int* sqlerrcode, char** detail, char** hint, char** query, int* position);
@ -40,8 +36,8 @@ void PLy_elog(int elevel, const char* fmt, ...)
int tb_depth;
StringInfoData emsg;
PyObject* exc = NULL;
*val = NULL;
*tb = NULL;
PyObject* val = NULL;
PyObject* tb = NULL;
const char* primary = NULL;
int sqlerrcode = 0;
char* detail = NULL;
@ -51,9 +47,11 @@ void PLy_elog(int elevel, const char* fmt, ...)
PyErr_Fetch(&exc, &val, &tb);
if (exc != NULL) {
if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error)) {
if (PyErr_GivenExceptionMatches(val, plpy_t_context.PLy_exc_spi_error)) {
PLy_get_spi_error_data(val, &sqlerrcode, &detail, &hint, &query, &position);
} else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal)) {
hint = pstrdup(hint);
query = pstrdup(query);
} else if (PyErr_GivenExceptionMatches(val, plpy_t_context.PLy_exc_fatal)) {
elevel = FATAL;
}
}
@ -112,6 +110,12 @@ void PLy_elog(int elevel, const char* fmt, ...)
if (tbmsg != NULL) {
pfree(tbmsg);
}
if (query != NULL) {
pfree(query);
}
if (hint != NULL) {
pfree(hint);
}
PG_RE_THROW();
}
PG_END_TRY();
@ -125,6 +129,12 @@ void PLy_elog(int elevel, const char* fmt, ...)
if (tbmsg) {
pfree(tbmsg);
}
if (query != NULL) {
pfree(query);
}
if (hint != NULL) {
pfree(hint);
}
}
/*
@ -137,8 +147,8 @@ void PLy_elog(int elevel, const char* fmt, ...)
static void PLy_traceback(char** xmsg, char** tbmsg, int* tb_depth)
{
PyObject* e = NULL;
*v = NULL;
*tb = NULL;
PyObject* v = NULL;
PyObject* tb = NULL;
PyObject* e_type_o = NULL;
PyObject* e_module_o = NULL;
char* e_type_s = NULL;
@ -188,8 +198,7 @@ static void PLy_traceback(char** xmsg, char** tbmsg, int* tb_depth)
appendStringInfoString(&xstr, "unrecognized exception");
}
} else if (strcmp(e_module_s, "builtins") == 0 || strcmp(e_module_s, "__main__") == 0 ||
strcmp(e_module_s, "exceptions") == 0) {
strcmp(e_module_s, "exceptions") == 0) {
/* mimics behavior of traceback.format_exception_only */
appendStringInfo(&xstr, "%s", e_type_s);
} else {
@ -327,22 +336,46 @@ static void PLy_traceback(char** xmsg, char** tbmsg, int* tb_depth)
Py_DECREF(e);
}
/* Extract the error data from a SPIError */
/*
* Extract error code from SPIError's sqlstate attribute.
*/
static void PLy_get_spi_sqlerrcode(PyObject* exc, int* sqlerrcode)
{
PyObject* sqlstate;
char* buffer;
sqlstate = PyObject_GetAttrString(exc, "sqlstate");
if (sqlstate == NULL)
return;
buffer = PyString_AsString(sqlstate);
if (strlen(buffer) == 5 && strspn(buffer, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 5) {
*sqlerrcode = MAKE_SQLSTATE(buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]);
}
Py_DECREF(sqlstate);
}
/*
* Extract the error data from a SPIError
*/
static void PLy_get_spi_error_data(
PyObject* exc, int* sqlerrcode, char** detail, char** hint, char** query, int* position)
{
PyObject* spidata = NULL;
spidata = PyObject_GetAttrString(exc, "spidata");
if (spidata == NULL) {
goto cleanup;
if (spidata != NULL) {
PyArg_ParseTuple(spidata, "izzzi", sqlerrcode, detail, hint, query, position);
} else {
/*
* If there's no spidata, at least set the sqlerrcode. This can happen
* if someone explicitly raises a SPI exception from Python code.
*/
PLy_get_spi_sqlerrcode(exc, sqlerrcode);
}
if (!PyArg_ParseTuple(spidata, "izzzi", sqlerrcode, detail, hint, query, position)) {
goto cleanup;
}
cleanup:
PyErr_Clear();
/* no elog here, we simply won't report the errhint, errposition etc */
Py_XDECREF(spidata);
@ -413,7 +446,7 @@ void PLy_exception_set_plural(PyObject* exc, const char* fmt_singular, const cha
{
char buf[1024];
va_list ap;
errno_t rc;
errno_t rc = EOK;
va_start(ap, n);
rc = vsnprintf_s(buf, sizeof(buf), sizeof(buf) - 1, dngettext(TEXTDOMAIN, fmt_singular, fmt_plural, n), ap);

View File

@ -5,11 +5,6 @@
#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 elevel, const char* fmt, ...) __attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
extern void PLy_exception_set(PyObject* exc, const char* fmt, ...) __attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));

View File

@ -1,7 +1,7 @@
/*
* executing Python code
*
* src/pl/plpython/plpy_exec.c
* src/common/pl/plpython/plpy_exec.cpp
*/
#include "postgres.h"
@ -409,16 +409,16 @@ static void plpython_return_error_callback(void* arg)
static PyObject* PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure* proc, HeapTuple* rv)
{
TriggerData* tdata = (TriggerData*)fcinfo->context;
PyObject *pltname = NULL;
PyObject *pltevent = NULL;
PyObject *pltwhen = NULL;
PyObject *pltlevel = NULL;
PyObject *pltrelid = NULL;
PyObject *plttablename = NULL;
PyObject *plttableschema = NULL;
PyObject *pltargs = NULL;
PyObject *pytnew = NULL;
PyObject *pytold = NULL;
PyObject* pltname = NULL;
PyObject* pltevent = NULL;
PyObject* pltwhen = NULL;
PyObject* pltlevel = NULL;
PyObject* pltrelid = NULL;
PyObject* plttablename = NULL;
PyObject* plttableschema = NULL;
PyObject* pltargs = NULL;
PyObject* pytnew = NULL;
PyObject* pytold = NULL;
PyObject* volatile pltdata = NULL;
char* stroid = NULL;
@ -565,9 +565,7 @@ static HeapTuple PLy_modify_tuple(PLyProcedure* proc, PyObject* pltd, TriggerDat
{
PyObject* volatile plntup = NULL;
PyObject* volatile plkeys = NULL;
PyObject* volatile platt = NULL;
PyObject* volatile plval = NULL;
PyObject* volatile plstr = NULL;
HeapTuple rtup;
int natts, i, attn, atti;
int* volatile modattrs = NULL;
@ -580,7 +578,7 @@ static HeapTuple PLy_modify_tuple(PLyProcedure* proc, PyObject* pltd, TriggerDat
plerrcontext.previous = t_thrd.log_cxt.error_context_stack;
t_thrd.log_cxt.error_context_stack = &plerrcontext;
plntup = plkeys = platt = plval = plstr = NULL;
plntup = plkeys = plval = NULL;
modattrs = NULL;
modvalues = NULL;
modnulls = NULL;
@ -603,6 +601,7 @@ static HeapTuple PLy_modify_tuple(PLyProcedure* proc, PyObject* pltd, TriggerDat
tupdesc = tdata->tg_relation->rd_att;
for (i = 0; i < natts; i++) {
PyObject* platt = NULL;
char* plattstr = NULL;
platt = PyList_GetItem(plkeys, i);
@ -658,7 +657,6 @@ static HeapTuple PLy_modify_tuple(PLyProcedure* proc, PyObject* pltd, TriggerDat
Py_XDECREF(plntup);
Py_XDECREF(plkeys);
Py_XDECREF(plval);
Py_XDECREF(plstr);
if (modnulls != NULL)
pfree(modnulls);
@ -695,7 +693,7 @@ static void plpython_trigger_error_callback(void* arg)
static PyObject* PLy_procedure_call(PLyProcedure* proc, char* kargs, PyObject* vargs)
{
PyObject* rv = NULL;
int volatile save_subxact_level = list_length(explicit_subtransactions);
int volatile save_subxact_level = list_length(plpy_t_context.explicit_subtransactions);
PyDict_SetItemString(proc->globals, kargs, vargs);
@ -712,7 +710,7 @@ static PyObject* PLy_procedure_call(PLyProcedure* proc, char* kargs, PyObject* v
* started, you cannot *unnest* subtransactions, only *nest* them
* without closing.
*/
Assert(list_length(explicit_subtransactions) >= save_subxact_level);
Assert(list_length(plpy_t_context.explicit_subtransactions) >= save_subxact_level);
}
PG_CATCH();
{
@ -738,10 +736,10 @@ static void PLy_abort_open_subtransactions(int save_subxact_level)
{
Assert(save_subxact_level >= 0);
while (list_length(explicit_subtransactions) > save_subxact_level) {
while (list_length(plpy_t_context.explicit_subtransactions) > save_subxact_level) {
PLySubtransactionData* subtransactiondata = NULL;
Assert(explicit_subtransactions != NIL);
Assert(plpy_t_context.explicit_subtransactions != NIL);
ereport(WARNING, (errmsg("forcibly aborting a subtransaction that has not been exited")));
@ -749,8 +747,8 @@ static void PLy_abort_open_subtransactions(int save_subxact_level)
SPI_restore_connection();
subtransactiondata = (PLySubtransactionData*)linitial(explicit_subtransactions);
explicit_subtransactions = list_delete_first(explicit_subtransactions);
subtransactiondata = (PLySubtransactionData*)linitial(plpy_t_context.explicit_subtransactions);
plpy_t_context.explicit_subtransactions = list_delete_first(plpy_t_context.explicit_subtransactions);
MemoryContextSwitchTo(subtransactiondata->oldcontext);
t_thrd.utils_cxt.CurrentResourceOwner = subtransactiondata->oldowner;

View File

@ -11,6 +11,7 @@
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "utils/guc.h"
#include "utils/memutils.h"
@ -36,16 +37,16 @@
#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);
extern "C" void _PG_init(void);
extern "C" Datum plpython_validator(PG_FUNCTION_ARGS);
extern "C" Datum plpython_call_handler(PG_FUNCTION_ARGS);
extern "C" 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);
extern "C" Datum plpython2_validator(PG_FUNCTION_ARGS);
extern "C" Datum plpython2_call_handler(PG_FUNCTION_ARGS);
extern "C" Datum plpython2_inline_handler(PG_FUNCTION_ARGS);
#endif
PG_MODULE_MAGIC;
@ -70,19 +71,18 @@ static void PLy_pop_execution_context(void);
static const int plpython_python_version = PY_MAJOR_VERSION;
/* initialize global variables */
PyObject* PLy_interp_globals = NULL;
/* this doesn't need to be global; use PLy_current_execution_context() */
static PLyExecutionContext* PLy_execution_contexts = NULL;
static THR_LOCAL PLyExecutionContext* PLy_execution_contexts = NULL;
THR_LOCAL plpy_t_context_struct plpy_t_context = {0};
void _PG_init(void)
pthread_mutex_t Ply_LocaleMutex = PTHREAD_MUTEX_INITIALIZER;
void PG_init(void)
{
/* Be sure we do initialization only once (should be redundant now) */
static THR_LOCAL bool inited = false;
const int** version_ptr;
if (inited) {
if (plpy_t_context.inited) {
return;
}
@ -107,10 +107,25 @@ void _PG_init(void)
#if PY_MAJOR_VERSION >= 3
PyImport_AppendInittab("plpy", PyInit_plpy);
#endif
#if PY_MAJOR_VERSION < 3
if (!PyEval_ThreadsInitialized()) {
PyEval_InitThreads();
}
#endif
Py_Initialize();
#if PY_MAJOR_VERSION >= 3
PyImport_ImportModule("plpy");
#endif
#if PY_MAJOR_VERSION >= 3
if (!PyEval_ThreadsInitialized()) {
PyEval_InitThreads();
PyEval_SaveThread();
}
#endif
PLy_init_interp();
PLy_init_plpy();
if (PyErr_Occurred()) {
@ -119,11 +134,16 @@ void _PG_init(void)
init_procedure_caches();
explicit_subtransactions = NIL;
plpy_t_context.explicit_subtransactions = NIL;
PLy_execution_contexts = NULL;
inited = true;
plpy_t_context.inited = true;
}
void _PG_init(void)
{
PG_init();
}
/*
@ -140,14 +160,14 @@ void PLy_init_interp(void)
PLy_elog(ERROR, "could not import \"__main__\" module");
}
Py_INCREF(mainmod);
PLy_interp_globals = PyModule_GetDict(mainmod);
plpy_t_context.PLy_interp_globals = PyModule_GetDict(mainmod);
PLy_interp_safe_globals = PyDict_New();
if (PLy_interp_safe_globals == NULL) {
PLy_elog(ERROR, "could not create globals");
}
PyDict_SetItemString(PLy_interp_globals, "GD", PLy_interp_safe_globals);
PyDict_SetItemString(plpy_t_context.PLy_interp_globals, "GD", PLy_interp_safe_globals);
Py_DECREF(mainmod);
if (PLy_interp_globals == NULL || PyErr_Occurred()) {
if (plpy_t_context.PLy_interp_globals == NULL || PyErr_Occurred()) {
PLy_elog(ERROR, "could not initialize globals");
}
}
@ -159,23 +179,49 @@ Datum plpython_validator(PG_FUNCTION_ARGS)
Form_pg_proc procStruct;
bool is_trigger = false;
if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
PG_RETURN_VOID();
if (!u_sess->attr.attr_sql.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);
AutoMutexLock plpythonLock(&Ply_LocaleMutex);
if (plpy_t_context.Ply_LockLevel == 0) {
plpythonLock.lock();
}
procStruct = (Form_pg_proc)GETSTRUCT(tuple);
is_trigger = PLy_procedure_is_trigger(procStruct);
PyLock pyLock(&(plpy_t_context.Ply_LockLevel));
ReleaseSysCache(tuple);
PG_TRY();
{
PG_init();
/* We can't validate triggers against any particular table ... */
PLy_procedure_get(funcoid, InvalidOid, is_trigger);
/* 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);
if (is_trigger) {
elog(ERROR, "PL/Python does not support trigger");
}
ReleaseSysCache(tuple);
/* We can't validate triggers against any particular table ... */
PLy_procedure_get(funcoid, InvalidOid, is_trigger);
}
PG_CATCH();
{
pyLock.reset();
plpythonLock.unLock();
PG_RE_THROW();
}
PG_END_TRY();
PG_RETURN_VOID();
}
@ -189,29 +235,48 @@ Datum plpython2_validator(PG_FUNCTION_ARGS)
Datum plpython_call_handler(PG_FUNCTION_ARGS)
{
AutoMutexLock plpythonLock(&Ply_LocaleMutex);
if (plpy_t_context.Ply_LockLevel == 0) {
plpythonLock.lock();
}
Datum retval;
PLyExecutionContext* exec_ctx = NULL;
ErrorContextCallback plerrcontext;
/* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
if (SPI_connect() != SPI_OK_CONNECT) {
elog(ERROR, "SPI_connect failed");
PyLock pyLock(&(plpy_t_context.Ply_LockLevel));
PG_TRY();
{
PG_init();
/* Note: SPI_finish() happens in plpy_exec.cpp, which is dubious design */
if (SPI_connect() != SPI_OK_CONNECT) {
elog(ERROR, "SPI_connect failed");
}
/*
* Push execution context onto stack. It is important that this get
* popped again, so avoid putting anything that could throw error between
* here and the PG_TRY.
*/
exec_ctx = PLy_push_execution_context();
/*
* Setup error traceback support for ereport()
*/
plerrcontext.callback = plpython_error_callback;
plerrcontext.previous = t_thrd.log_cxt.error_context_stack;
t_thrd.log_cxt.error_context_stack = &plerrcontext;
}
/*
* Push execution context onto stack. It is important that this get
* popped again, so avoid putting anything that could throw error between
* here and the PG_TRY. (plpython_error_callback expects the stack entry
* to be there, so we have to make the context first.)
*/
exec_ctx = PLy_push_execution_context();
/*
* Setup error traceback support for ereport()
*/
plerrcontext.callback = plpython_error_callback;
plerrcontext.previous = t_thrd.log_cxt.error_context_stack;
t_thrd.log_cxt.error_context_stack = &plerrcontext;
PG_CATCH();
{
pyLock.reset();
plpythonLock.unLock();
PG_RE_THROW();
}
PG_END_TRY();
PG_TRY();
{
@ -219,6 +284,7 @@ Datum plpython_call_handler(PG_FUNCTION_ARGS)
PLyProcedure* proc = NULL;
if (CALLED_AS_TRIGGER(fcinfo)) {
elog(ERROR, "PL/Python does not support trigger");
Relation tgrel = ((TriggerData*)fcinfo->context)->tg_relation;
HeapTuple trv;
@ -236,14 +302,26 @@ Datum plpython_call_handler(PG_FUNCTION_ARGS)
{
PLy_pop_execution_context();
PyErr_Clear();
pyLock.reset();
plpythonLock.unLock();
PG_RE_THROW();
}
PG_END_TRY();
/* Pop the error context stack */
t_thrd.log_cxt.error_context_stack = plerrcontext.previous;
/* ... and then the execution context */
PLy_pop_execution_context();
PG_TRY();
{
/* Pop the error context stack */
t_thrd.log_cxt.error_context_stack = plerrcontext.previous;
/* ... and then the execution context */
PLy_pop_execution_context();
}
PG_CATCH();
{
pyLock.reset();
plpythonLock.unLock();
PG_RE_THROW();
}
PG_END_TRY();
return retval;
}
@ -257,43 +335,67 @@ Datum plpython2_call_handler(PG_FUNCTION_ARGS)
Datum plpython_inline_handler(PG_FUNCTION_ARGS)
{
AutoMutexLock plpythonLock(&Ply_LocaleMutex);
if (plpy_t_context.Ply_LockLevel == 0) {
plpythonLock.lock();
}
PyLock pyLock(&(plpy_t_context.Ply_LockLevel));
InlineCodeBlock* codeblock = (InlineCodeBlock*)DatumGetPointer(PG_GETARG_DATUM(0));
FunctionCallInfoData fake_fcinfo;
FmgrInfo flinfo;
PLyProcedure proc;
PLyExecutionContext* exec_ctx = NULL;
ErrorContextCallback plerrcontext;
errno_t rc = EOK;
/* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
if (SPI_connect() != SPI_OK_CONNECT) {
elog(ERROR, "SPI_connect failed");
PG_TRY();
{
PG_init();
/* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
if (SPI_connect() != SPI_OK_CONNECT) {
elog(ERROR, "SPI_connect failed");
}
rc = memset_s(&fake_fcinfo, sizeof(fake_fcinfo), 0, sizeof(fake_fcinfo));
securec_check(rc, "\0", "\0");
rc = memset_s(&flinfo, sizeof(flinfo), 0, sizeof(flinfo));
securec_check(rc, "\0", "\0");
fake_fcinfo.flinfo = &flinfo;
flinfo.fn_oid = InvalidOid;
flinfo.fn_mcxt = CurrentMemoryContext;
rc = memset_s(&proc, sizeof(PLyProcedure), 0, sizeof(PLyProcedure));
securec_check(rc, "\0", "\0");
proc.pyname = PLy_strdup("__plpython_inline_block");
proc.result.out.d.typoid = VOIDOID;
/*
* Push execution context onto stack. It is important that this get
* popped again, so avoid putting anything that could throw error between
* here and the PG_TRY. (plpython_inline_error_callback doesn't currently
* need the stack entry, but for consistency with plpython_call_handler we
* do it in this order.)
*/
exec_ctx = PLy_push_execution_context();
/*
* Setup error traceback support for ereport()
*/
plerrcontext.callback = plpython_inline_error_callback;
plerrcontext.previous = t_thrd.log_cxt.error_context_stack;
t_thrd.log_cxt.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;
/*
* Push execution context onto stack. It is important that this get
* popped again, so avoid putting anything that could throw error between
* here and the PG_TRY. (plpython_inline_error_callback doesn't currently
* need the stack entry, but for consistency with plpython_call_handler we
* do it in this order.)
*/
exec_ctx = PLy_push_execution_context();
/*
* Setup error traceback support for ereport()
*/
plerrcontext.callback = plpython_inline_error_callback;
plerrcontext.previous = t_thrd.log_cxt.error_context_stack;
t_thrd.log_cxt.error_context_stack = &plerrcontext;
PG_CATCH();
{
plpythonLock.unLock();
pyLock.reset();
PG_RE_THROW();
}
PG_END_TRY();
PG_TRY();
{
@ -306,17 +408,30 @@ Datum plpython_inline_handler(PG_FUNCTION_ARGS)
PLy_pop_execution_context();
PLy_procedure_delete(&proc);
PyErr_Clear();
plpythonLock.unLock();
pyLock.reset();
PG_RE_THROW();
}
PG_END_TRY();
/* Pop the error context stack */
t_thrd.log_cxt.error_context_stack = plerrcontext.previous;
/* ... and then the execution context */
PLy_pop_execution_context();
PG_TRY();
{
/* Now clean up the transient procedure we made */
PLy_procedure_delete(&proc);
/* Pop the error context stack */
t_thrd.log_cxt.error_context_stack = plerrcontext.previous;
/* ... and then the execution context */
PLy_pop_execution_context();
/* Now clean up the transient procedure we made */
PLy_procedure_delete(&proc);
}
PG_CATCH();
{
plpythonLock.unLock();
pyLock.reset();
PG_RE_THROW();
}
PG_END_TRY();
PG_RETURN_VOID();
}
@ -358,7 +473,7 @@ PLyExecutionContext* PLy_current_execution_context(void)
static PLyExecutionContext* PLy_push_execution_context(void)
{
PLyExecutionContext* context = PLy_malloc(sizeof(PLyExecutionContext));
PLyExecutionContext* context = (PLyExecutionContext*)PLy_malloc(sizeof(PLyExecutionContext));
context->curr_proc = NULL;
context->scratch_ctx = AllocSetContextCreate(u_sess->top_transaction_mem_cxt,

View File

@ -7,8 +7,25 @@
#include "plpy_procedure.h"
/* the interpreter's globals dict */
extern PyObject* PLy_interp_globals;
class PyLock {
public:
PyLock(int* lockLevel) : m_lockLevel(lockLevel)
{
(*m_lockLevel)++;
}
~PyLock()
{
(*m_lockLevel)--;
}
void reset()
{
(*m_lockLevel) = 0;
}
private:
int* m_lockLevel;
};
/*
* A stack of PL/Python execution contexts. Each time user-defined Python code

View File

@ -108,6 +108,6 @@ static PyObject* PLy_plan_status(PyObject* self, PyObject* args)
Py_INCREF(Py_True);
return Py_True;
}
PLy_exception_set(PLy_exc_error, "plan.status takes no arguments");
PLy_exception_set(plpy_t_context.PLy_exc_error, "plan.status takes no arguments");
return NULL;
}

View File

@ -1,7 +1,7 @@
/*
* the plpy module
*
* src/pl/plpython/plpy_plpymodule.c
* src/common/pl/plpython/plpy_plpymodule.cpp
*/
#include "postgres.h"
@ -21,8 +21,6 @@
#include "plpy_spi.h"
#include "plpy_subxactobject.h"
HTAB* PLy_spi_exceptions = NULL;
static void PLy_add_exceptions(PyObject* plpy);
static void PLy_generate_spi_exceptions(PyObject* mod, PyObject* base);
@ -88,6 +86,7 @@ static PyMethodDef PLy_methods[] = {
*/
{"cursor", PLy_cursor, METH_VARARGS, NULL},
/* Sentinel */
{NULL, NULL, 0, NULL}};
static PyMethodDef PLy_exc_methods[] = {{NULL, NULL, 0, NULL}};
@ -133,9 +132,9 @@ PyMODINIT_FUNC PyInit_plpy(void)
void PLy_init_plpy(void)
{
PyObject *main_mod = NULL;
PyObject *main_dict = NULL;
PyObject *plpy_mod = NULL;
PyObject* main_mod = NULL;
PyObject* main_dict = NULL;
PyObject* plpy_mod = NULL;
#if PY_MAJOR_VERSION < 3
PyObject* plpy = NULL;
@ -180,8 +179,8 @@ static void PLy_add_exceptions(PyObject* plpy)
#else
excmod = PyModule_Create(&PLy_exc_module);
#endif
if (PyModule_AddObject(plpy, "spiexceptions", excmod) < 0)
PLy_elog(ERROR, "could not add the spiexceptions module");
if (excmod == NULL)
PLy_elog(ERROR, "could not create the spiexceptions module");
/*
* XXX it appears that in some circumstances the reference count of the
@ -194,27 +193,35 @@ static void PLy_add_exceptions(PyObject* plpy)
* backend.
*/
Py_INCREF(excmod);
if (PyModule_AddObject(plpy, "spiexceptions", excmod) < 0) {
PLy_elog(ERROR, "could not add the spiexceptions module");
}
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);
if (PLy_exc_error == NULL || PLy_exc_fatal == NULL || PLy_exc_spi_error == NULL)
plpy_t_context.PLy_exc_error = PyErr_NewException("plpy.Error", NULL, NULL);
plpy_t_context.PLy_exc_fatal = PyErr_NewException("plpy.Fatal", NULL, NULL);
plpy_t_context.PLy_exc_spi_error = PyErr_NewException("plpy.SPIError", NULL, NULL);
if (plpy_t_context.PLy_exc_error == NULL || plpy_t_context.PLy_exc_fatal == NULL ||
plpy_t_context.PLy_exc_spi_error == NULL)
PLy_elog(ERROR, "could not create the base SPI exceptions");
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);
Py_INCREF(plpy_t_context.PLy_exc_error);
PyModule_AddObject(plpy, "Error", plpy_t_context.PLy_exc_error);
Py_INCREF(plpy_t_context.PLy_exc_fatal);
PyModule_AddObject(plpy, "Fatal", plpy_t_context.PLy_exc_fatal);
Py_INCREF(plpy_t_context.PLy_exc_spi_error);
PyModule_AddObject(plpy, "SPIError", plpy_t_context.PLy_exc_spi_error);
errno_t rc = EOK;
rc = memset_s(&hash_ctl, sizeof(hash_ctl), 0, sizeof(hash_ctl));
securec_check(rc, "\0", "\0");
MemSet_S(&hash_ctl, sizeof(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);
plpy_t_context.PLy_spi_exceptions = hash_create("Plpy SPI exceptions", 512, &hash_ctl, HASH_ELEM | HASH_FUNCTION);
PLy_generate_spi_exceptions(excmod, PLy_exc_spi_error);
PLy_generate_spi_exceptions(excmod, plpy_t_context.PLy_exc_spi_error);
}
/*
@ -241,8 +248,10 @@ static void PLy_generate_spi_exceptions(PyObject* mod, PyObject* base)
PyDict_SetItemString(dict, "sqlstate", sqlstate);
Py_DECREF(sqlstate);
exc = PyErr_NewException(exception_map[i].name, base, dict);
Py_INCREF(exc);
PyModule_AddObject(mod, exception_map[i].classname, exc);
entry = hash_search(PLy_spi_exceptions, &exception_map[i].sqlstate, HASH_ENTER, &found);
entry = (PLyExceptionEntry*)hash_search(
plpy_t_context.PLy_spi_exceptions, &exception_map[i].sqlstate, HASH_ENTER, &found);
entry->exc = exc;
Assert(!found);
}
@ -383,7 +392,7 @@ static PyObject* PLy_output(volatile int level, PyObject* self, PyObject* args)
Py_XDECREF(so);
/* Make Python raise the exception */
PLy_exception_set(PLy_exc_error, "%s", edata->message);
PLy_exception_set(plpy_t_context.PLy_exc_error, "%s", edata->message);
return NULL;
}
PG_END_TRY();

View File

@ -7,9 +7,6 @@
#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

48
src/common/pl/plpython/plpy_procedure.cpp Executable file → Normal file
View File

@ -1,7 +1,7 @@
/*
* Python procedure manipulation for plpython
*
* src/pl/plpython/plpy_procedure.c
* src/common/pl/plpython/plpy_procedure.cpp
*/
#include "postgres.h"
@ -22,7 +22,6 @@
#include "plpy_elog.h"
#include "plpy_main.h"
static HTAB* PLy_procedure_cache = NULL;
static PLyProcedure* PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger);
static bool PLy_procedure_argument_valid(PLyTypeInfo* arg);
@ -32,12 +31,16 @@ static char* PLy_procedure_munge_source(const char* name, const char* src);
void init_procedure_caches(void)
{
HASHCTL hash_ctl;
errno_t rc = EOK;
rc = memset_s(&hash_ctl, sizeof(hash_ctl), 0, sizeof(hash_ctl));
securec_check(rc, "\0", "\0");
MemSet_S(&hash_ctl, sizeof(hash_ctl), 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(PLyProcedureKey);
hash_ctl.entrysize = sizeof(PLyProcedureEntry);
hash_ctl.hash = tag_hash;
PLy_procedure_cache = hash_create("PL/Python procedures", 32, &hash_ctl, HASH_ELEM | HASH_FUNCTION);
plpy_t_context.PLy_procedure_cache =
hash_create("PL/Python procedures", 32, &hash_ctl, HASH_ELEM | HASH_FUNCTION);
}
/*
@ -87,7 +90,7 @@ PLyProcedure* PLy_procedure_get(Oid fn_oid, Oid fn_rel, bool is_trigger)
if (use_cache) {
key.fn_oid = fn_oid;
key.fn_rel = fn_rel;
entry = hash_search(PLy_procedure_cache, &key, HASH_ENTER, &found);
entry = (PLyProcedureEntry*)hash_search(plpy_t_context.PLy_procedure_cache, &key, HASH_ENTER, &found);
proc = entry->proc;
}
@ -111,7 +114,7 @@ PLyProcedure* PLy_procedure_get(Oid fn_oid, Oid fn_rel, bool is_trigger)
{
/* Do not leave an uninitialised entry in the cache */
if (use_cache)
hash_search(PLy_procedure_cache, &key, HASH_REMOVE, NULL);
hash_search(plpy_t_context.PLy_procedure_cache, &key, HASH_REMOVE, NULL);
PG_RE_THROW();
}
PG_END_TRY();
@ -141,10 +144,16 @@ static PLyProcedure* PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is
"__plpython_procedure_%s_%u",
NameStr(procStruct->proname),
fn_oid);
if (rv >= sizeof(procName) || rv < 0)
if (rv >= (int)sizeof(procName) || rv < 0)
elog(ERROR, "procedure name would overrun buffer");
proc = PLy_malloc(sizeof(PLyProcedure));
/* Replace any not-legal-in-Python-names characters with '_' */
for (char* ptr = procName; *ptr; ptr++) {
if (!((*ptr >= 'A' && *ptr <= 'Z') || (*ptr >= 'a' && *ptr <= 'z') || (*ptr >= '0' && *ptr <= '9')))
*ptr = '_';
}
proc = (PLyProcedure*)PLy_malloc(sizeof(PLyProcedure));
proc->proname = PLy_strdup(NameStr(procStruct->proname));
proc->pyname = PLy_strdup(procName);
proc->fn_xmin = HeapTupleGetRawXmin(procTup);
@ -213,8 +222,8 @@ static PLyProcedure* PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is
*/
if (procStruct->pronargs) {
Oid* types = NULL;
char **names = NULL;
char *modes = NULL;
char** names = NULL;
char* modes = NULL;
int i, pos, total;
/* extract argument type info from the pg_proc tuple */
@ -306,7 +315,7 @@ void PLy_procedure_compile(PLyProcedure* proc, const char* src)
PyObject* crv = NULL;
char* msrc = NULL;
proc->globals = PyDict_Copy(PLy_interp_globals);
proc->globals = PyDict_Copy(plpy_t_context.PLy_interp_globals);
/*
* SD is private preserved data between calls. GD is global data shared by
@ -333,8 +342,8 @@ void PLy_procedure_compile(PLyProcedure* proc, const char* src)
/*
* compile a call to the function
*/
clen = snprintf(call, sizeof(call), "%s()", proc->pyname);
if (clen < 0 || clen >= sizeof(call))
clen = snprintf_s(call, sizeof(call), sizeof(call) - 1, "%s()", proc->pyname);
if (clen < 0 || clen >= (int)sizeof(call))
elog(ERROR, "string would overflow buffer");
proc->code = Py_CompileString(call, "<string>", Py_eval_input);
if (proc->code != NULL)
@ -442,19 +451,20 @@ static bool PLy_procedure_valid(PLyProcedure* proc, HeapTuple procTup)
static char* PLy_procedure_munge_source(const char* name, const char* src)
{
char *mrc = NULL;
char *mp = NULL;
char* mrc = NULL;
char* mp = NULL;
const char* sp = NULL;
size_t mlen, plen;
size_t mlen;
int plen;
/*
* room for function source and the def statement
*/
mlen = (strlen(src) * 2) + strlen(name) + 16;
mrc = palloc(mlen);
plen = snprintf(mrc, mlen, "def %s():\n\t", name);
Assert(plen >= 0 && plen < mlen);
mrc = (char*)palloc(mlen);
plen = snprintf_s(mrc, mlen, mlen - 1, "def %s():\n\t", name);
Assert(plen >= 0 && (size_t)plen < mlen);
sp = src;
mp = mrc + plen;

View File

@ -20,9 +20,13 @@ static PyObject* PLy_result_nrows(PyObject* self, PyObject* args);
static PyObject* PLy_result_status(PyObject* self, PyObject* args);
static Py_ssize_t PLy_result_length(PyObject* arg);
static PyObject* PLy_result_item(PyObject* arg, Py_ssize_t idx);
#if PY_MAJOR_VERSION < 3
static PyObject* PLy_result_slice(PyObject* arg, Py_ssize_t lidx, Py_ssize_t hidx);
static int PLy_result_ass_item(PyObject* arg, Py_ssize_t idx, PyObject* item);
static int PLy_result_ass_slice(PyObject* rg, Py_ssize_t lidx, Py_ssize_t hidx, PyObject* slice);
static int PLy_result_ass_slice(PyObject* arg, Py_ssize_t lidx, Py_ssize_t hidx, PyObject* slice);
#else
static PyObject* PLy_result_str(PyObject* arg);
#endif
static PyObject* PLy_result_subscript(PyObject* arg, PyObject* item);
static int PLy_result_ass_subscript(PyObject* self, PyObject* item, PyObject* value);
@ -33,9 +37,15 @@ static PySequenceMethods PLy_result_as_sequence = {
NULL, /* sq_concat */
NULL, /* sq_repeat */
PLy_result_item, /* sq_item */
#if PY_MAJOR_VERSION < 3
PLy_result_slice, /* sq_slice */
PLy_result_ass_item, /* sq_ass_item */
PLy_result_ass_slice, /* sq_ass_slice */
PLy_result_ass_slice /* sq_ass_slice */
#else
NULL, /* sq_slice */
NULL, /* sq_ass_item */
NULL /* sq_ass_slice */
#endif
};
static PyMappingMethods PLy_result_as_mapping = {
@ -70,7 +80,11 @@ static PyTypeObject PLy_ResultType = {
&PLy_result_as_mapping, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
#if PY_MAJOR_VERSION < 3
0, /* tp_str */
#else
&PLy_result_str, /* tp_str */
#endif
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
@ -129,7 +143,7 @@ static PyObject* PLy_result_colnames(PyObject* self, PyObject* unused)
int i;
if (!ob->tupdesc) {
PLy_exception_set(PLy_exc_error, "command did not produce a result set");
PLy_exception_set(plpy_t_context.PLy_exc_error, "command did not produce a result set");
return NULL;
}
@ -147,7 +161,7 @@ static PyObject* PLy_result_coltypes(PyObject* self, PyObject* unused)
int i;
if (!ob->tupdesc) {
PLy_exception_set(PLy_exc_error, "command did not produce a result set");
PLy_exception_set(plpy_t_context.PLy_exc_error, "command did not produce a result set");
return NULL;
}
@ -165,7 +179,7 @@ static PyObject* PLy_result_coltypmods(PyObject* self, PyObject* unused)
int i;
if (!ob->tupdesc) {
PLy_exception_set(PLy_exc_error, "command did not produce a result set");
PLy_exception_set(plpy_t_context.PLy_exc_error, "command did not produce a result set");
return NULL;
}
@ -210,6 +224,7 @@ static PyObject* PLy_result_item(PyObject* arg, Py_ssize_t idx)
return rv;
}
#if PY_MAJOR_VERSION < 3
static int PLy_result_ass_item(PyObject* arg, Py_ssize_t idx, PyObject* item)
{
int rv;
@ -235,6 +250,23 @@ static int PLy_result_ass_slice(PyObject* arg, Py_ssize_t lidx, Py_ssize_t hidx,
rv = PyList_SetSlice(ob->rows, lidx, hidx, slice);
return rv;
}
#else
static PyObject* PLy_result_str(PyObject* arg)
{
PLyResultObject* ob = (PLyResultObject*)arg;
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromFormat(
"<%s status=%S nrows=%S rows=%S>", Py_TYPE(ob)->tp_name, ob->status, ob->nrows, ob->rows);
#else
return PyString_FromFormat("<%s status=%ld nrows=%ld rows=%s>",
ob->ob_type->tp_name,
PyInt_AsLong(ob->status),
PyInt_AsLong(ob->nrows),
PyString_AsString(PyObject_Str(ob->rows)));
#endif
}
#endif
static PyObject* PLy_result_subscript(PyObject* arg, PyObject* item)
{

View File

@ -1,7 +1,7 @@
/*
* interface to SPI functions
*
* src/pl/plpython/plpy_spi.c
* src/common/pl/plpython/plpy_spi.cpp
*/
#include "postgres.h"
@ -59,9 +59,9 @@ PyObject* PLy_spi_prepare(PyObject* self, PyObject* args)
nargs = list ? PySequence_Length(list) : 0;
plan->nargs = nargs;
plan->types = nargs ? PLy_malloc(sizeof(Oid) * nargs) : NULL;
plan->values = nargs ? PLy_malloc(sizeof(Datum) * nargs) : NULL;
plan->args = nargs ? PLy_malloc(sizeof(PLyTypeInfo) * nargs) : NULL;
plan->types = nargs ? (Oid*)PLy_malloc(sizeof(Oid) * nargs) : NULL;
plan->values = nargs ? (Datum*)PLy_malloc(sizeof(Datum) * nargs) : NULL;
plan->args = nargs ? (PLyTypeInfo*)PLy_malloc(sizeof(PLyTypeInfo) * nargs) : NULL;
oldcontext = CurrentMemoryContext;
oldowner = t_thrd.utils_cxt.CurrentResourceOwner;
@ -169,7 +169,7 @@ PyObject* PLy_spi_execute(PyObject* self, PyObject* args)
if (PyArg_ParseTuple(args, "O|Ol", &plan, &list, &limit) && is_PLyPlanObject(plan))
return PLy_spi_execute_plan(plan, list, limit);
PLy_exception_set(PLy_exc_error, "plpy.execute expected a query or a plan");
PLy_exception_set(plpy_t_context.PLy_exc_error, "plpy.execute expected a query or a plan");
return NULL;
}
@ -224,7 +224,7 @@ static PyObject* PLy_spi_execute_plan(PyObject* ob, PyObject* list, long limit)
volatile int j;
if (nargs > 0)
nulls = palloc(nargs * sizeof(char));
nulls = (char*)palloc(nargs * sizeof(char));
else
nulls = NULL;
@ -270,7 +270,7 @@ static PyObject* PLy_spi_execute_plan(PyObject* ob, PyObject* list, long limit)
* cleanup plan->values array
*/
for (k = 0; k < nargs; k++) {
if (plan->args[k].out.d.typbyval == NULL && (plan->values[k] != PointerGetDatum(NULL))) {
if (!plan->args[k].out.d.typbyval && (plan->values[k] != PointerGetDatum(NULL))) {
pfree(DatumGetPointer(plan->values[k]));
plan->values[k] = PointerGetDatum(NULL);
}
@ -289,7 +289,7 @@ static PyObject* PLy_spi_execute_plan(PyObject* ob, PyObject* list, long limit)
}
if (rv < 0) {
PLy_exception_set(PLy_exc_spi_error, "SPI_execute_plan failed: %s", SPI_result_code_string(rv));
PLy_exception_set(plpy_t_context.PLy_exc_spi_error, "SPI_execute_plan failed: %s", SPI_result_code_string(rv));
return NULL;
}
@ -327,7 +327,7 @@ static PyObject* PLy_spi_execute_query(char* query, long limit)
if (rv < 0) {
Py_XDECREF(ret);
PLy_exception_set(PLy_exc_spi_error, "SPI_execute failed: %s", SPI_result_code_string(rv));
PLy_exception_set(plpy_t_context.PLy_exc_spi_error, "SPI_execute failed: %s", SPI_result_code_string(rv));
return NULL;
}
@ -371,6 +371,7 @@ static PyObject* PLy_spi_execute_fetch_result(SPITupleTable* tuptable, int rows,
if (rows) {
Py_DECREF(result->rows);
/* Return a new list of length len on success, or NULL on failure. */
result->rows = PyList_New(rows);
PLy_input_tuple_funcs(&args, tuptable->tupdesc);
@ -385,7 +386,7 @@ static PyObject* PLy_spi_execute_fetch_result(SPITupleTable* tuptable, int rows,
{
MemoryContextSwitchTo(oldcontext);
if (!PyErr_Occurred())
PLy_exception_set(PLy_exc_error, "unrecognized error in PLy_spi_execute_fetch_result");
PLy_exception_set(plpy_t_context.PLy_exc_error, "unrecognized error in PLy_spi_execute_fetch_result");
PLy_typeinfo_dealloc(&args);
SPI_freetuptable(tuptable);
Py_DECREF(result);
@ -470,10 +471,10 @@ void PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldown
SPI_restore_connection();
/* Look up the correct exception */
entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode), HASH_FIND, NULL);
entry = (PLyExceptionEntry*)hash_search(plpy_t_context.PLy_spi_exceptions, &(edata->sqlerrcode), HASH_FIND, NULL);
/* We really should find it, but just in case have a fallback */
Assert(entry != NULL);
exc = entry ? entry->exc : PLy_exc_spi_error;
exc = entry ? entry->exc : plpy_t_context.PLy_exc_spi_error;
/* Make Python raise the exception */
PLy_spi_exception_set(exc, edata);
FreeErrorData(edata);

View File

@ -33,8 +33,6 @@
#include "plpy_elog.h"
List* explicit_subtransactions = NIL;
static void PLy_subtransaction_dealloc(PyObject* subxact);
static PyObject* PLy_subtransaction_enter(PyObject* self, PyObject* unused);
static PyObject* PLy_subtransaction_exit(PyObject* self, PyObject* args);
@ -132,7 +130,7 @@ static PyObject* PLy_subtransaction_enter(PyObject* self, PyObject* unused)
subxact->started = true;
oldcontext = CurrentMemoryContext;
subxactdata = PLy_malloc(sizeof(*subxactdata));
subxactdata = (PLySubtransactionData*)PLy_malloc(sizeof(*subxactdata));
subxactdata->oldcontext = oldcontext;
subxactdata->oldowner = t_thrd.utils_cxt.CurrentResourceOwner;
@ -140,7 +138,7 @@ static PyObject* PLy_subtransaction_enter(PyObject* self, PyObject* unused)
/* Do not want to leave the previous memory context */
MemoryContextSwitchTo(oldcontext);
explicit_subtransactions = lcons(subxactdata, explicit_subtransactions);
plpy_t_context.explicit_subtransactions = lcons(subxactdata, plpy_t_context.explicit_subtransactions);
Py_INCREF(self);
return self;
@ -178,7 +176,7 @@ static PyObject* PLy_subtransaction_exit(PyObject* self, PyObject* args)
return NULL;
}
if (explicit_subtransactions == NIL) {
if (plpy_t_context.explicit_subtransactions == NIL) {
PLy_exception_set(PyExc_ValueError, "there is no subtransaction to exit from");
return NULL;
}
@ -192,8 +190,8 @@ static PyObject* PLy_subtransaction_exit(PyObject* self, PyObject* args)
ReleaseCurrentSubTransaction();
}
subxactdata = (PLySubtransactionData*)linitial(explicit_subtransactions);
explicit_subtransactions = list_delete_first(explicit_subtransactions);
subxactdata = (PLySubtransactionData*)linitial(plpy_t_context.explicit_subtransactions);
plpy_t_context.explicit_subtransactions = list_delete_first(plpy_t_context.explicit_subtransactions);
MemoryContextSwitchTo(subxactdata->oldcontext);
t_thrd.utils_cxt.CurrentResourceOwner = subxactdata->oldowner;

View File

@ -1,5 +1,5 @@
/*
* src/pl/plpython/plpy_subxactobject.h
* src/common/pl/plpython/plpy_subxactobject.h
*/
#ifndef PLPY_SUBXACTOBJECT
@ -8,9 +8,6 @@
#include "nodes/pg_list.h"
#include "utils/resowner.h"
/* a list of nested explicit subtransactions */
extern List* explicit_subtransactions;
typedef struct PLySubtransactionObject {
PyObject_HEAD bool started;
bool exited;

158
src/common/pl/plpython/plpy_typeio.cpp Executable file → Normal file
View File

@ -1,7 +1,7 @@
/*
* transforming Datums to Python objects and vice versa
*
* src/pl/plpython/plpy_typeio.c
* src/common/pl/plpython/plpy_typeio.cpp
*/
#include "postgres.h"
@ -38,9 +38,12 @@ 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* PLyLong_FromOid(PLyDatumToOb* arg, Datum d);
static PyObject* PLyBytes_FromBytea(PLyDatumToOb* arg, Datum d);
static PyObject* PLyString_FromDatum(PLyDatumToOb* arg, Datum d);
static PyObject* PLyList_FromArray(PLyDatumToOb* arg, Datum d);
static PyObject* PLyList_FromArray_recurse(
PLyDatumToOb* elm, int* dims, int ndim, int dim, char** dataptr_p, bits8** bitmap_p, int* bitmask_p);
/* conversion from Python objects to Datums */
static Datum PLyObject_ToBool(PLyObToDatum* arg, int32 typmod, PyObject* plrv);
@ -121,7 +124,7 @@ void PLy_input_tuple_funcs(PLyTypeInfo* arg, TupleDesc desc)
if (arg->in.r.atts)
PLy_free(arg->in.r.atts);
arg->in.r.natts = desc->natts;
arg->in.r.atts = PLy_malloc0(desc->natts * sizeof(PLyDatumToOb));
arg->in.r.atts = (PLyDatumToOb*)PLy_malloc0(desc->natts * sizeof(PLyDatumToOb));
}
/* Can this be an unnamed tuple? If not, then an Assert would be enough */
@ -181,7 +184,7 @@ void PLy_output_tuple_funcs(PLyTypeInfo* arg, TupleDesc desc)
if (arg->out.r.atts)
PLy_free(arg->out.r.atts);
arg->out.r.natts = desc->natts;
arg->out.r.atts = PLy_malloc0(desc->natts * sizeof(PLyDatumToOb));
arg->out.r.atts = (PLyObToDatum*)PLy_malloc0(desc->natts * sizeof(PLyObToDatum));
}
Assert(OidIsValid(desc->tdtypeid));
@ -374,7 +377,7 @@ static void PLy_output_datum_func2(PLyObToDatum* arg, HeapTuple typeTup)
errmsg("PL/Python functions cannot return type %s", format_type_be(arg->typoid)),
errdetail("PL/Python does not support conversion to arrays of row types.")));
arg->elm = PLy_malloc0(sizeof(*arg->elm));
arg->elm = (PLyObToDatum*)PLy_malloc0(sizeof(*arg->elm));
arg->elm->func = arg->func;
arg->func = PLySequence_ToArray;
@ -429,6 +432,9 @@ static void PLy_input_datum_func2(PLyDatumToOb* arg, Oid typeOid, HeapTuple type
case INT8OID:
arg->func = PLyLong_FromInt64;
break;
case OIDOID:
arg->func = PLyLong_FromOid;
break;
case BYTEAOID:
arg->func = PLyBytes_FromBytea;
break;
@ -441,7 +447,7 @@ static void PLy_input_datum_func2(PLyDatumToOb* arg, Oid typeOid, HeapTuple type
char dummy_delim;
Oid funcid;
arg->elm = PLy_malloc0(sizeof(*arg->elm));
arg->elm = (PLyDatumToOb*)PLy_malloc0(sizeof(*arg->elm));
arg->elm->func = arg->func;
arg->func = PLyList_FromArray;
arg->elm->typoid = element_type;
@ -512,6 +518,11 @@ static PyObject* PLyLong_FromInt64(PLyDatumToOb* arg, Datum d)
return PyLong_FromLong(DatumGetInt64(d));
}
static PyObject* PLyLong_FromOid(PLyDatumToOb* arg, Datum d)
{
return PyLong_FromUnsignedLong(DatumGetObjectId(d));
}
static PyObject* PLyBytes_FromBytea(PLyDatumToOb* arg, Datum d)
{
text* txt = DatumGetByteaP(d);
@ -534,39 +545,94 @@ static PyObject* PLyList_FromArray(PLyDatumToOb* arg, Datum d)
{
ArrayType* array = DatumGetArrayTypeP(d);
PLyDatumToOb* elm = arg->elm;
PyObject* list = NULL;
int length;
int lbound;
int i;
int ndim;
int* dims;
char* dataptr;
bits8* bitmap;
int bitmask;
if (ARR_NDIM(array) == 0)
return PyList_New(0);
if (ARR_NDIM(array) != 1)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert multidimensional array to Python list"),
errdetail("PL/Python only supports one-dimensional arrays.")));
/* Array dimensions and left bounds */
ndim = ARR_NDIM(array);
dims = ARR_DIMS(array);
Assert(ndim < MAXDIM);
length = ARR_DIMS(array)[0];
lbound = ARR_LBOUND(array)[0];
list = PyList_New(length);
if (list == NULL)
PLy_elog(ERROR, "could not create new Python list");
/*
* We iterate the SQL array in the physical order it's stored in the
* datum. For example, for a 3-dimensional array the order of iteration
* would be the following: [0,0,0] elements through [0,0,k], then [0,1,0]
* through [0,1,k] till [0,m,k], then [1,0,0] through [1,0,k] till
* [1,m,k], and so on.
*
* In Python, there are no multi-dimensional lists as such, but they are
* represented as a list of lists. So a 3-d array of [n,m,k] elements is a
* list of n m-element arrays, each element of which is k-element array.
* PLyList_FromArray_recurse() builds the Python list for a single
* dimension, and recurses for the next inner dimension.
*/
dataptr = ARR_DATA_PTR(array);
bitmap = ARR_NULLBITMAP(array);
bitmask = 1;
for (i = 0; i < length; i++) {
Datum elem;
bool isnull = false;
int offset;
return PLyList_FromArray_recurse(elm, dims, ndim, 0, &dataptr, &bitmap, &bitmask);
}
offset = lbound + i;
elem = array_ref(array, 1, &offset, arg->typlen, elm->typlen, elm->typbyval, elm->typalign, &isnull);
if (isnull) {
Py_INCREF(Py_None);
PyList_SET_ITEM(list, i, Py_None);
} else {
PyList_SET_ITEM(list, i, elm->func(elm, elem));
static PyObject* PLyList_FromArray_recurse(
PLyDatumToOb* elm, int* dims, int ndim, int dim, char** dataptr_p, bits8** bitmap_p, int* bitmask_p)
{
int i;
PyObject* list;
list = PyList_New(dims[dim]);
if (!list)
return NULL;
if (dim < ndim - 1) {
/* Outer dimension. Recurse for each inner slice. */
for (i = 0; i < dims[dim]; i++) {
PyObject* sublist;
sublist = PLyList_FromArray_recurse(elm, dims, ndim, dim + 1, dataptr_p, bitmap_p, bitmask_p);
PyList_SET_ITEM(list, i, sublist);
}
} else {
/*
* Innermost dimension. Fill the list with the values from the array
* for this slice.
*/
char* dataptr = *dataptr_p;
bits8* bitmap = *bitmap_p;
int bitmask = *bitmask_p;
for (i = 0; i < dims[dim]; i++) {
/* checking for NULL */
if (bitmap && (*bitmap & bitmask) == 0) {
Py_INCREF(Py_None);
PyList_SET_ITEM(list, i, Py_None);
} else {
Datum itemvalue;
itemvalue = fetch_att(dataptr, elm->typbyval, elm->typlen);
PyList_SET_ITEM(list, i, elm->func(elm, itemvalue));
dataptr = att_addlength_pointer(dataptr, elm->typlen, dataptr);
dataptr = (char*)att_align_nominal(dataptr, elm->typalign);
}
/* advance bitmap pointer if any */
if (bitmap) {
bitmask <<= 1;
if (bitmask == 0x100 /* (1<<8) */) {
bitmap++;
bitmask = 1;
}
}
}
*dataptr_p = dataptr;
*bitmap_p = bitmap;
*bitmask_p = bitmask;
}
return list;
@ -612,7 +678,7 @@ static Datum PLyObject_ToBytea(PLyObToDatum* arg, int32 typmod, PyObject* plrv)
char* plrv_sc = PyBytes_AsString(plrv_so);
size_t len = PyBytes_Size(plrv_so);
size_t size = len + VARHDRSZ;
bytea* result = palloc(size);
bytea* result = (bytea*)palloc(size);
SET_VARSIZE(result, size);
errno_t rc;
@ -650,7 +716,9 @@ static Datum PLyObject_ToComposite(PLyObToDatum* arg, int32 typmod, PyObject* pl
elog(ERROR, "received unnamed record type as input");
/* Create a dummy PLyTypeInfo */
MemSet(&info, 0, sizeof(PLyTypeInfo));
errno_t rc = memset_s(&info, sizeof(PLyTypeInfo), 0, sizeof(PLyTypeInfo));
securec_check(rc, "\0", "\0");
PLy_typeinfo_init(&info);
/* Mark it as needing output routines lookup */
info.is_rowtype = 2;
@ -726,6 +794,7 @@ static Datum PLyObject_ToDatum(PLyObToDatum* arg, int32 typmod, PyObject* plrv)
static Datum PLySequence_ToArray(PLyObToDatum* arg, int32 typmod, PyObject* plrv)
{
ArrayType* array = NULL;
Datum rv;
int i;
Datum* elems = NULL;
bool* nulls = NULL;
@ -738,8 +807,8 @@ static Datum PLySequence_ToArray(PLyObToDatum* arg, int32 typmod, PyObject* plrv
PLy_elog(ERROR, "return value of function with array return type is not a Python sequence");
len = PySequence_Length(plrv);
elems = palloc(sizeof(*elems) * len);
nulls = palloc(sizeof(*nulls) * len);
elems = (Datum*)palloc(sizeof(*elems) * len);
nulls = (bool*)palloc(sizeof(*nulls) * len);
for (i = 0; i < len; i++) {
PyObject* obj = PySequence_GetItem(plrv, i);
@ -768,7 +837,14 @@ static Datum PLySequence_ToArray(PLyObToDatum* arg, int32 typmod, PyObject* plrv
arg->elm->typlen,
arg->elm->typbyval,
arg->elm->typalign);
return PointerGetDatum(array);
/*
* If the result type is a domain of array, the resulting array must be
* checked.
*/
rv = PointerGetDatum(array);
if (get_typtype(arg->typoid) == TYPTYPE_DOMAIN)
domain_check(rv, false, arg->typoid, &arg->typfunc.fn_extra, arg->typfunc.fn_mcxt);
return rv;
}
static Datum PLyString_ToComposite(PLyTypeInfo* info, TupleDesc desc, PyObject* string)
@ -801,8 +877,8 @@ static Datum PLyMapping_ToComposite(PLyTypeInfo* info, TupleDesc desc, PyObject*
Assert(info->is_rowtype == 1);
/* Build tuple */
values = palloc(sizeof(Datum) * desc->natts);
nulls = palloc(sizeof(bool) * desc->natts);
values = (Datum*)palloc(sizeof(Datum) * desc->natts);
nulls = (bool*)palloc(sizeof(bool) * desc->natts);
for (i = 0; i < desc->natts; ++i) {
char* key = NULL;
PyObject* volatile value = NULL;
@ -882,8 +958,8 @@ static Datum PLySequence_ToComposite(PLyTypeInfo* info, TupleDesc desc, PyObject
Assert(info->is_rowtype == 1);
/* Build tuple */
values = palloc(sizeof(Datum) * desc->natts);
nulls = palloc(sizeof(bool) * desc->natts);
values = (Datum*)palloc(sizeof(Datum) * desc->natts);
nulls = (bool*)palloc(sizeof(bool) * desc->natts);
idx = 0;
for (i = 0; i < desc->natts; ++i) {
PyObject* volatile value = NULL;
@ -942,8 +1018,8 @@ static Datum PLyGenericObject_ToComposite(PLyTypeInfo* info, TupleDesc desc, PyO
Assert(info->is_rowtype == 1);
/* Build tuple */
values = palloc(sizeof(Datum) * desc->natts);
nulls = palloc(sizeof(bool) * desc->natts);
values = (Datum*)palloc(sizeof(Datum) * desc->natts);
nulls = (bool*)palloc(sizeof(bool) * desc->natts);
for (i = 0; i < desc->natts; ++i) {
char* key = NULL;
PyObject* volatile value = NULL;

17
src/common/pl/plpython/plpy_util.cpp Executable file → Normal file
View File

@ -27,7 +27,10 @@ void* PLy_malloc0(size_t bytes)
{
void* ptr = PLy_malloc(bytes);
MemSet(ptr, 0, bytes);
if (bytes > 0) {
errno_t rc = memset_s(ptr, bytes, 0, bytes);
securec_check(rc, "\0", "\0");
}
return ptr;
}
@ -38,8 +41,8 @@ char* PLy_strdup(const char* str)
errno_t rc = EOK;
len = strlen(str) + 1;
result = PLy_malloc(len);
rc = memcpy_s(result, len, str, len - 1);
result = (char*)PLy_malloc(len);
rc = memcpy_s(result, len, str, len);
securec_check(rc, "\0", "\0");
return result;
@ -58,10 +61,10 @@ void PLy_free(void* ptr)
*/
PyObject* PLyUnicode_Bytes(PyObject* unicode)
{
PyObject *bytes = NULL;
PyObject *rv = NULL;
char *utf8string = NULL;
char *encoded = NULL;
PyObject* bytes = NULL;
PyObject* rv = NULL;
char* utf8string = NULL;
char* encoded = NULL;
/* First encode the Python unicode object with UTF-8. */
bytes = PyUnicode_AsUTF8String(unicode);

View File

@ -119,6 +119,25 @@ typedef int Py_ssize_t;
#undef TEXTDOMAIN
#define TEXTDOMAIN PG_TEXTDOMAIN("plpython")
typedef struct plpy_t_context_struct {
bool inited;
MemoryContext plpython_func_cxt;
HTAB* PLy_spi_exceptions;
HTAB* PLy_procedure_cache;
/* a list of nested explicit subtransactions */
List* explicit_subtransactions;
PyObject* PLy_exc_error;
PyObject* PLy_exc_fatal;
PyObject* PLy_exc_spi_error;
PyObject* PLy_interp_globals;
int Ply_LockLevel;
} plpy_t_context_struct;
extern THR_LOCAL plpy_t_context_struct plpy_t_context;
#include <compile.h>
#include <eval.h>

View File

@ -4,4 +4,4 @@ default_version = '1.0'
module_pathname = '$libdir/plpython2'
relocatable = false
schema = pg_catalog
superuser = true
sysadmin = true

View File

@ -51,6 +51,10 @@
/* Define to build with GSSAPI support. (--with-gssapi) */
#undef ENABLE_GSS
/* Define to build with PYTHON support. (--with-python) */
#undef ENABLE_PYTHON2
#undef ENABLE_PYTHON3
/* Define to 1 if you want National Language Support. (--enable-nls) */
#undef ENABLE_NLS