"""Module implementing a Postgresql based repository for cfvers""" # Copyright 2003-2005 Iustin Pop # # This file is part of cfvers. # # cfvers 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 of the License, or # (at your option) any later version. # # cfvers 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 cfvers; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # $Id: r_postgresql.py 218 2005-10-30 09:26:23Z iusty $ import psycopg2 import cfvers.repository.sql class RPostgresql(cfvers.repository.sql.RSql): def __init__(self, create=False, cnxargs=None, createopts=None): self._quote = self._quote_format self.conn = psycopg2.connect(cnxargs) self.conn.set_isolation_level(1) self.backend = 'postgresql' self.cnxargs = cnxargs if create: self._create(createopts=createopts) self._check_schema(self.conn.cursor()) return def _init_schema(self, cursor): cursor.execute("set client_min_messages to 'warning'") super(RPostgresql, self)._init_schema(cursor) cursor.execute("ALTER TABLE config SET WITHOUT OIDS") cursor.execute("ALTER TABLE areas SET WITHOUT OIDS") cursor.execute("CREATE SEQUENCE items_id_seq") cursor.execute("ALTER TABLE items SET WITHOUT OIDS") cursor.execute("ALTER TABLE items ALTER COLUMN id SET DEFAULT nextval('items_id_seq')") cursor.execute("ALTER TABLE items ADD constraint items_area_fk FOREIGN KEY (area) REFERENCES areas(name) ON UPDATE CASCADE") cursor.execute("ALTER TABLE entries SET WITHOUT OIDS") cursor.execute("ALTER TABLE entries ADD CONSTRAINT entries_item_fk FOREIGN KEY (item) REFERENCES items(id)") cursor.execute("ALTER TABLE revisions ADD CONSTRAINT revisions_area_fk FOREIGN KEY (area) REFERENCES areas(name) ON UPDATE CASCADE") cursor.execute("ALTER TABLE revisions SET WITHOUT OIDS") return def _delete_schema(self, cursor): super(RPostgresql, self)._delete_schema(cursor) self._try_stm(cursor, "DROP SEQUENCE items_id_seq") return def TimestampFromMx(mxtimestamp): return mxtimestamp TimestampFromMx = staticmethod(TimestampFromMx) def _encode_binary(value): if not isinstance(value, str): raise ProgrammingException("Invalid type passed to _encode_binary: %s" % type(value)) return psycopg2.Binary(value) _encode_binary = staticmethod(_encode_binary) def _decode_binary(value): return str(value) _decode_binary = staticmethod(_decode_binary) def putRevision(self, r): """Adds a Revision to the database and fills its revno It is important to note that only this function can return a valid new revision number. Until stored in the repository, the new revision number cannot be safely determined. """ cursor = self.conn.cursor() # Lock the areas row in order that the next query performs correctly cursor.execute("select * from areas where name = %s for update", (r.area,)) cursor.execute("insert into revisions (area, revno, logmsg, ctime, uid, gid, uname, gname, commiter, server) values (%s, (select coalesce(max(revno), 0)+1 from revisions where area = %s), %s, %s, %s, %s, %s, %s, %s, %s)", (r.area, r.area, r.logmsg, self.TimestampFromMx(r.ctime), r.uid, r.gid, r.uname, r.gname, r.commiter, r.server)) cursor.execute("select max(revno) from revisions where area = %s", (r.area,)) row = cursor.fetchone() r.revno = row[0] return r def quotestr(value): return psycopg2.QuotedString(value) quotestr = staticmethod(quotestr)