diff -N -r -u roundup-0.6.0b3/doc/psycopg.txt roundup-0.6.0b3-new/doc/psycopg.txt --- roundup-0.6.0b3/doc/psycopg.txt 1970-01-01 01:00:00.000000000 +0100 +++ roundup-0.6.0b3-new/doc/psycopg.txt 2003-06-27 12:41:27.000000000 +0200 @@ -0,0 +1,48 @@ +========================== +PostgreSQL/psycopg Backend +========================== + +This is notes about PostreSQL backend based on the psycopg adapter for +roundup issue tracker. + + +Prerequisites +============= + +To use PostgreSQL as backend for storing roundup data, you should +additionally install: + + 1. PostgreSQL 7.x - http://www.postgresql.org/ + + 2. The psycopg python interface to PostgreSQL - + http://initd.org/software/initd/psycopg + + +Additional configuration +======================== + +To initialise and use PostgreSQL database roundup's configuration file +(config.py in the tracker's home directory) should be appended with the +following constants (substituting real values, obviously): + + PSYCOPG_DBHOST = 'localhost' + PSYCOPG_DBUSER = 'roundup' + PSYCOPG_DBPASSWORD = 'roundup' + PSYCOPG_DBNAME = 'roundup' + PSYCOPG_PORT = 5432 + PSYCOPG_DATABASE = {'host':MYSQL_DBHOST, 'port':PSYCOPG_PORT, + 'user':MYSQL_DBUSER, 'password':MYSQL_DBPASSWORD, + 'database':MYSQL_DBNAME} + +Also note that you can leave some values out of PSYCOPG_DATABASE: 'host' and +'port' are not necessary when connecting to a local database and 'password' +is optional if postgres trusts local connections. The user specified in +PSYCOPG_DBUSER must have rights to create a new database and to connect to +the "template1" database, used while initializing roundup. + + + Have fun with psycopg, + Federico Di Gregorio + + +vim: et tw=80 Files roundup-0.6.0b3/messagesummary.pyo and roundup-0.6.0b3-new/messagesummary.pyo differ Files roundup-0.6.0b3/nosyreaction.pyo and roundup-0.6.0b3-new/nosyreaction.pyo differ diff -N -r -u roundup-0.6.0b3/roundup/backends/back_psycopg.py roundup-0.6.0b3-new/roundup/backends/back_psycopg.py --- roundup-0.6.0b3/roundup/backends/back_psycopg.py 1970-01-01 01:00:00.000000000 +0100 +++ roundup-0.6.0b3-new/roundup/backends/back_psycopg.py 2003-06-27 12:36:53.000000000 +0200 @@ -0,0 +1,220 @@ +# +# Copyright (c) 2003 Martynas Sklyzmantas, Andrey Lebedev +# +# This module is free software, and you may redistribute it and/or modify +# under the same terms as Python, so long as this copyright message and +# disclaimer are retained in their original form. +# +# psycopg backend for roundup +# + +from roundup.backends.rdbms_common import * +from roundup.backends import rdbms_common +import psycopg +import os, shutil + +class Maintenance: + """ Database maintenance functions """ + def db_nuke(self, config): + """Clear all database contents and drop database itself""" + config.PSYCOPG_DATABASE['database'] = 'template1' + db = Database(config, 'admin') + db.conn.set_isolation_level(0) + db.sql("DROP DATABASE %s" % config.PSYCOPG_DBNAME) + db.sql("CREATE DATABASE %s" % config.PSYCOPG_DBNAME) + if os.path.exists(config.DATABASE): + shutil.rmtree(config.DATABASE) + config.PSYCOPG_DATABASE['database'] = config.PSYCOPG_DBNAME + + def db_exists(self, config): + """Check if database already exists""" + try: + db = Database(config, 'admin') + return 1 + except: + return 0 + +class Database(Database): + arg = '%s' + + def open_connection(self): + db = getattr(self.config, 'PSYCOPG_DATABASE') + try: + self.conn = psycopg.connect(**db) + except psycopg.OperationalError, message: + raise DatabaseError, message + + self.cursor = self.conn.cursor() + + try: + self.database_schema = self.load_dbschema() + except: + self.rollback() + self.database_schema = {} + self.sql("CREATE TABLE schema (schema TEXT)") + self.sql("CREATE TABLE ids (name VARCHAR(255), num INT4)") + + def close(self): + self.conn.close() + + def __repr__(self): + return '' % id(self) + + def sql_fetchone(self): + return self.cursor.fetchone() + + def sql_fetchall(self): + return self.cursor.fetchall() + + def sql_stringquote(self, value): + return psycopg.QuotedString(str(value)) + + def save_dbschema(self, schema): + s = repr(self.database_schema) + self.sql('INSERT INTO schema VALUES (%s)', (s,)) + + def load_dbschema(self): + self.cursor.execute('SELECT schema FROM schema') + schema = self.cursor.fetchone() + if schema: + return eval(schema[0]) + + def save_journal(self, classname, cols, nodeid, journaldate, + journaltag, action, params): + params = repr(params) + entry = (nodeid, journaldate, journaltag, action, params) + + a = self.arg + sql = 'INSERT INTO %s__journal (%s) values (%s, %s, %s, %s, %s)' % ( + classname, cols, a, a, a, a, a) + + if __debug__: + print >>hyperdb.DEBUG, 'addjournal', (self, sql, entry) + + self.cursor.execute(sql, entry) + + def load_journal(self, classname, cols, nodeid): + sql = 'SELECT %s FROM %s__journal WHERE nodeid = %s' % ( + cols, classname, self.arg) + + if __debug__: + print >>hyperdb.DEBUG, 'getjournal', (self, sql, nodeid) + + self.cursor.execute(sql, (nodeid,)) + res = [] + for nodeid, date_stamp, user, action, params in self.cursor.fetchall(): + params = eval(params) + res.append((nodeid, date.Date(date_stamp), user, action, params)) + return res + + def create_class_table(self, spec): + cols, mls = self.determine_columns(spec.properties.items()) + cols.append('id') + cols.append('__retired__') + scols = ',' . join(['"%s" VARCHAR(255)' % x for x in cols]) + sql = 'CREATE TABLE "_%s" (%s)' % (spec.classname, scols) + + if __debug__: + print >>hyperdb.DEBUG, 'create_class', (self, sql) + + self.cursor.execute(sql) + return cols, mls + + def create_journal_table(self, spec): + cols = ',' . join(['"%s" VARCHAR(255)' % x + for x in 'nodeid date tag action params' . split()]) + sql = 'CREATE TABLE "%s__journal" (%s)' % (spec.classname, cols) + + if __debug__: + print >>hyperdb.DEBUG, 'create_class', (self, sql) + + self.cursor.execute(sql) + + def create_multilink_table(self, spec, ml): + sql = '''CREATE TABLE "%s_%s" (linkid VARCHAR(255), + nodeid VARCHAR(255))''' % (spec.classname, ml) + + if __debug__: + print >>hyperdb.DEBUG, 'create_class', (self, sql) + + self.cursor.execute(sql) + + # Static methods + nuke = Maintenance().db_nuke + exists = Maintenance().db_exists + +class PsycopgClass: + def find(self, **propspec): + """Get the ids of nodes in this class which link to the given nodes.""" + + if __debug__: + print >>hyperdb.DEBUG, 'find', (self, propspec) + + # shortcut + if not propspec: + return [] + + # validate the args + props = self.getprops() + propspec = propspec.items() + for propname, nodeids in propspec: + # check the prop is OK + prop = props[propname] + if not isinstance(prop, Link) and not isinstance(prop, Multilink): + raise TypeError, "'%s' not a Link/Multilink property"%propname + + # first, links + l = [] + where = [] + allvalues = () + a = self.db.arg + for prop, values in propspec: + if not isinstance(props[prop], hyperdb.Link): + continue + if type(values) is type(''): + allvalues += (values,) + where.append('_%s = %s' % (prop, a)) + else: + allvalues += tuple(values.keys()) + where.append('_%s in (%s)' % (prop, ','.join([a]*len(values)))) + tables = [] + if where: + self.db.sql('SELECT id AS nodeid FROM _%s WHERE %s' % ( + self.classname, ' and '.join(where)), allvalues) + l += [x[0] for x in self.db.sql_fetchall()] + + # now multilinks + for prop, values in propspec: + vals = () + if not isinstance(props[prop], hyperdb.Multilink): + continue + if type(values) is type(''): + vals = (values,) + s = a + else: + vals = tuple(values.keys()) + s = ','.join([a]*len(values)) + query = 'SELECT nodeid FROM %s_%s WHERE linkid IN (%s)'%( + self.classname, prop, s) + self.db.sql(query, vals) + l += [x[0] for x in self.db.sql_fetchall()] + + if __debug__: + print >>hyperdb.DEBUG, 'find ... ', l + + # Remove duplicated ids + d = {} + for k in l: + d[k] = 1 + return d.keys() + + return l + +class Class(PsycopgClass, rdbms_common.Class): + pass +class IssueClass(PsycopgClass, rdbms_common.IssueClass): + pass +class FileClass(PsycopgClass, rdbms_common.FileClass): + pass + +#vim: set et diff -N -r -u roundup-0.6.0b3/roundup/backends/__init__.py roundup-0.6.0b3-new/roundup/backends/__init__.py --- roundup-0.6.0b3/roundup/backends/__init__.py 2003-04-24 08:55:24.000000000 +0200 +++ roundup-0.6.0b3-new/roundup/backends/__init__.py 2003-06-27 12:36:45.000000000 +0200 @@ -87,4 +87,13 @@ metakit = back_metakit __all__.append('metakit') +try: + import psycopg +except ImportError, message: + if str(message) != 'No module named psycopg': raise +else: + import back_psycopg + psycopg = back_psycopg + __all__.append('psycopg') + # vim: set filetype=python ts=4 sw=4 et si Files roundup-0.6.0b3/statusauditor.pyo and roundup-0.6.0b3-new/statusauditor.pyo differ