Skip to content

Commit c8f7046

Browse files
committed
pgsqlSetNoticeCallback
Allows a callback to be triggered on every notice sent by PostgreSQL. Such notices can be sent with a RAISE NOTICE in PL/pgSQL; in a long running stored procedure, they prove useful as realtime checkpoint indicators.
1 parent 47ec320 commit c8f7046

File tree

4 files changed

+92
-3
lines changed

4 files changed

+92
-3
lines changed

ext/pdo_pgsql/pgsql_driver.c

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,23 @@ int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *
104104

105105
static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */
106106
{
107-
/* pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */
107+
int ret;
108+
zval zarg;
109+
zval retval;
110+
pdo_pgsql_fci * fc;
111+
if ((fc = ((pdo_pgsql_db_handle *)dbh->driver_data)->notice_callback)) {
112+
ZVAL_STRINGL(&zarg, (char *) message, strlen(message));
113+
fc->fci.param_count = 1;
114+
fc->fci.params = &zarg;
115+
fc->fci.retval = &retval;
116+
if ((ret = zend_call_function(&fc->fci, &fc->fcc)) != FAILURE) {
117+
zval_ptr_dtor(&retval);
118+
}
119+
zval_ptr_dtor(&zarg);
120+
if (ret == FAILURE) {
121+
pdo_raise_impl_error(dbh, NULL, "HY000", "could not call user-supplied function");
122+
}
123+
}
108124
}
109125
/* }}} */
110126

@@ -125,6 +141,16 @@ static void pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *i
125141
}
126142
/* }}} */
127143

144+
static void pdo_pgsql_cleanup_notice_callback(pdo_pgsql_db_handle *H) /* {{{ */
145+
{
146+
if (H->notice_callback) {
147+
zval_ptr_dtor(&H->notice_callback->fci.function_name);
148+
efree(H->notice_callback);
149+
H->notice_callback = NULL;
150+
}
151+
}
152+
/* }}} */
153+
128154
/* {{{ pdo_pgsql_create_lob_stream */
129155
static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count)
130156
{
@@ -229,6 +255,7 @@ static void pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */
229255
pefree(H->lob_streams, dbh->is_persistent);
230256
H->lob_streams = NULL;
231257
}
258+
pdo_pgsql_cleanup_notice_callback(H);
232259
if (H->server) {
233260
PQfinish(H->server);
234261
H->server = NULL;
@@ -1224,6 +1251,53 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid)
12241251
}
12251252
/* }}} */
12261253

1254+
/* {{{ proto bool PDO::pgsqlSetNoticeCallback(mixed callback)
1255+
Sets a callback to receive DB notices (after client_min_messages has been set) */
1256+
PHP_METHOD(PDO_PGSql_Ext, pgsqlSetNoticeCallback)
1257+
{
1258+
zval *callback;
1259+
zend_string *cbname;
1260+
pdo_dbh_t *dbh;
1261+
pdo_pgsql_db_handle *H;
1262+
pdo_pgsql_fci *fc;
1263+
1264+
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "z", &callback)) {
1265+
RETURN_FALSE;
1266+
}
1267+
1268+
dbh = Z_PDO_DBH_P(getThis());
1269+
PDO_CONSTRUCT_CHECK;
1270+
1271+
H = (pdo_pgsql_db_handle *)dbh->driver_data;
1272+
1273+
if (Z_TYPE_P(callback) == IS_NULL) {
1274+
pdo_pgsql_cleanup_notice_callback(H);
1275+
RETURN_TRUE;
1276+
} else {
1277+
if (!(fc = H->notice_callback)) {
1278+
fc = (pdo_pgsql_fci*)ecalloc(1, sizeof(pdo_pgsql_fci));
1279+
} else {
1280+
zval_ptr_dtor(&fc->fci.function_name);
1281+
memcpy(&fc->fcc, &empty_fcall_info_cache, sizeof(fc->fcc));
1282+
}
1283+
1284+
if (FAILURE == zend_fcall_info_init(callback, 0, &fc->fci, &fc->fcc, &cbname, NULL)) {
1285+
php_error_docref(NULL, E_WARNING, "function '%s' is not callable", ZSTR_VAL(cbname));
1286+
zend_string_release_ex(cbname, 0);
1287+
efree(fc);
1288+
H->notice_callback = NULL;
1289+
RETURN_FALSE;
1290+
}
1291+
Z_TRY_ADDREF_P(&fc->fci.function_name);
1292+
zend_string_release_ex(cbname, 0);
1293+
1294+
H->notice_callback = fc;
1295+
1296+
RETURN_TRUE;
1297+
}
1298+
}
1299+
/* }}} */
1300+
12271301
static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind)
12281302
{
12291303
switch (kind) {
@@ -1341,7 +1415,7 @@ static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{
13411415
goto cleanup;
13421416
}
13431417

1344-
PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh);
1418+
PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)dbh);
13451419

13461420
H->attached = 1;
13471421
H->pgoid = -1;

ext/pdo_pgsql/pgsql_driver.stub.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,7 @@ public function pgsqlGetNotify(int $fetchMode = PDO::FETCH_DEFAULT, int $timeout
3333

3434
/** @tentative-return-type */
3535
public function pgsqlGetPid(): int {}
36+
37+
/** @tentative-return-type */
38+
public function pgsqlSetNoticeCallback(?callable $callback): bool {}
3639
}

ext/pdo_pgsql/pgsql_driver_arginfo.h

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/pdo_pgsql/php_pdo_pgsql_int.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ typedef struct {
3232
char *errmsg;
3333
} pdo_pgsql_error_info;
3434

35+
typedef struct {
36+
zend_fcall_info fci;
37+
zend_fcall_info_cache fcc;
38+
} pdo_pgsql_fci;
39+
3540
/* stuff we use in a pgsql database handle */
3641
typedef struct {
3742
PGconn *server;
@@ -46,6 +51,7 @@ typedef struct {
4651
bool disable_native_prepares; /* deprecated since 5.6 */
4752
bool disable_prepares;
4853
HashTable *lob_streams;
54+
pdo_pgsql_fci * notice_callback;
4955
} pdo_pgsql_db_handle;
5056

5157
typedef struct {

0 commit comments

Comments
 (0)