psycopg2/psycopg/notify_type.c
Jason Erickson b075017ad9 Pulled down changes from dvarrazzo branch on gh
Pulled the master branch from of Daniele's psycopg branch on github and
merged the changes.
2011-02-10 15:59:31 -07:00

341 lines
9.5 KiB
C

/* notify_type.c - python interface to Notify objects
*
* Copyright (C) 2010 Daniele Varrazzo <daniele.varrazzo@gmail.com>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*/
#define PSYCOPG_MODULE
#include "psycopg/psycopg.h"
#include "psycopg/notify.h"
static const char notify_doc[] =
"A notification received from the backend.\n\n"
"`!Notify` instances are made available upon reception on the\n"
"`~connection.notifies` member of the listening connection. The object\n"
"can be also accessed as a 2 items tuple returning the members\n"
":samp:`({pid},{channel})` for backward compatibility.\n\n"
"See :ref:`async-notify` for details.";
static const char pid_doc[] =
"The ID of the backend process that sent the notification.\n\n"
"Note: if the sending session was handled by Psycopg, you can use\n"
"`~connection.get_backend_pid()` to know its PID.";
static const char channel_doc[] =
"The name of the channel to which the notification was sent.";
static const char payload_doc[] =
"The payload message of the notification.\n\n"
"Attaching a payload to a notification is only available since\n"
"PostgreSQL 9.0: for notifications received from previous versions\n"
"of the server this member is always the empty string.";
static PyMemberDef notify_members[] = {
{ "pid", T_OBJECT, offsetof(NotifyObject, pid), READONLY, (char *)pid_doc },
{ "channel", T_OBJECT, offsetof(NotifyObject, channel), READONLY, (char *)channel_doc },
{ "payload", T_OBJECT, offsetof(NotifyObject, payload), READONLY, (char *)payload_doc },
{ NULL }
};
static PyObject *
notify_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
NotifyObject *self = (NotifyObject *)type->tp_alloc(type, 0);
return (PyObject *)self;
}
static int
notify_init(NotifyObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"pid", "channel", "payload", NULL};
PyObject *pid = NULL, *channel = NULL, *payload = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|O", kwlist,
&pid, &channel, &payload)) {
return -1;
}
if (!payload) {
payload = Text_FromUTF8("");
}
Py_CLEAR(self->pid);
Py_INCREF(pid);
self->pid = pid;
Py_CLEAR(self->channel);
Py_INCREF(channel);
self->channel = channel;
Py_CLEAR(self->payload);
Py_INCREF(payload);
self->payload = payload;
return 0;
}
static int
notify_traverse(NotifyObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->pid);
Py_VISIT(self->channel);
Py_VISIT(self->payload);
return 0;
}
static void
notify_dealloc(NotifyObject *self)
{
Py_CLEAR(self->pid);
Py_CLEAR(self->channel);
Py_CLEAR(self->payload);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static void
notify_del(PyObject *self)
{
PyObject_GC_Del(self);
}
/* Convert a notify into a 2 or 3 items tuple. */
static PyObject *
notify_astuple(NotifyObject *self, int with_payload)
{
PyObject *tself;
if (!(tself = PyTuple_New(with_payload ? 3 : 2))) { return NULL; }
Py_INCREF(self->pid);
PyTuple_SET_ITEM(tself, 0, self->pid);
Py_INCREF(self->channel);
PyTuple_SET_ITEM(tself, 1, self->channel);
if (with_payload) {
Py_INCREF(self->payload);
PyTuple_SET_ITEM(tself, 2, self->payload);
}
return tself;
}
/* note on Notify-tuple comparison.
*
* Such a comparison is required otherwise a check n == (pid, channel)
* would fail. We also want to compare two notifies, and the obvious meaning is
* "check that all the attributes are equal". Unfortunately this leads to an
* inconsistent situation:
* Notify(pid, channel, payload1)
* == (pid, channel)
* == Notify(pid, channel, payload2)
* even when payload1 != payload2. We can probably live with that, but hashing
* makes things worse: hashability is a desirable property for a Notify, and
* to maintain compatibility we should put a notify object in the same bucket
* of a 2-item tuples... but we can't put all the payloads with the same
* (pid, channel) in the same bucket: it would be an extremely poor hash.
* So we maintain compatibility in the sense that notify without payload
* behave as 2-item tuples in term of hashability, but if a payload is present
* the (pid, channel) pair is no more equivalent as dict key to the Notify.
*/
static PyObject *
notify_richcompare(NotifyObject *self, PyObject *other, int op)
{
PyObject *rv = NULL;
PyObject *tself = NULL;
PyObject *tother = NULL;
if (Py_TYPE(other) == &NotifyType) {
if (!(tself = notify_astuple(self, 1))) { goto exit; }
if (!(tother = notify_astuple((NotifyObject *)other, 1))) { goto exit; }
rv = PyObject_RichCompare(tself, tother, op);
}
else if (PyTuple_Check(other)) {
if (!(tself = notify_astuple(self, 0))) { goto exit; }
rv = PyObject_RichCompare(tself, other, op);
}
else {
Py_INCREF(Py_False);
rv = Py_False;
}
exit:
Py_XDECREF(tself);
Py_XDECREF(tother);
return rv;
}
long
notify_hash(NotifyObject *self)
{
long rv = -1L;
PyObject *tself = NULL;
/* if self == a tuple, then their hashes are the same. */
int has_payload = PyObject_IsTrue(self->payload);
if (!(tself = notify_astuple(self, has_payload))) { goto exit; }
rv = PyObject_Hash(tself);
exit:
Py_XDECREF(tself);
return rv;
}
static PyObject*
notify_repr(NotifyObject *self)
{
PyObject *rv = NULL;
PyObject *format = NULL;
PyObject *args = NULL;
if (!(format = Text_FromUTF8("Notify(%r, %r, %r)"))) {
goto exit;
}
if (!(args = PyTuple_New(3))) { goto exit; }
Py_INCREF(self->pid);
PyTuple_SET_ITEM(args, 0, self->pid);
Py_INCREF(self->channel);
PyTuple_SET_ITEM(args, 1, self->channel);
Py_INCREF(self->payload);
PyTuple_SET_ITEM(args, 2, self->payload);
rv = Text_Format(format, args);
exit:
Py_XDECREF(args);
Py_XDECREF(format);
return rv;
}
/* Notify can be accessed as a 2 items tuple for backward compatibility */
static Py_ssize_t
notify_len(NotifyObject *self)
{
return 2;
}
static PyObject *
notify_getitem(NotifyObject *self, Py_ssize_t item)
{
if (item < 0)
item += 2;
switch (item) {
case 0:
Py_INCREF(self->pid);
return self->pid;
case 1:
Py_INCREF(self->channel);
return self->channel;
default:
PyErr_SetString(PyExc_IndexError, "index out of range");
return NULL;
}
}
static PySequenceMethods notify_sequence = {
(lenfunc)notify_len, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
(ssizeargfunc)notify_getitem, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
0, /* sq_contains */
0, /* sq_inplace_concat */
0, /* sq_inplace_repeat */
};
PyTypeObject NotifyType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2.extensions.Notify",
sizeof(NotifyObject),
0,
(destructor)notify_dealloc, /* tp_dealloc */
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
(reprfunc)notify_repr, /*tp_repr*/
0, /*tp_as_number*/
&notify_sequence, /*tp_as_sequence*/
0, /*tp_as_mapping*/
(hashfunc)notify_hash, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
notify_doc, /*tp_doc*/
(traverseproc)notify_traverse, /*tp_traverse*/
0, /*tp_clear*/
(richcmpfunc)notify_richcompare, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
/* Attribute descriptor and subclassing stuff */
0, /*tp_methods*/
notify_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
(initproc)notify_init, /*tp_init*/
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
notify_new, /*tp_new*/
(freefunc)notify_del, /*tp_free Low-level free-memory routine */
0, /*tp_is_gc For PyObject_IS_GC */
0, /*tp_bases*/
0, /*tp_mro method resolution order */
0, /*tp_cache*/
0, /*tp_subclasses*/
0 /*tp_weaklist*/
};