8000 bpo-42064: Pass module state to trace, progress, and authorizer callb… · python/cpython@979336d · GitHub
[go: up one dir, main page]

Skip to content

Commit 979336d

Browse files
author
Erlend Egeberg Aasland
authored
bpo-42064: Pass module state to trace, progress, and authorizer callbacks (GH-27940)
- add print-or-clear traceback helper - add helpers to clear and visit saved contexts - modify callbacks to use the new callback_context struct
1 parent 8ca6b61 commit 979336d

File tree

2 files changed

+111
-73
lines changed

2 files changed

+111
-73
lines changed

Modules/_sqlite/connection.c

Lines changed: 107 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ static const char * const begin_statements[] = {
5555

5656
static int pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level, void *Py_UNUSED(ignored));
5757
static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self);
58+
static void free_callback_context(callback_context *ctx);
59+
static void set_callback_context(callback_context **ctx_pp,
60+
callback_context *ctx);
5861

5962
static PyObject *
6063
new_statement_cache(pysqlite_Connection *self, int maxsize)
@@ -170,9 +173,9 @@ pysqlite_connection_init_impl(pysqlite_Connection *self,
170173
self->thread_ident = PyThread_get_thread_ident();
171174
self->check_same_thread = check_same_thread;
172175

173-
self->function_pinboard_trace_callback = NULL;
174-
self->function_pinboard_progress_handler = NULL;
175-
self->function_pinboard_authorizer_cb = NULL;
176+
set_callback_context(&self->trace_ctx, NULL);
177+
set_callback_context(&self->progress_ctx, NULL);
178+
set_callback_context(&self->authorizer_ctx, NULL);
176179

177180
self->Warning = state->Warning;
178181
self->Error = state->Error;
@@ -216,6 +219,13 @@ pysqlite_do_all_statements(pysqlite_Connection *self)
216219
}
217220
}
218221

222+
#define VISIT_CALLBACK_CONTEXT(ctx) \
223+
do { \
224+
if (ctx) { \
225+
Py_VISIT(ctx->callable); \
226+
} \
227+
} while (0)
228+
219229
static int
220230
connection_traverse(pysqlite_Connection *self, visitproc visit, void *arg)
221231
{
@@ -225,12 +235,21 @@ connection_traverse(pysqlite_Connection *self, visitproc visit, void *arg)
225235
Py_VISIT(self->cursors);
226236
Py_VISIT(self->row_factory);
227237
Py_VISIT(self->text_factory);
228-
Py_VISIT(self->function_pinboard_trace_callback);
229-
Py_VISIT(self->function_pinboard_progress_handler);
230-
Py_VISIT(self->function_pinboard_authorizer_cb);
238+
VISIT_CALLBACK_CONTEXT(self->trace_ctx);
239+
VISIT_CALLBACK_CONTEXT(self->progress_ctx);
240+
VISIT_CALLBACK_CONTEXT(self->authorizer_ctx);
241+
#undef VISIT_CALLBACK_CONTEXT
231242
return 0;
232243
}
233244

245+
static inline void
246+
clear_callback_context(callback_context *ctx)
247+
{
248+
if (ctx != NULL) {
249+
Py_CLEAR(ctx->callable);
250+
}
251+
}
252+
234253
static int
235254
connection_clear(pysqlite_Connection *self)
236255
{
@@ -239,9 +258,9 @@ connection_clear(pysqlite_Connection *self)
239258
Py_CLEAR(self->cursors);
240259
Py_CLEAR(self->row_factory);
241260
Py_CLEAR(self->text_factory);
242-
Py_CLEAR(self->function_pinboard_trace_callback);
243-
Py_CLEAR(self->function_pinboard_progress_handler);
244-
Py_CLEAR(self->function_pinboard_authorizer_cb);
261+
clear_callback_context(self->trace_ctx);
262+
clear_callback_context(self->progress_ctx);
263+
clear_callback_context(self->authorizer_ctx);
245264
return 0;
246265
}
247266

@@ -255,6 +274,14 @@ connection_close(pysqlite_Connection *self)
255274
}
256275
}
257276

