From 0021e56d800dffbb7fa30fbb3349a1016456343e Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Wed, 6 Oct 2010 01:09:17 +0100 Subject: [PATCH] Added connection.xid() and related objects. By James Henstridge on 2008-07-23. Merged from lp:~jamesh/psycopg/two-phase-commit/revision/356 * psycopg/connection_type.c (psyco_conn_xid): add a Connection.xid() method that instantiates Xid objects. * psycopg/psycopgmodule.c (init_psycopg): initialise the Xid object type. * psycopg/xid.h: * psycopg/xid_type.c: Implement a basic transaction ID object for use in two phase commit. --- ChangeLog | 12 ++ psycopg/connection_type.c | 15 +++ psycopg/psycopgmodule.c | 4 + psycopg/xid.h | 48 ++++++++ psycopg/xid_type.c | 251 ++++++++++++++++++++++++++++++++++++++ setup.py | 2 +- 6 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 psycopg/xid.h create mode 100644 psycopg/xid_type.c diff --git a/ChangeLog b/ChangeLog index bba34a88..1b9d0d95 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,18 @@ * Merged James Henstridge work on two-phase commit support. + 2008-07-23 James Henstridge + + * psycopg/connection_type.c (psyco_conn_xid): add a + Connection.xid() method that instantiates Xid objects. + + * psycopg/psycopgmodule.c (init_psycopg): initialise the Xid + object type. + + * psycopg/xid.h: + * psycopg/xid_type.c: Implement a basic transaction ID object for + use in two phase commit. + 2008-05-12 James Henstridge * beginnings of a TPC test harness diff --git a/psycopg/connection_type.c b/psycopg/connection_type.c index 3e4bbddb..f4455cfe 100644 --- a/psycopg/connection_type.c +++ b/psycopg/connection_type.c @@ -40,6 +40,7 @@ #include "psycopg/pqpath.h" #include "psycopg/lobject.h" #include "psycopg/green.h" +#include "psycopg/xid.h" /** DBAPI methods **/ @@ -168,6 +169,18 @@ psyco_conn_rollback(connectionObject *self, PyObject *args) } +#define psyco_conn_xid_doc \ +"xid(format_id, gtrid, bqual) -- create a transaction identifier." + +static PyObject * +psyco_conn_xid(connectionObject *self, PyObject *args, PyObject *kwargs) +{ + EXC_IF_CONN_CLOSED(self); + + return PyObject_Call((PyObject *)&XidType, args, kwargs); +} + + #ifdef PSYCOPG_EXTENSIONS /* set_isolation_level method - switch connection isolation level */ @@ -494,6 +507,8 @@ static struct PyMethodDef connectionObject_methods[] = { METH_VARARGS, psyco_conn_commit_doc}, {"rollback", (PyCFunction)psyco_conn_rollback, METH_VARARGS, psyco_conn_rollback_doc}, + {"xid", (PyCFunction)psyco_conn_xid, + METH_VARARGS|METH_KEYWORDS, psyco_conn_xid_doc}, #ifdef PSYCOPG_EXTENSIONS {"set_isolation_level", (PyCFunction)psyco_conn_set_isolation_level, METH_VARARGS, psyco_conn_set_isolation_level_doc}, diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c index 1e5f8403..2fff7242 100644 --- a/psycopg/psycopgmodule.c +++ b/psycopg/psycopgmodule.c @@ -35,6 +35,7 @@ #include "psycopg/green.h" #include "psycopg/lobject.h" #include "psycopg/notify.h" +#include "psycopg/xid.h" #include "psycopg/typecast.h" #include "psycopg/microprotocols.h" #include "psycopg/microprotocols_proto.h" @@ -734,6 +735,7 @@ init_psycopg(void) listType.ob_type = &PyType_Type; chunkType.ob_type = &PyType_Type; NotifyType.ob_type = &PyType_Type; + XidType.ob_type = &PyType_Type; if (PyType_Ready(&connectionType) == -1) return; if (PyType_Ready(&cursorType) == -1) return; @@ -748,6 +750,7 @@ init_psycopg(void) if (PyType_Ready(&listType) == -1) return; if (PyType_Ready(&chunkType) == -1) return; if (PyType_Ready(&NotifyType) == -1) return; + if (PyType_Ready(&XidType) == -1) return; #ifdef PSYCOPG_EXTENSIONS lobjectType.ob_type = &PyType_Type; @@ -855,6 +858,7 @@ init_psycopg(void) chunkType.tp_alloc = PyType_GenericAlloc; pydatetimeType.tp_alloc = PyType_GenericAlloc; NotifyType.tp_alloc = PyType_GenericAlloc; + XidType.tp_alloc = PyType_GenericAlloc; #ifdef PSYCOPG_EXTENSIONS lobjectType.tp_alloc = PyType_GenericAlloc; diff --git a/psycopg/xid.h b/psycopg/xid.h new file mode 100644 index 00000000..ca94efde --- /dev/null +++ b/psycopg/xid.h @@ -0,0 +1,48 @@ +/* xid.h - definition for the psycopg cursor type + * + * Copyright (C) 2008 James Henstridge + * + * This file is part of psycopg. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef PSYCOPG_XID_H +#define PSYCOPG_XID_H 1 + +#include + +#include "psycopg/config.h" + +extern HIDDEN PyTypeObject XidType; + +typedef struct { + PyObject_HEAD + + /* the PosgreSQL string transaction ID */ + char *pg_xact_id; + + /* the Python-style three-part transaction ID */ + PyObject *format_id; + PyObject *gtrid; + PyObject *bqual; + + /* Additional information PostgreSQL exposes about prepared transactions */ + PyObject *prepared; + PyObject *owner; + PyObject *database; +} XidObject; + +#endif /* PSYCOPG_XID_H */ diff --git a/psycopg/xid_type.c b/psycopg/xid_type.c new file mode 100644 index 00000000..963ce972 --- /dev/null +++ b/psycopg/xid_type.c @@ -0,0 +1,251 @@ +/* cursor_type.c - python interface to cursor objects + * + * Copyright (C) 2008 Canonical Ltd. + * + * This file is part of psycopg. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public Likcense + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define PY_SSIZE_T_CLEAN +#include +#include + +#define PSYCOPG_MODULE +#include "psycopg/config.h" +#include "psycopg/python.h" +#include "psycopg/psycopg.h" +#include "psycopg/xid.h" + +static const char xid_prefix[] = "psycopg-v1"; + +static PyMemberDef xid_members[] = { + { "format_id", T_OBJECT, offsetof(XidObject, format_id), RO }, + { "gtrid", T_OBJECT, offsetof(XidObject, gtrid), RO }, + { "bqual", T_OBJECT, offsetof(XidObject, bqual), RO }, + { "prepared", T_OBJECT, offsetof(XidObject, prepared), RO }, + { "owner", T_OBJECT, offsetof(XidObject, owner), RO }, + { "database", T_OBJECT, offsetof(XidObject, database), RO }, + { NULL } +}; + +static PyObject * +xid_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + XidObject *self = (XidObject *)type->tp_alloc(type, 0); + + self->pg_xact_id = NULL; + Py_INCREF(Py_None); + self->format_id = Py_None; + Py_INCREF(Py_None); + self->gtrid = Py_None; + Py_INCREF(Py_None); + self->bqual = Py_None; + Py_INCREF(Py_None); + self->prepared = Py_None; + Py_INCREF(Py_None); + self->owner = Py_None; + Py_INCREF(Py_None); + self->database = Py_None; + + return (PyObject *)self; +} + +static int +xid_init(XidObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"format_id", "gtrid", "bqual", NULL}; + int format_id, i, gtrid_len, bqual_len, xid_len; + const char *gtrid, *bqual; + PyObject *tmp; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iss", kwlist, + &format_id, >rid, &bqual)) + return -1; + + if (format_id < 0 || format_id >= 0xffffffff) { + PyErr_SetString(PyExc_ValueError, + "format_id must be a non-negative 32-bit integer"); + return -1; + } + + /* make sure that gtrid is no more than 64 characters long and + made of printable characters (which we're defining as those + between 0x20 and 0x7f). */ + gtrid_len = strlen(gtrid); + if (gtrid_len > 64) { + PyErr_SetString(PyExc_ValueError, + "gtrid must be a string no longer than 64 characters"); + return -1; + } + for (i = 0; i < gtrid_len; i++) { + if (gtrid[i] < 0x20 || gtrid[i] >= 0x7f) { + PyErr_SetString(PyExc_ValueError, + "gtrid must contain only printable characters."); + return -1; + } + } + /* Same for bqual */ + bqual_len = strlen(bqual); + if (bqual_len > 64) { + PyErr_SetString(PyExc_ValueError, + "bqual must be a string no longer than 64 characters"); + return -1; + } + for (i = 0; i < bqual_len; i++) { + if (bqual[i] < 0x20 || bqual[i] >= 0x7f) { + PyErr_SetString(PyExc_ValueError, + "bqual must contain only printable characters."); + return -1; + } + } + + /* Now we can construct the PostgreSQL transaction ID, which is of + the following form: + + psycopg-v1:$FORMAT_ID:$GTRID_LEN:$GTRID$BQUAL + + Where $FORMAT_ID is eight hex digits and $GTRID_LEN is 2 hex digits. + */ + xid_len = strlen(xid_prefix) + 1 + 8 + 1 + 2 + 1 + gtrid_len + bqual_len + 1; + if (self->pg_xact_id) + free(self->pg_xact_id); + self->pg_xact_id = malloc(xid_len); + memset(self->pg_xact_id, '\0', xid_len); + snprintf(self->pg_xact_id, xid_len, "%s:%08X:%02X:%s%s", + xid_prefix, format_id, gtrid_len, gtrid, bqual); + + tmp = self->format_id; + self->format_id = PyInt_FromLong(format_id); + Py_XDECREF(tmp); + + tmp = self->gtrid; + self->gtrid = PyString_FromString(gtrid); + Py_XDECREF(tmp); + + tmp = self->bqual; + self->bqual = PyString_FromString(bqual); + Py_XDECREF(tmp); + + return 0; +} + +static int +xid_traverse(XidObject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->format_id); + Py_VISIT(self->gtrid); + Py_VISIT(self->bqual); + Py_VISIT(self->prepared); + Py_VISIT(self->owner); + Py_VISIT(self->database); + return 0; +} + +static void +xid_dealloc(XidObject *self) +{ + if (self->pg_xact_id) { + free(self->pg_xact_id); + self->pg_xact_id = NULL; + } + Py_CLEAR(self->format_id); + Py_CLEAR(self->gtrid); + Py_CLEAR(self->bqual); + Py_CLEAR(self->prepared); + Py_CLEAR(self->owner); + Py_CLEAR(self->database); + + self->ob_type->tp_free((PyObject *)self); +} + +static void +xid_del(PyObject *self) +{ + PyObject_GC_Del(self); +} + +static PyObject * +xid_repr(XidObject *self) +{ + return PyString_FromFormat("", + self->pg_xact_id ? self->pg_xact_id : "(null)"); +} + +static const char xid_doc[] = + "A transaction identifier used for two phase commit."; + +PyTypeObject XidType = { + PyObject_HEAD_INIT(NULL) + 0, + "psycopg2.extensions.Xid", + sizeof(XidObject), + 0, + (destructor)xid_dealloc, /* tp_dealloc */ + 0, /*tp_print*/ + + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + + 0, /*tp_compare*/ + + (reprfunc)xid_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + + 0, /*tp_call*/ + 0, /*tp_str*/ + + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + xid_doc, /*tp_doc*/ + + (traverseproc)xid_traverse, /*tp_traverse*/ + 0, /*tp_clear*/ + + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + + /* Attribute descriptor and subclassing stuff */ + + 0, /*tp_methods*/ + xid_members, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + + (initproc)xid_init, /*tp_init*/ + 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/ + xid_new, /*tp_new*/ + (freefunc)xid_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*/ +}; diff --git a/setup.py b/setup.py index 3119f1a0..acc0cbdc 100644 --- a/setup.py +++ b/setup.py @@ -346,7 +346,7 @@ sources = [ 'psycopgmodule.c', 'pqpath.c', 'typecast.c', 'microprotocols.c', 'microprotocols_proto.c', 'connection_type.c', 'connection_int.c', 'cursor_type.c', 'cursor_int.c', - 'lobject_type.c', 'lobject_int.c', 'notify_type.c', + 'lobject_type.c', 'lobject_int.c', 'notify_type.c', 'xid_type.c', 'adapter_qstring.c', 'adapter_pboolean.c', 'adapter_binary.c', 'adapter_asis.c', 'adapter_list.c', 'adapter_datetime.c', 'adapter_pfloat.c', 'adapter_pdecimal.c',