From 17524d814bc790e2425c810e92aaae01140e366e Mon Sep 17 00:00:00 2001 From: richard Date: Fri, 5 Mar 2004 00:08:09 +0000 Subject: [PATCH] *** empty log message *** git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@2143 57a73879-2fb5-44c3-a270-3262357dd7e2 --- CHANGES.txt | 13 ++++++--- roundup/backends/back_mysql.py | 20 +++++++------- roundup/backends/back_postgresql.py | 12 ++++++++- roundup/backends/back_sqlite.py | 13 +++++++-- roundup/backends/rdbms_common.py | 41 +++++++++++++++++++++++------ roundup/rcsv.py | 8 +++--- roundup/roundupdb.py | 6 ++--- 7 files changed, 81 insertions(+), 32 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4348098..4152d2f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -13,9 +13,6 @@ Feature: - all RDBMS backends now have indexes on several columns - change nosymessage and send_message to accept msgid=None (RFE #707235). - handle Resent-From: headers (sf bug 841151) -- existing trackers (ie. live ones) may be used as templates for new - trackers - the TEMPLATE-INFO.txt name entry has the tracker's dir name - appended (so the demo tracker's template name is "classic-demo") - always sort MultilinkHTMLProperty in the correct order, usually alphabetically (sf feature 790512). - added script for copying user(s) from tracker to tracker (sf patch @@ -74,8 +71,16 @@ Cleanup: * form_parser.py - parsePropsFromForm & extractFormList in a FormParser class -2004-??-?? 0.6.7 +2004-??-?? 0.6.8 +Fixed: +- existing trackers (ie. live ones) may be used as templates for new + trackers - the TEMPLATE-INFO.txt name entry has the tracker's dir name + appended (so the demo tracker's template name is "classic-demo") + + +2004-03-01 0.6.7 Fixed: +- be more backward-compatible when asking for EMAIL_CHARSET - made error on create consistent with edit when user enters invalid data for Multilink and Link form fields (sf bug 904072) - made errors from bad input in the quick "Show issue:" form more diff --git a/roundup/backends/back_mysql.py b/roundup/backends/back_mysql.py index 8d8ea34..c9ebfd6 100644 --- a/roundup/backends/back_mysql.py +++ b/roundup/backends/back_mysql.py @@ -68,7 +68,7 @@ class Database(Database): self.sql("SET AUTOCOMMIT=0") self.sql("BEGIN") try: - self.database_schema = self.load_dbschema() + self.load_dbschema() except MySQLdb.OperationalError, message: if message[0] != ER.NO_DB_ERROR: raise @@ -82,7 +82,16 @@ class Database(Database): # http://www.mysql.com/doc/en/CREATE_TABLE.html self.sql("CREATE TABLE ids (name varchar(255), num INT) TYPE=%s"% self.mysql_backend) - self.sql("CREATE INDEX ids_name_idx on ids(name)") + self.sql("CREATE INDEX ids_name_idx ON ids(name)") + self.create_version_2_tables() + + def create_version_2_tables(self): + self.cursor.execute('CREATE TABLE otks (key VARCHAR(255), ' + 'value VARCHAR(255), __time FLOAT(20))') + self.cursor.execute('CREATE INDEX otks_key_idx ON otks(key)') + self.cursor.execute('CREATE TABLE sessions (key VARCHAR(255), ' + 'last_use FLOAT(20), user VARCHAR(255))') + self.cursor.execute('CREATE INDEX sessions_key_idx ON sessions(key)') def __repr__(self): return ''%id(self) @@ -104,13 +113,6 @@ class Database(Database): 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]) - return None - def save_journal(self, classname, cols, nodeid, journaldate, journaltag, action, params): params = repr(params) diff --git a/roundup/backends/back_postgresql.py b/roundup/backends/back_postgresql.py index 13f2e2f..147cb83 100644 --- a/roundup/backends/back_postgresql.py +++ b/roundup/backends/back_postgresql.py @@ -27,12 +27,22 @@ class Database(rdbms_common.Database): self.cursor = self.conn.cursor() try: - self.database_schema = self.load_dbschema() + 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)") + self.sql("CREATE INDEX ids_name_idx ON ids(name)") + self.create_version_2_tables() + + def create_version_2_tables(self): + self.cursor.execute('CREATE TABLE otks (key VARCHAR(255), ' + 'value VARCHAR(255), __time NUMERIC)') + self.cursor.execute('CREATE INDEX otks_key_idx ON otks(key)') + self.cursor.execute('CREATE TABLE sessions (key VARCHAR(255), ' + 'last_use NUMERIC, user VARCHAR(255))') + self.cursor.execute('CREATE INDEX sessions_key_idx ON sessions(key)') def __repr__(self): return '' % id(self) diff --git a/roundup/backends/back_sqlite.py b/roundup/backends/back_sqlite.py index c17235d..6d28cea 100644 --- a/roundup/backends/back_sqlite.py +++ b/roundup/backends/back_sqlite.py @@ -1,4 +1,4 @@ -# $Id: back_sqlite.py,v 1.13 2004-02-11 23:55:09 richard Exp $ +# $Id: back_sqlite.py,v 1.14 2004-03-05 00:08:09 richard Exp $ '''Implements a backend for SQLite. See https://pysqlite.sourceforge.net/ for pysqlite info @@ -30,7 +30,7 @@ class Database(rdbms_common.Database): self.conn = sqlite.connect(db=db) self.cursor = self.conn.cursor() try: - self.database_schema = self.load_dbschema() + self.load_dbschema() except sqlite.DatabaseError, error: if str(error) != 'no such table: schema': raise @@ -38,6 +38,15 @@ class Database(rdbms_common.Database): self.cursor.execute('create table schema (schema varchar)') self.cursor.execute('create table ids (name varchar, num integer)') self.cursor.execute('create index ids_name_idx on ids(name)') + self.create_version_2_tables() + + def create_version_2_tables(self): + self.cursor.execute('create table otks (key varchar, ' + 'value varchar, __time varchar)') + self.cursor.execute('create index otks_key_idx on otks(key)') + self.cursor.execute('create table sessions (key varchar, ' + 'last_use varchar, user varchar)') + self.cursor.execute('create index sessions_key_idx on sessions(key)') def sql_close(self): ''' Squash any error caused by us already having closed the diff --git a/roundup/backends/rdbms_common.py b/roundup/backends/rdbms_common.py index e7d141c..16392c6 100644 --- a/roundup/backends/rdbms_common.py +++ b/roundup/backends/rdbms_common.py @@ -1,4 +1,4 @@ -# $Id: rdbms_common.py,v 1.75 2004-02-11 23:55:09 richard Exp $ +# $Id: rdbms_common.py,v 1.76 2004-03-05 00:08:09 richard Exp $ ''' Relational database (SQL) backend common code. Basics: @@ -19,6 +19,12 @@ sql_* methods, since everything else should be fairly generic. There's probably a bit of work to be done if a database is used that actually honors column typing, since the initial databases don't (sqlite stores everything as a string.) + +The schema of the hyperdb being mapped to the database is stored in the +database itself as a repr()'ed dictionary of information about each Class +that maps to a table. If that information differs from the hyperdb schema, +then we update it. We also store in the schema dict a __version__ which +allows us to upgrade the database schema when necessary. See upgrade_db(). ''' __docformat__ = 'restructuredtext' @@ -77,7 +83,9 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database): self.cache_lru = [] def sql_open_connection(self): - ''' Open a connection to the database, creating it if necessary + ''' Open a connection to the database, creating it if necessary. + + Must call self.load_dbschema() ''' raise NotImplemented @@ -106,24 +114,26 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database): ''' return re.sub("'", "''", str(value)) + def load_dbschema(self): + ''' Load the schema definition that the database currently implements + ''' + self.cursor.execute('select schema from schema') + self.database_schema = eval(self.cursor.fetchone()[0]) + def save_dbschema(self, schema): ''' Save the schema definition that the database currently implements ''' s = repr(self.database_schema) self.sql('insert into schema values (%s)', (s,)) - def load_dbschema(self): - ''' Load the schema definition that the database currently implements - ''' - self.cursor.execute('select schema from schema') - return eval(self.cursor.fetchone()[0]) - def post_init(self): ''' Called once the schema initialisation has finished. We should now confirm that the schema defined by our "classes" attribute actually matches the schema in the database. ''' + self.upgrade_db() + # now detect changes in the schema save = 0 for classname, spec in self.classes.items(): @@ -155,6 +165,21 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database): # commit self.conn.commit() + # update this number when we need to make changes to the SQL structure + # of the backen database + current_db_version = 2 + def upgrade_db(self): + ''' Update the SQL database to reflect changes in the backend code. + ''' + version = self.database_schema.get('__version', 1) + if version == 1: + # version 1 doesn't have the OTK, session and indexing in the + # database + self.create_version_2_tables() + + self.database_schema['__version'] = self.current_db_version + + def refresh_database(self): self.post_init() diff --git a/roundup/rcsv.py b/roundup/rcsv.py index 91a8125..08798d0 100644 --- a/roundup/rcsv.py +++ b/roundup/rcsv.py @@ -5,12 +5,10 @@ __docformat__ = 'restructuredtext' from roundup.i18n import _ from cStringIO import StringIO -error = """Sorry, you need a module compatible with the csv module. -Either upgrade your Python to 2.3 or later, or get and install -the csv module from: +error = """ +Sorry, you need a csv module. Either upgrade your Python to 2.3 or later, +or get and install the csv module from: http://www.object-craft.com.au/projects/csv/ - -These two csv modules are different but Roundup can use either. """ try: import csv diff --git a/roundup/roundupdb.py b/roundup/roundupdb.py index ddaf4dd..0a39ee3 100644 --- a/roundup/roundupdb.py +++ b/roundup/roundupdb.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: roundupdb.py,v 1.99 2004-02-29 00:35:55 richard Exp $ +# $Id: roundupdb.py,v 1.100 2004-03-05 00:08:09 richard Exp $ """Extending hyperdb with types specific to issue-tracking. """ @@ -149,14 +149,14 @@ class IssueClass: if address: sendto.append(address) recipients.append(userid) - + def good_recipient(userid): # Make sure we don't send mail to either the anonymous # user or a user who has already seen the message. return (userid and (self.db.user.get(userid, 'username') != 'anonymous') and not seen_message.has_key(userid)) - + # possibly send the message to the author, as long as they aren't # anonymous if (good_recipient(authid) and -- 2.30.2