mirror of
				https://github.com/psycopg/psycopg2.git
				synced 2025-11-04 09:47:30 +03:00 
			
		
		
		
	
							parent
							
								
									2ad82b973b
								
							
						
					
					
						commit
						1f330e9cac
					
				
							
								
								
									
										3
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								NEWS
									
									
									
									
									
								
							| 
						 | 
					@ -10,6 +10,9 @@ New features:
 | 
				
			||||||
  `~psycopg2.extensions.libpq_version()` to inspect the version of the
 | 
					  `~psycopg2.extensions.libpq_version()` to inspect the version of the
 | 
				
			||||||
  ``libpq`` library the module was compiled/loaded with
 | 
					  ``libpq`` library the module was compiled/loaded with
 | 
				
			||||||
  (:tickets:`#35, #323`).
 | 
					  (:tickets:`#35, #323`).
 | 
				
			||||||
 | 
					- The attributes `~connection.notices` and `~connection.notifies` can be
 | 
				
			||||||
 | 
					  customized replacing them with any object exposing an `!append()` method
 | 
				
			||||||
 | 
					  (:ticket:`#326`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
What's new in psycopg 2.6.1
 | 
					What's new in psycopg 2.6.1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -312,6 +312,10 @@ received from a previous version server will have the
 | 
				
			||||||
    Added `~psycopg2.extensions.Notify` object and handling notification
 | 
					    Added `~psycopg2.extensions.Notify` object and handling notification
 | 
				
			||||||
    payload.
 | 
					    payload.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. versionchanged:: 2.7
 | 
				
			||||||
 | 
					    The `~connection.notifies` attribute is writable: it is possible to
 | 
				
			||||||
 | 
					    replace it with any object exposing an `!append()` method. An useful
 | 
				
			||||||
 | 
					    example would be to use a `~collections.deque` object.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. index::
 | 
					.. index::
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -483,8 +483,16 @@ The ``connection`` class
 | 
				
			||||||
            ['NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"\n',
 | 
					            ['NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"\n',
 | 
				
			||||||
             'NOTICE:  CREATE TABLE will create implicit sequence "foo_id_seq" for serial column "foo.id"\n']
 | 
					             'NOTICE:  CREATE TABLE will create implicit sequence "foo_id_seq" for serial column "foo.id"\n']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionchanged:: 2.7
 | 
				
			||||||
 | 
					            The `!notices` attribute is writable: the user may replace it
 | 
				
			||||||
 | 
					            with any Python object exposing an `!append()` method. If
 | 
				
			||||||
 | 
					            appending raises an exception the notice is silently
 | 
				
			||||||
 | 
					            dropped.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        To avoid a leak in case excessive notices are generated, only the last
 | 
					        To avoid a leak in case excessive notices are generated, only the last
 | 
				
			||||||
        50 messages are kept.
 | 
					        50 messages are kept. This check is only in place if the `!notices`
 | 
				
			||||||
 | 
					        attribute is a list: if any other object is used it will be up to the
 | 
				
			||||||
 | 
					        user to guard from leakage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        You can configure what messages to receive using `PostgreSQL logging
 | 
					        You can configure what messages to receive using `PostgreSQL logging
 | 
				
			||||||
        configuration parameters`__ such as ``log_statement``,
 | 
					        configuration parameters`__ such as ``log_statement``,
 | 
				
			||||||
| 
						 | 
					@ -506,6 +514,12 @@ The ``connection`` class
 | 
				
			||||||
            the payload was not accessible. To keep backward compatibility,
 | 
					            the payload was not accessible. To keep backward compatibility,
 | 
				
			||||||
            `!Notify` objects can still be accessed as 2 items tuples.
 | 
					            `!Notify` objects can still be accessed as 2 items tuples.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionchanged:: 2.7
 | 
				
			||||||
 | 
					            The `!notifies` attribute is writable: the user may replace it
 | 
				
			||||||
 | 
					            with any Python object exposing an `!append()` method. If
 | 
				
			||||||
 | 
					            appending raises an exception the notification is silently
 | 
				
			||||||
 | 
					            dropped.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. attribute:: cursor_factory
 | 
					    .. attribute:: cursor_factory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -111,41 +111,60 @@ void
 | 
				
			||||||
conn_notice_process(connectionObject *self)
 | 
					conn_notice_process(connectionObject *self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    struct connectionObject_notice *notice;
 | 
					    struct connectionObject_notice *notice;
 | 
				
			||||||
    Py_ssize_t nnotices;
 | 
					    PyObject *msg = NULL;
 | 
				
			||||||
 | 
					    PyObject *tmp = NULL;
 | 
				
			||||||
 | 
					    static PyObject *append;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (NULL == self->notice_pending) {
 | 
					    if (NULL == self->notice_pending) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    notice =  self->notice_pending;
 | 
					    if (!append) {
 | 
				
			||||||
 | 
					        if (!(append = Text_FromUTF8("append"))) {
 | 
				
			||||||
 | 
					            goto error;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    notice = self->notice_pending;
 | 
				
			||||||
    while (notice != NULL) {
 | 
					    while (notice != NULL) {
 | 
				
			||||||
        PyObject *msg;
 | 
					 | 
				
			||||||
        msg = conn_text_from_chars(self, notice->message);
 | 
					 | 
				
			||||||
        Dprintf("conn_notice_process: %s", notice->message);
 | 
					        Dprintf("conn_notice_process: %s", notice->message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (msg) {
 | 
					        if (!(msg = conn_text_from_chars(self, notice->message))) { goto error; }
 | 
				
			||||||
            PyList_Append(self->notice_list, msg);
 | 
					
 | 
				
			||||||
            Py_DECREF(msg);
 | 
					        if (!(tmp = PyObject_CallMethodObjArgs(
 | 
				
			||||||
        }
 | 
					                self->notice_list, append, msg, NULL))) {
 | 
				
			||||||
        else {
 | 
					
 | 
				
			||||||
            /* We don't really have a way to report errors, so gulp it.
 | 
					            goto error;
 | 
				
			||||||
             * The function should only fail for out of memory, so we are
 | 
					 | 
				
			||||||
             * likely going to die anyway. */
 | 
					 | 
				
			||||||
            PyErr_Clear();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Py_DECREF(tmp); tmp = NULL;
 | 
				
			||||||
 | 
					        Py_DECREF(msg); msg = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        notice = notice->next;
 | 
					        notice = notice->next;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Remove the oldest item if the queue is getting too long. */
 | 
					    /* Remove the oldest item if the queue is getting too long. */
 | 
				
			||||||
 | 
					    if (PyList_Check(self->notice_list)) {
 | 
				
			||||||
 | 
					        Py_ssize_t nnotices;
 | 
				
			||||||
        nnotices = PyList_GET_SIZE(self->notice_list);
 | 
					        nnotices = PyList_GET_SIZE(self->notice_list);
 | 
				
			||||||
        if (nnotices > CONN_NOTICES_LIMIT) {
 | 
					        if (nnotices > CONN_NOTICES_LIMIT) {
 | 
				
			||||||
        PySequence_DelSlice(self->notice_list,
 | 
					            if (-1 == PySequence_DelSlice(self->notice_list,
 | 
				
			||||||
            0, nnotices - CONN_NOTICES_LIMIT);
 | 
					                    0, nnotices - CONN_NOTICES_LIMIT)) {
 | 
				
			||||||
 | 
					                PyErr_Clear();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    conn_notice_clean(self);
 | 
					    conn_notice_clean(self);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					error:
 | 
				
			||||||
 | 
					    Py_XDECREF(tmp);
 | 
				
			||||||
 | 
					    Py_XDECREF(msg);
 | 
				
			||||||
 | 
					    conn_notice_clean(self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* TODO: the caller doesn't expects errors from us */
 | 
				
			||||||
 | 
					    PyErr_Clear();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
| 
						 | 
					@ -177,6 +196,15 @@ conn_notifies_process(connectionObject *self)
 | 
				
			||||||
    PGnotify *pgn = NULL;
 | 
					    PGnotify *pgn = NULL;
 | 
				
			||||||
    PyObject *notify = NULL;
 | 
					    PyObject *notify = NULL;
 | 
				
			||||||
    PyObject *pid = NULL, *channel = NULL, *payload = NULL;
 | 
					    PyObject *pid = NULL, *channel = NULL, *payload = NULL;
 | 
				
			||||||
 | 
					    PyObject *tmp = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static PyObject *append;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!append) {
 | 
				
			||||||
 | 
					        if (!(append = Text_FromUTF8("append"))) {
 | 
				
			||||||
 | 
					            goto error;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while ((pgn = PQnotifies(self->pgconn)) != NULL) {
 | 
					    while ((pgn = PQnotifies(self->pgconn)) != NULL) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -196,7 +224,11 @@ conn_notifies_process(connectionObject *self)
 | 
				
			||||||
        Py_DECREF(channel); channel = NULL;
 | 
					        Py_DECREF(channel); channel = NULL;
 | 
				
			||||||
        Py_DECREF(payload); payload = NULL;
 | 
					        Py_DECREF(payload); payload = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        PyList_Append(self->notifies, (PyObject *)notify);
 | 
					        if (!(tmp = PyObject_CallMethodObjArgs(
 | 
				
			||||||
 | 
					                self->notifies, append, notify, NULL))) {
 | 
				
			||||||
 | 
					            goto error;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Py_DECREF(tmp); tmp = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Py_DECREF(notify); notify = NULL;
 | 
					        Py_DECREF(notify); notify = NULL;
 | 
				
			||||||
        PQfreemem(pgn); pgn = NULL;
 | 
					        PQfreemem(pgn); pgn = NULL;
 | 
				
			||||||
| 
						 | 
					@ -205,6 +237,7 @@ conn_notifies_process(connectionObject *self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
    if (pgn) { PQfreemem(pgn); }
 | 
					    if (pgn) { PQfreemem(pgn); }
 | 
				
			||||||
 | 
					    Py_XDECREF(tmp);
 | 
				
			||||||
    Py_XDECREF(notify);
 | 
					    Py_XDECREF(notify);
 | 
				
			||||||
    Py_XDECREF(pid);
 | 
					    Py_XDECREF(pid);
 | 
				
			||||||
    Py_XDECREF(channel);
 | 
					    Py_XDECREF(channel);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1001,8 +1001,8 @@ static struct PyMemberDef connectionObject_members[] = {
 | 
				
			||||||
        "True if the connection is closed."},
 | 
					        "True if the connection is closed."},
 | 
				
			||||||
    {"encoding", T_STRING, offsetof(connectionObject, encoding), READONLY,
 | 
					    {"encoding", T_STRING, offsetof(connectionObject, encoding), READONLY,
 | 
				
			||||||
        "The current client encoding."},
 | 
					        "The current client encoding."},
 | 
				
			||||||
    {"notices", T_OBJECT, offsetof(connectionObject, notice_list), READONLY},
 | 
					    {"notices", T_OBJECT, offsetof(connectionObject, notice_list), 0},
 | 
				
			||||||
    {"notifies", T_OBJECT, offsetof(connectionObject, notifies), READONLY},
 | 
					    {"notifies", T_OBJECT, offsetof(connectionObject, notifies), 0},
 | 
				
			||||||
    {"dsn", T_STRING, offsetof(connectionObject, dsn), READONLY,
 | 
					    {"dsn", T_STRING, offsetof(connectionObject, dsn), READONLY,
 | 
				
			||||||
        "The current connection string."},
 | 
					        "The current connection string."},
 | 
				
			||||||
    {"async", T_LONG, offsetof(connectionObject, async), READONLY,
 | 
					    {"async", T_LONG, offsetof(connectionObject, async), READONLY,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -129,6 +129,42 @@ class ConnectionTests(ConnectingTestCase):
 | 
				
			||||||
        self.assertEqual(50, len(conn.notices))
 | 
					        self.assertEqual(50, len(conn.notices))
 | 
				
			||||||
        self.assert_('table99' in conn.notices[-1], conn.notices[-1])
 | 
					        self.assert_('table99' in conn.notices[-1], conn.notices[-1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_notices_deque(self):
 | 
				
			||||||
 | 
					        from collections import deque
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        conn = self.conn
 | 
				
			||||||
 | 
					        self.conn.notices = deque()
 | 
				
			||||||
 | 
					        cur = conn.cursor()
 | 
				
			||||||
 | 
					        if self.conn.server_version >= 90300:
 | 
				
			||||||
 | 
					            cur.execute("set client_min_messages=debug1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur.execute("create temp table table1 (id serial); create temp table table2 (id serial);")
 | 
				
			||||||
 | 
					        cur.execute("create temp table table3 (id serial); create temp table table4 (id serial);")
 | 
				
			||||||
 | 
					        self.assertEqual(len(conn.notices), 4)
 | 
				
			||||||
 | 
					        self.assert_('table1' in conn.notices.popleft())
 | 
				
			||||||
 | 
					        self.assert_('table2' in conn.notices.popleft())
 | 
				
			||||||
 | 
					        self.assert_('table3' in conn.notices.popleft())
 | 
				
			||||||
 | 
					        self.assert_('table4' in conn.notices.popleft())
 | 
				
			||||||
 | 
					        self.assertEqual(len(conn.notices), 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # not limited, but no error
 | 
				
			||||||
 | 
					        for i in range(0, 100, 10):
 | 
				
			||||||
 | 
					            sql = " ".join(["create temp table table2_%d (id serial);" % j for j in range(i, i+10)])
 | 
				
			||||||
 | 
					            cur.execute(sql)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(100, len(conn.notices))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_notices_noappend(self):
 | 
				
			||||||
 | 
					        conn = self.conn
 | 
				
			||||||
 | 
					        self.conn.notices = None    # will make an error swallowes ok
 | 
				
			||||||
 | 
					        cur = conn.cursor()
 | 
				
			||||||
 | 
					        if self.conn.server_version >= 90300:
 | 
				
			||||||
 | 
					            cur.execute("set client_min_messages=debug1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur.execute("create temp table table1 (id serial);")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(self.conn.notices, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_server_version(self):
 | 
					    def test_server_version(self):
 | 
				
			||||||
        self.assert_(self.conn.server_version)
 | 
					        self.assert_(self.conn.server_version)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -155,6 +155,27 @@ conn.close()
 | 
				
			||||||
        self.assertEqual('foo', notify.channel)
 | 
					        self.assertEqual('foo', notify.channel)
 | 
				
			||||||
        self.assertEqual('Hello, world!', notify.payload)
 | 
					        self.assertEqual('Hello, world!', notify.payload)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_notify_deque(self):
 | 
				
			||||||
 | 
					        from collections import deque
 | 
				
			||||||
 | 
					        self.autocommit(self.conn)
 | 
				
			||||||
 | 
					        self.conn.notifies = deque()
 | 
				
			||||||
 | 
					        self.listen('foo')
 | 
				
			||||||
 | 
					        self.notify('foo').communicate()
 | 
				
			||||||
 | 
					        time.sleep(0.5)
 | 
				
			||||||
 | 
					        self.conn.poll()
 | 
				
			||||||
 | 
					        notify = self.conn.notifies.popleft()
 | 
				
			||||||
 | 
					        self.assert_(isinstance(notify, psycopg2.extensions.Notify))
 | 
				
			||||||
 | 
					        self.assertEqual(len(self.conn.notifies), 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_notify_noappend(self):
 | 
				
			||||||
 | 
					        self.autocommit(self.conn)
 | 
				
			||||||
 | 
					        self.conn.notifies = None
 | 
				
			||||||
 | 
					        self.listen('foo')
 | 
				
			||||||
 | 
					        self.notify('foo').communicate()
 | 
				
			||||||
 | 
					        time.sleep(0.5)
 | 
				
			||||||
 | 
					        self.conn.poll()
 | 
				
			||||||
 | 
					        self.assertEqual(self.conn.notifies, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_notify_init(self):
 | 
					    def test_notify_init(self):
 | 
				
			||||||
        n = psycopg2.extensions.Notify(10, 'foo')
 | 
					        n = psycopg2.extensions.Notify(10, 'foo')
 | 
				
			||||||
        self.assertEqual(10, n.pid)
 | 
					        self.assertEqual(10, n.pid)
 | 
				
			||||||
| 
						 | 
					@ -192,6 +213,7 @@ conn.close()
 | 
				
			||||||
        self.assertNotEqual(hash(Notify(10, 'foo', 'bar')),
 | 
					        self.assertNotEqual(hash(Notify(10, 'foo', 'bar')),
 | 
				
			||||||
            hash(Notify(10, 'foo')))
 | 
					            hash(Notify(10, 'foo')))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_suite():
 | 
					def test_suite():
 | 
				
			||||||
    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
					    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user