Skip to content

Commit 1fa5f87

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 8db5e70 commit 1fa5f87

File tree

4 files changed

+91
-2
lines changed

4 files changed

+91
-2
lines changed

ext/pdo_pgsql/pgsql_driver.c

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

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

@@ -124,6 +140,16 @@ static void pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *i
124140
}
125141
/* }}} */
126142

143+
static void pdo_pgsql_cleanup_notice_callback(pdo_pgsql_db_handle *H) /* {{{ */
144+
{
145+
if (H->notice_callback) {
146+
zval_ptr_dtor(&H->notice_callback->fci.function_name);
147+
efree(H->notice_callback);
148+
H->notice_callback = NULL;
149+
}
150+
}
151+
/* }}} */
152+
127153
/* {{{ pdo_pgsql_create_lob_stream */
128154
static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count)
129155
{
@@ -206,6 +232,7 @@ static void pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */
206232
{
207233
pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
208234
if (H) {
235+
pdo_pgsql_cleanup_notice_callback(H);
209236
if (H->server) {
210237
PQfinish(H->server);
211238
H->server = NULL;
@@ -1142,6 +1169,53 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid)
11421169
}
11431170
/* }}} */
11441171

1172+
/* {{{ proto bool PDO::pgsqlSetNoticeCallback(mixed callback)
1173+
Sets a callback to receive DB notices (after client_min_messages has been set) */
1174+
PHP_METHOD(PDO_PGSql_Ext, pgsqlSetNoticeCallback)
1175+
{
1176+
zval *callback;
1177+
zend_string *cbname;
1178+
pdo_dbh_t *dbh;
1179+
pdo_pgsql_db_handle *H;
1180+
pdo_pgsql_fci *fc;
1181+
1182+
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "z", &callback)) {
1183+
RETURN_FALSE;
1184+
}
1185+
1186+
dbh = Z_PDO_DBH_P(getThis());
1187+
PDO_CONSTRUCT_CHECK;
1188+
1189+
H = (pdo_pgsql_db_handle *)dbh->driver_data;
1190+
1191+
if (Z_TYPE_P(callback) == IS_NULL) {
1192+
pdo_pgsql_cleanup_notice_callback(H);
1193+
RETURN_TRUE;
1194+
} else {
1195+
if (!(fc = H->notice_callback)) {
1196+
fc = (pdo_pgsql_fci*)ecalloc(1, sizeof(pdo_pgsql_fci));
1197+
} else {
1198+
zval_ptr_dtor(&fc->fci.function_name);
1199+
memcpy(&fc->fcc, &empty_fcall_info_cache, sizeof(fc->fcc));
1200+
}
1201+
1202+
if (FAILURE == zend_fcall_info_init(callback, 0, &fc->fci, &fc->fcc, &cbname, NULL)) {
1203+
php_error_docref(NULL, E_WARNING, "function '%s' is not callable", ZSTR_VAL(cbname));
1204+
zend_string_release_ex(cbname, 0);
1205+
efree(fc);
1206+
H->notice_callback = NULL;
1207+
RETURN_FALSE;
1208+
}
1209+
Z_TRY_ADDREF_P(&fc->fci.function_name);
1210+
zend_string_release_ex(cbname, 0);
1211+
1212+
H->notice_callback = fc;
1213+
1214+
RETURN_TRUE;
1215+
}
1216+
}
1217+
/* }}} */
1218+
11451219
static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind)
11461220
{
11471221
switch (kind) {
@@ -1257,7 +1331,7 @@ static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{
12571331
goto cleanup;
12581332
}
12591333

1260-
PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh);
1334+
PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)dbh);
12611335

12621336
H->attached = 1;
12631337
H->pgoid = -1;

ext/pdo_pgsql/pgsql_driver.stub.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,7 @@ public function pgsqlGetNotify(int $fetchMode = PDO::FETCH_USE_DEFAULT, int $tim
3030

3131
/** @tentative-return-type */
3232
public function pgsqlGetPid(): int {}
33+
34+
/** @tentative-return-type */
35+
public function pgsqlSetNoticeCallback(?callable $callback): bool {}
3336
}

ext/pdo_pgsql/pgsql_driver_arginfo.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ ZEND_END_ARG_INFO()
4646
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, 0, 0, IS_LONG, 0)
4747
ZEND_END_ARG_INFO()
4848

49+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_PDO_PGSql_Ext_pgsqlSetNoticeCallback, 0, 1, _IS_BOOL, 0)
50+
ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 1)
51+
ZEND_END_ARG_INFO()
52+
4953

5054
ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray);
5155
ZEND_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile);
@@ -56,6 +60,7 @@ ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen);
5660
ZEND_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink);
5761
ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetNotify);
5862
ZEND_METHOD(PDO_PGSql_Ext, pgsqlGetPid);
63+
ZEND_METHOD(PDO_PGSql_Ext, pgsqlSetNoticeCallback);
5964

6065

6166
static const zend_function_entry class_PDO_PGSql_Ext_methods[] = {
@@ -68,5 +73,6 @@ static const zend_function_entry class_PDO_PGSql_Ext_methods[] = {
6873
ZEND_ME(PDO_PGSql_Ext, pgsqlLOBUnlink, arginfo_class_PDO_PGSql_Ext_pgsqlLOBUnlink, ZEND_ACC_PUBLIC)
6974
ZEND_ME(PDO_PGSql_Ext, pgsqlGetNotify, arginfo_class_PDO_PGSql_Ext_pgsqlGetNotify, ZEND_ACC_PUBLIC)
7075
ZEND_ME(PDO_PGSql_Ext, pgsqlGetPid, arginfo_class_PDO_PGSql_Ext_pgsqlGetPid, ZEND_ACC_PUBLIC)
76+
ZEND_ME(PDO_PGSql_Ext, pgsqlSetNoticeCallback, arginfo_class_PDO_PGSql_Ext_pgsqlSetNoticeCallback, ZEND_ACC_PUBLIC)
7177
ZEND_FE_END
7278
};

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;
@@ -45,6 +50,7 @@ typedef struct {
4550
bool emulate_prepares;
4651
bool disable_native_prepares; /* deprecated since 5.6 */
4752
bool disable_prepares;
53+
pdo_pgsql_fci * notice_callback;
4854
} pdo_pgsql_db_handle;
4955

5056
typedef struct {

0 commit comments

Comments
 (0)