277+
static void
278+
free_callback_contexts(pysqlite_Connection *self)
279+
{
280+
set_callback_context(&self->trace_ctx, NULL);
281+
set_callback_context(&self->progress_ctx, NULL);
282+
set_callback_context(&self->authorizer_ctx, NULL);
283+
}
284+
258285
static void
259286
connection_dealloc(pysqlite_Connection *self)
260287
{
@@ -264,6 +291,7 @@ connection_dealloc(pysqlite_Connection *self)
264291

265292
/* Clean up if user has not called .close() explicitly. */
266293
connection_close(self);
294+
free_callback_contexts(self);
267295

268296
tp->tp_free(self);
269297
Py_DECREF(tp);
@@ -600,6 +628,19 @@ _pysqlite_build_py_params(sqlite3_context *context, int argc,
600628
return NULL;
601629
}
602630

631+
static void
632+
print_or_clear_traceback(callback_context *ctx)
633+
{
634+
assert(ctx != NULL);
635+
assert(ctx->state != NULL);
636+
if (ctx->state->enable_callback_tracebacks) {
637+
PyErr_Print();
638+
}
639+
else {
640+
PyErr_Clear();
641+
}
642+
}
643+
603644
// Checks the Python exception and sets the appropriate SQLite error code.
604645
static void
605646
set_sqlite_error(sqlite3_context *context, const char *msg)
@@ -615,14 +656,7 @@ set_sqlite_error(sqlite3_context *context, const char *msg)
615656
sqlite3_result_error(context, msg, -1);
616657
}
617658
callback_context *ctx = (callback_context *)sqlite3_user_data(context);
618-
assert(ctx != NULL);
619-
assert(ctx->state != NULL);
620-
if (ctx->state->enable_callback_tracebacks) {
621-
PyErr_Print();
622-
}
623-
else {
624-
PyErr_Clear();
625-
}
659+
print_or_clear_traceback(ctx);
626660
}
627661

628662
static void
@@ -796,10 +830,21 @@ static void
796830
free_callback_context(callback_context *ctx)
797831
{
798832
assert(ctx != NULL);
799-
Py_DECREF(ctx->callable);
833+
Py_XDECREF(ctx->callable);
800834
PyMem_Free(ctx);
801835
}
802836

