diff --git a/configure b/configure index e193cabc0..cf5a70e95 100755 --- a/configure +++ b/configure @@ -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; } diff --git a/src/bin/initdb/initdb.cpp b/src/bin/initdb/initdb.cpp index 4cf439253..f7376c039 100644 --- a/src/bin/initdb/initdb.cpp +++ b/src/bin/initdb/initdb.cpp @@ -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(); } diff --git a/src/common/backend/utils/errcodes.txt b/src/common/backend/utils/errcodes.txt old mode 100755 new mode 100644 index d9e3cf12d..afe8a5826 --- a/src/common/backend/utils/errcodes.txt +++ b/src/common/backend/utils/errcodes.txt @@ -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 diff --git a/src/common/backend/utils/fmgr/dfmgr.cpp b/src/common/backend/utils/fmgr/dfmgr.cpp old mode 100755 new mode 100644 index dcb1b39cd..8c5cf7013 --- a/src/common/backend/utils/fmgr/dfmgr.cpp +++ b/src/common/backend/utils/fmgr/dfmgr.cpp @@ -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) { diff --git a/src/common/pl/plpython/Makefile b/src/common/pl/plpython/Makefile index 9fa056455..742e54bb8 100644 --- a/src/common/pl/plpython/Makefile +++ b/src/common/pl/plpython/Makefile @@ -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) diff --git a/src/common/pl/plpython/plpy_cursorobject.cpp b/src/common/pl/plpython/plpy_cursorobject.cpp index bd174ccfb..a83956136 100644 --- a/src/common/pl/plpython/plpy_cursorobject.cpp +++ b/src/common/pl/plpython/plpy_cursorobject.cpp @@ -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); diff --git a/src/common/pl/plpython/plpy_elog.cpp b/src/common/pl/plpython/plpy_elog.cpp old mode 100755 new mode 100644 index 256e1aa04..8b625e5e2 --- a/src/common/pl/plpython/plpy_elog.cpp +++ b/src/common/pl/plpython/plpy_elog.cpp @@ -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); diff --git a/src/common/pl/plpython/plpy_elog.h b/src/common/pl/plpython/plpy_elog.h index 2d2c5779e..4a7626cd5 100644 --- a/src/common/pl/plpython/plpy_elog.h +++ b/src/common/pl/plpython/plpy_elog.h @@ -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))); diff --git a/src/common/pl/plpython/plpy_exec.cpp b/src/common/pl/plpython/plpy_exec.cpp index bfb44d44b..2b35fd7c8 100644 --- a/src/common/pl/plpython/plpy_exec.cpp +++ b/src/common/pl/plpython/plpy_exec.cpp @@ -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; diff --git a/src/common/pl/plpython/plpy_main.cpp b/src/common/pl/plpython/plpy_main.cpp index 22e208312..5707c7564 100644 --- a/src/common/pl/plpython/plpy_main.cpp +++ b/src/common/pl/plpython/plpy_main.cpp @@ -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, diff --git a/src/common/pl/plpython/plpy_main.h b/src/common/pl/plpython/plpy_main.h index 66c98e895..992de1789 100644 --- a/src/common/pl/plpython/plpy_main.h +++ b/src/common/pl/plpython/plpy_main.h @@ -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 diff --git a/src/common/pl/plpython/plpy_planobject.cpp b/src/common/pl/plpython/plpy_planobject.cpp index f5bd1a7b0..9c91f4634 100644 --- a/src/common/pl/plpython/plpy_planobject.cpp +++ b/src/common/pl/plpython/plpy_planobject.cpp @@ -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; } diff --git a/src/common/pl/plpython/plpy_plpymodule.cpp b/src/common/pl/plpython/plpy_plpymodule.cpp index 2346316c1..3f74b43eb 100644 --- a/src/common/pl/plpython/plpy_plpymodule.cpp +++ b/src/common/pl/plpython/plpy_plpymodule.cpp @@ -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(); diff --git a/src/common/pl/plpython/plpy_plpymodule.h b/src/common/pl/plpython/plpy_plpymodule.h index d7a1fe493..4b42b3b5d 100644 --- a/src/common/pl/plpython/plpy_plpymodule.h +++ b/src/common/pl/plpython/plpy_plpymodule.h @@ -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 diff --git a/src/common/pl/plpython/plpy_procedure.cpp b/src/common/pl/plpython/plpy_procedure.cpp old mode 100755 new mode 100644 index f0ef4592b..d27cbd777 --- a/src/common/pl/plpython/plpy_procedure.cpp +++ b/src/common/pl/plpython/plpy_procedure.cpp @@ -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, "", 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; diff --git a/src/common/pl/plpython/plpy_resultobject.cpp b/src/common/pl/plpython/plpy_resultobject.cpp index c03ea53b4..6e05044e6 100644 --- a/src/common/pl/plpython/plpy_resultobject.cpp +++ b/src/common/pl/plpython/plpy_resultobject.cpp @@ -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) { diff --git a/src/common/pl/plpython/plpy_spi.cpp b/src/common/pl/plpython/plpy_spi.cpp index c41e62bf3..a4150ec81 100644 --- a/src/common/pl/plpython/plpy_spi.cpp +++ b/src/common/pl/plpython/plpy_spi.cpp @@ -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); diff --git a/src/common/pl/plpython/plpy_subxactobject.cpp b/src/common/pl/plpython/plpy_subxactobject.cpp index 4836c0fec..f3796e8fd 100644 --- a/src/common/pl/plpython/plpy_subxactobject.cpp +++ b/src/common/pl/plpython/plpy_subxactobject.cpp @@ -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; diff --git a/src/common/pl/plpython/plpy_subxactobject.h b/src/common/pl/plpython/plpy_subxactobject.h index 81dda1b0c..1ce89cece 100644 --- a/src/common/pl/plpython/plpy_subxactobject.h +++ b/src/common/pl/plpython/plpy_subxactobject.h @@ -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; diff --git a/src/common/pl/plpython/plpy_typeio.cpp b/src/common/pl/plpython/plpy_typeio.cpp old mode 100755 new mode 100644 index 001e7c637..5810e99ec --- a/src/common/pl/plpython/plpy_typeio.cpp +++ b/src/common/pl/plpython/plpy_typeio.cpp @@ -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; diff --git a/src/common/pl/plpython/plpy_util.cpp b/src/common/pl/plpython/plpy_util.cpp old mode 100755 new mode 100644 index b15ad4738..50ae9351a --- a/src/common/pl/plpython/plpy_util.cpp +++ b/src/common/pl/plpython/plpy_util.cpp @@ -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); diff --git a/src/common/pl/plpython/plpython.h b/src/common/pl/plpython/plpython.h index d1f4296c2..1fdf7abe6 100644 --- a/src/common/pl/plpython/plpython.h +++ b/src/common/pl/plpython/plpython.h @@ -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 #include diff --git a/src/common/pl/plpython/plpythonu.control b/src/common/pl/plpython/plpythonu.control index ae91b1c25..04861add9 100644 --- a/src/common/pl/plpython/plpythonu.control +++ b/src/common/pl/plpython/plpythonu.control @@ -4,4 +4,4 @@ default_version = '1.0' module_pathname = '$libdir/plpython2' relocatable = false schema = pg_catalog -superuser = true +sysadmin = true diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index d568c8da0..0a0807529 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -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