"""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)


syntax highlighted by Code2HTML, v. 0.9.1