openGauss supports plpython
This commit is contained in:
13
configure
vendored
13
configure
vendored
@ -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; }
|
||||
|
||||
@ -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
16
src/common/backend/utils/errcodes.txt
Executable file → Normal 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
26
src/common/backend/utils/fmgr/dfmgr.cpp
Executable file → Normal 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) {
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
75
src/common/pl/plpython/plpy_elog.cpp
Executable file → Normal 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);
|
||||
|
||||
@ -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)));
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
48
src/common/pl/plpython/plpy_procedure.cpp
Executable file → Normal 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;
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
158
src/common/pl/plpython/plpy_typeio.cpp
Executable file → Normal 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
17
src/common/pl/plpython/plpy_util.cpp
Executable file → Normal 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);
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -4,4 +4,4 @@ default_version = '1.0'
|
||||
module_pathname = '$libdir/plpython2'
|
||||
relocatable = false
|
||||
schema = pg_catalog
|
||||
superuser = true
|
||||
sysadmin = true
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user