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

Reply via email to