837+
static void
838+
set_callback_context(callback_context **ctx_pp, callback_context *ctx)
839+
{
840+
assert(ctx_pp != NULL);
841+
callback_context *tmp = *ctx_pp;
842+
*ctx_pp = ctx;
843+
if (tmp != NULL) {
844+
free_callback_context(tmp);
845+
}
846+
}
847+
803848
static void
804849
destructor_callback(void *ctx)
805850
{
@@ -917,33 +962,22 @@ authorizer_callback(void *ctx, int action, const char *arg1,
917962
PyGILState_STATE gilstate = PyGILState_Ensure();
918963

919964
PyObject *ret;
920-
int rc;
965+
int rc = SQLITE_DENY;
921966

922-
ret = PyObject_CallFunction((PyObject*)ctx, "issss", action, arg1, arg2,
923-
dbname, access_attempt_source);
967+
assert(ctx != NULL);
968+
PyObject *callable = ((callback_context *)ctx)->callable;
969+
ret = PyObject_CallFunction(callable, "issss", action, arg1, arg2, dbname,
970+
access_attempt_source);
924971

925972
if (ret == NULL) {
926-
pysqlite_state *state = pysqlite_get_state(NULL);
927-
if (state->enable_callback_tracebacks) {
928-
PyErr_Print();
929-
}
930-
else {
931-
PyErr_Clear();
932-
}
933-
973+
print_or_clear_traceback(ctx);
934974
rc = SQLITE_DENY;
935975
}
936976
else {
937977
if (PyLong_Check(ret)) {
938978
rc = _PyLong_AsInt(ret);
939979
if (rc == -1 && PyErr_Occurred()) {
940-
pysqlite_state *state = pysqlite_get_state(NULL);
941-
if (state->enable_callback_tracebacks) {
942-
PyErr_Print();
943-
}
944-
else {
945-
PyErr_Clear();
946-
}
980+
print_or_clear_traceback(ctx);
947981
rc = SQLITE_DENY;
948982
}
949983
}
@@ -964,8 +998,10 @@ progress_callback(void *ctx)
964998

965999
int rc;
9661000
PyObject *ret;
967-
ret = _PyObject_CallNoArg((PyObject*)ctx);
9681001

1002+
assert(ctx != NULL);
1003+
PyObject *callable = ((callback_context *)ctx)->callable;
1004+
ret = _PyObject_CallNoArg(callable);
9691005
if (!ret) {
9701006
/* abort query if error occurred */
9711007
rc = -1;
@@ -975,13 +1011,7 @@ progress_callback(void *ctx)
9751011
Py_DECREF(ret);
9761012
}
9771013
if (rc < 0) {
978-
pysqlite_state *state = pysqlite_get_state(NULL);
979-
if (state->enable_callback_tracebacks) {
980-
PyErr_Print();
981-
}
982-
else {
983-
PyErr_Clear();
984-
}
1014+
print_or_clear_traceback(ctx);
9851015
}
9861016

9871017
PyGILState_Release(gilstate);
@@ -1015,21 +1045,18 @@ trace_callback(void *ctx, const char *statement_string)
10151045
PyObject *ret = NULL;
10161046
py_statement = PyUnicode_DecodeUTF8(statement_string,
10171047
strlen(statement_string), "replace");
1048+
assert(ctx != NULL);
10181049
if (py_statement) {
1019-
ret = PyObject_CallOneArg((PyObject*)ctx, py_statement);
1050+
PyObject *callable = ((callback_context *)ctx)->callable;
1051+
ret = PyObject_CallOneArg(callable, py_statement);
10201052
Py_DECREF(py_statement);
10211053
}
10221054

10231055
if (ret) {
10241056
Py_DECREF(ret);
1025-
} else {
1026-
pysqlite_state *state = pysqlite_get_state(NULL);
1027-
if (state->enable_callback_tracebacks) {
1028-
PyErr_Print();
1029-
}
1030-
else {
1031-
PyErr_Clear();
1032-
}
1057+
}
1058+
else {
1059+
print_or_clear_traceback(ctx);
10331060
}
10341061

10351062
PyGILState_Release(gilstate);
@@ -1058,17 +1085,20 @@ pysqlite_connection_set_authorizer_impl(pysqlite_Connection *self,
10581085
int rc;
10591086
if (callable == Py_None) {
10601087
rc = sqlite3_set_authorizer(self->db, NULL, NULL);
1061-
Py_XSETREF(self->function_pinboard_authorizer_cb, NULL);
1088+
set_callback_context(&self->authorizer_ctx, NULL);
10621089
}
10631090
else {
1064-
Py_INCREF(callable);
1065-
Py_XSETREF(self->function_pinboard_authorizer_cb, callable);
1066-
rc = sqlite3_set_authorizer(self->db, authorizer_callback, callable);
1091+
callback_context *ctx = create_callback_context(self->state, callable);
1092+
if (ctx == NULL) {
1093+
return NULL;
1094+
}
1095+
rc = sqlite3_set_authorizer(self->db, authorizer_callback, ctx);
1096+
set_callback_context(&self->authorizer_ctx, ctx);
10671097
}
10681098
if (rc != SQLITE_OK) {
10691099
PyErr_SetString(self->OperationalError,
10701100
"Error setting authorizer callback");
1071-
Py_XSETREF(self->function_pinboard_authorizer_cb, NULL);
1101+
set_callback_context(&self->authorizer_ctx, NULL);
10721102
return NULL;
10731103
}
10741104
Py_RETURN_NONE;
@@ -1095,11 +1125,15 @@ pysqlite_connection_set_progress_handler_impl(pysqlite_Connection *self,
10951125
if (callable == Py_None) {
10961126
/* None clears the progress handler previously set */
10971127
sqlite3_progress_handler(self->db, 0, 0, (void*)0);
1098-
Py_XSETREF(self->function_pinboard_progress_handler, NULL);
1099-
} else {
1100-
sqlite3_progress_handler(self->db, n, progress_callback, callable);
1101-
Py_INCREF(callable);
1102-
Py_XSETREF(self->function_pinboard_progress_handler, callable);
1128+
set_callback_context(&self->progress_ctx, NULL);
1129+
}
1130+
else {
1131+
callback_context *ctx = create_callback_context(self->state, callable);
1132+
if (ctx == NULL) {
1133+
return NULL;
1134+
}
1135+
sqlite3_progress_handler(self->db, n, progress_callback, ctx);
1136+
set_callback_context(&self->progress_ctx, ctx);
11031137
}
11041138
Py_RETURN_NONE;
11051139
}
@@ -1136,15 +1170,19 @@ pysqlite_connection_set_trace_callback_impl(pysqlite_Connection *self,
11361170
#else
11371171
sqlite3_trace(self->db, 0, (void*)0);
11381172
#endif
1139-
Py_XSETREF(self->function_pinboard_trace_callback, NULL);
1140-
} else {
1173+
set_callback_context(&self->trace_ctx, NULL);
1174+
}
1175+
else {
1176+
callback_context *ctx = create_callback_context(self->state, callable);
1177+
if (ctx == NULL) {
1178+
return NULL;
1179+
}
11411180
#ifdef HAVE_TRACE_V2
1142-
sqlite3_trace_v2(self->db, SQLITE_TRACE_STMT, trace_callback, callable);
1181+
sqlite3_trace_v2(self->db, SQLITE_TRACE_STMT, trace_callback, ctx);
11431182
#else
1144-
sqlite3_trace(self->db, trace_callback, callable);
1183+
sqlite3_trace(self->db, trace_callback, ctx);
11451184
#endif
1146-
Py_INCREF(callable);
1147-
Py_XSETREF(self->function_pinboard_trace_callback, callable);
1185+
set_callback_context(&self->trace_ctx, ctx);
11481186
}
11491187

11501188
Py_RETURN_NONE;

Modules/_sqlite/connection.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,10 @@ typedef struct
8282
*/
8383
PyObject* text_factory;
8484

85-
/* remember references to object used in trace_callback/progress_handler/authorizer_cb */
86-
PyObject* function_pinboard_trace_callback;
87-
PyObject* function_pinboard_progress_handler;
88-
PyObject* function_pinboard_authorizer_cb;
85+
// Remember contexts used by the trace, progress, and authoriser callbacks
86+
callback_context *trace_ctx;
87+
callback_context *progress_ctx;
88+
callback_context *authorizer_ctx;
8989

9090
/* Exception objects: borrowed refs. */
9191
PyObject* Warning;

0 commit comments

Comments
 (0)
0