Hi.
My name is Michael. I use postgresql notice processing to inform user
about some actions. For example, I send to user something about inserted
rows from table trigger (RAISE NOTICE 'Some values in row have invalid
data'). I using it in Qt project with some libpq functions
(PQSetNoticeProcessor). Received messsages renders into QTextEditor.
In future I want use python in my work. But postgresql python drivers have
no callback notice processing. I add this future in your library.
I'm beginner in python.
For thread-safe I'm using Global Interpreter Lock: PyGILState_STATE gil =
PyGILState_Ensure(); /* call callback */ PyGILState_Release(gil);
Mechanism provided by Dmitry Dvoinikov in 2005 is not flexibly. Python
developer must call "x = conn.notices()" at all times.
I create python callback mechanizm, like it created in libpq.
Developer can register his python callback by setting 'notice_receiver'
attribute of pgconn object. There is example.
import pg
# change this on
conn = pg.connect(dbname="drivingschool", user="manager", passwd="1234")
def notice_receiver(severity, primary, detail, hint):
print severity, primary, detail, hint
pass
print conn.status
print conn.__members__
print conn.query
if conn.notice_receiver == None:
conn.notice_receiver = notice_receiver
print conn.notice_receiver
#while 1:
conn.notice_receiver('asdf', 'asdf', 'asdf', 'asdfas')
query = None
try:
query = conn.query('''-- Function: test_trigger_function()
CREATE OR REPLACE FUNCTION test_trigger_function()
RETURNS trigger AS
$BODY$
declare
begin
case TG_OP
when 'DELETE' then
raise notice 'delete trigger';
return old;
when 'UPDATE' then
raise notice 'update trigger';
return new;
when 'INSERT' then
raise notice 'insert trigger';
return new;
end case;
return null;
end
$BODY$
LANGUAGE plpgsql;
DROP TABLE test;
CREATE TABLE test
(
id serial NOT NULL,
name text,
descr text,
field1 integer NOT NULL DEFAULT 0,
test_time time without time zone,
test_timestamp timestamp without time zone,
test_date date,
test_float numeric(6,2),
bool_column boolean,
real_column real,
CONSTRAINT test_pk PRIMARY KEY (id, field1)
);
-- Trigger: test_trigger on test
DROP TRIGGER test_trigger ON test;
CREATE TRIGGER test_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON test
FOR EACH ROW
EXECUTE PROCEDURE test_trigger_function();''')
except:
print 'error'
try:
query = conn.query("insert into test(id, field1) values ('10', '10')
returning *")
print query.getresult()
print query.listfields()
except:
print 'error'
try:
query = conn.query("delete from test where id = 10 and field1 = 10")
except:
print 'error'
conn.notice_receiver(*('last', 'last', 'last', 'last'))
conn.notice_receiver = None
------------------------------------------------------------------------------
---------------
---------Patch:
---------------
Index: trunk/module/setup.py
===================================================================
--- trunk/module/setup.py (revision 425)
+++ trunk/module/setup.py (working copy)
@@ -70,8 +70,8 @@
py_modules = ['pg', 'pgdb']
libraries = ['pq']
include_dirs = [pg_config('includedir')]
-library_dirs=[pg_config('libdir')]
-define_macros = [('PYGRESQL_VERSION', '\\"%s\\"' % version)]
+library_dirs = [pg_config('libdir')]
+define_macros = [('PYGRESQL_VERSION', '\"%s\"' % version)]
undef_macros = []
extra_compile_args = ['-O2']
@@ -138,7 +138,7 @@
name="PyGreSQL",
version=version,
description="Python PostgreSQL Interfaces",
- long_description = __doc__.split('\n\n', 2)[1], # first passage
+ long_description=__doc__.split('\n\n', 2)[1], # first passage
keywords="pygresql postgresql database api dbapi",
author="D'Arcy J. M. Cain",
author_email="[email protected]",
Index: trunk/module/pgmodule.c
===================================================================
--- trunk/module/pgmodule.c (revision 425)
+++ trunk/module/pgmodule.c (working copy)
@@ -129,6 +129,7 @@
int valid; /* validity flag */
PGconn *cnx; /* PostGres connection handle */
PGresult *last_result; /* last result content */
+ PyObject *notice_receiver; /* notice processor */
} pgobject;
staticforward PyTypeObject PgType;
@@ -146,6 +147,8 @@
pgobj->valid = 1;
pgobj->last_result = NULL;
pgobj->cnx = NULL;
+ Py_INCREF(Py_None);
+ pgobj->notice_receiver = Py_None;
return (PyObject *) pgobj;
}
@@ -1623,6 +1626,24 @@
/* ---------------------------------------------------------------------
*/
/* PG QUERY OBJECT IMPLEMENTATION */
+/* call python notice receiver callback */
+void notice_receiver(void *arg ,const PGresult *res)
+{
+ PyGILState_STATE gil = PyGILState_Ensure();
+ pgobject *self = (pgobject*) arg;
+ if (PyCallable_Check(self->notice_receiver)) {
+ const char *severity = (PQresultErrorField(res, PG_DIAG_SEVERITY));
+ const char *primary = (PQresultErrorField(res,
PG_DIAG_MESSAGE_PRIMARY));
+ const char *detail = (PQresultErrorField(res,
PG_DIAG_MESSAGE_DETAIL));
+ const char *hint = (PQresultErrorField(res, PG_DIAG_MESSAGE_HINT));
+ PyObject *args = Py_BuildValue("(s,s,s,s)", severity, primary,
detail, hint);
+ PyObject *result = PyObject_CallObject(self->notice_receiver, args);
+ Py_DECREF(args);
+ Py_XDECREF(result);
+ }
+ PyGILState_Release(gil);
+}
+
/* connects to a database */
static char connect__doc__[] =
"connect(dbname, host, port, opt, tty) -- connect to a PostgreSQL
database "
@@ -1706,6 +1727,8 @@
return NULL;
}
+ PQsetNoticeReceiver(npgobj->cnx, notice_receiver, npgobj);
+
return (PyObject *) npgobj;
}
@@ -1721,6 +1744,7 @@
PQfinish(self->cnx);
Py_END_ALLOW_THREADS
}
+ Py_DECREF(self->notice_receiver);
PyObject_Del(self);
}
@@ -3040,10 +3064,16 @@
return PyInt_FromLong(PQserverVersion(self->cnx));
#endif
+ /* notice processor */
+ if (!strcmp(name, "notice_receiver")) {
+ Py_INCREF(self->notice_receiver);
+ return self->notice_receiver;
+ }
+
/* attributes list */
if (!strcmp(name, "__members__"))
{
- PyObject *list = PyList_New(10);
+ PyObject *list = PyList_New(11);
if (list)
{
@@ -3057,6 +3087,7 @@
PyList_SET_ITEM(list, 7, PyString_FromString("user"));
PyList_SET_ITEM(list, 8,
PyString_FromString("protocol_version"));
PyList_SET_ITEM(list, 9,
PyString_FromString("server_version"));
+ PyList_SET_ITEM(list, 10,
PyString_FromString("notice_receiver"));
}
return list;
@@ -3065,6 +3096,21 @@
return Py_FindMethod(pgobj_methods, (PyObject *) self, name);
}
+/* set attribute */
+static int
+pg_setattr(pgobject * self, char *name, PyObject* attr) {
+ int result = -1;
+ if (!strcmp(name, "notice_receiver")) {
+ if (PyCallable_Check(attr) || attr == Py_None) {
+ Py_XINCREF(attr); /* Add a reference to new callback */
+ Py_XDECREF(self->notice_receiver); /* Dispose of previous callback
*/
+ self->notice_receiver = attr; /* Remember new callback */
+ result = 0;
+ }
+ }
+ return result;
+}
+
/* object type definition */
staticforward PyTypeObject PgType = {
PyObject_HEAD_INIT(NULL)
@@ -3076,7 +3122,7 @@
(destructor) pg_dealloc, /* tp_dealloc */
0, /* tp_print */
(getattrfunc) pg_getattr, /* tp_getattr */
- 0, /* tp_setattr */
+ (setattrfunc) pg_setattr, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number
*/
--
With best regards, Michael Filonenko.
_______________________________________________
PyGreSQL mailing list
[email protected]
http://mailman.vex.net/mailman/listinfo/pygresql