Mercurial > p > roundup > code
changeset 2244:ac4f295499a4
fixed journal marshalling in RDBMS backends [SF#943627]
fixed handling of key values starting with numbers [SF#941363]
fixed journal "param" column size in RDBMS backends
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Sun, 02 May 2004 23:16:05 +0000 |
| parents | 20507f6759fc |
| children | c5fb778ec7e4 |
| files | CHANGES.txt doc/index.txt roundup/backends/back_mysql.py roundup/backends/back_sqlite.py roundup/backends/rdbms_common.py roundup/backends/sessions_rdbms.py roundup/cgi/actions.py roundup/cgi/client.py roundup/cgi/templating.py roundup/hyperdb.py templates/classic/dbinit.py |
| diffstat | 11 files changed, 87 insertions(+), 41 deletions(-) [+] |
line wrap: on
line diff
--- a/CHANGES.txt Sat May 01 08:19:00 2004 +0000 +++ b/CHANGES.txt Sun May 02 23:16:05 2004 +0000 @@ -10,6 +10,9 @@ - fix nested scope bug in rdbms multilink sorting - re-seed the random number generator for each request - postgresql backend altered to not use popen (thanks Georges Martin) +- fixed journal marshalling in RDBMS backends (sf bug 943627) +- fixed handling of key values starting with numbers (sf bug 941363) +- fixed journal "param" column size in RDBMS backends 2004-04-18 0.7.0b3
--- a/doc/index.txt Sat May 01 08:19:00 2004 +0000 +++ b/doc/index.txt Sun May 02 23:16:05 2004 +0000 @@ -102,6 +102,7 @@ Georges Martin, Gordon McMillan, John F Meinel Jr, +Truls E. Nęss, Patrick Ohly, Luke Opperman, Will Partain,
--- a/roundup/backends/back_mysql.py Sat May 01 08:19:00 2004 +0000 +++ b/roundup/backends/back_mysql.py Sun May 02 23:16:05 2004 +0000 @@ -130,6 +130,7 @@ hyperdb.Password : str, hyperdb.Boolean : int, hyperdb.Number : lambda x: x, + hyperdb.Multilink : lambda x: x, # used in journal marshalling } def sql_open_connection(self): @@ -302,13 +303,14 @@ print >>hyperdb.DEBUG, '... data', entry execute(sql, tuple(entry)) - # now load up the old journal data + # now load up the old journal data to migrate it cols = ','.join('nodeid date tag action params'.split()) sql = 'select %s from %s__journal'%(cols, cn) if __debug__: print >>hyperdb.DEBUG, 'migration', (self, sql) execute(sql) + # data conversions olddata = [] for nodeid, journaldate, journaltag, action, params in \ self.cursor.fetchall(): @@ -394,7 +396,7 @@ for x in 'nodeid date tag action params'.split()]) sql = '''create table %s__journal ( nodeid integer, date timestamp, tag varchar(255), - action varchar(255), params varchar(255)) type=%s'''%( + action varchar(255), params text) type=%s'''%( spec.classname, self.mysql_backend) if __debug__: print >>hyperdb.DEBUG, 'create_journal_table', (self, sql)
--- a/roundup/backends/back_sqlite.py Sat May 01 08:19:00 2004 +0000 +++ b/roundup/backends/back_sqlite.py Sun May 02 23:16:05 2004 +0000 @@ -1,4 +1,4 @@ -# $Id: back_sqlite.py,v 1.26 2004-04-18 23:05:18 richard Exp $ +# $Id: back_sqlite.py,v 1.27 2004-05-02 23:16:05 richard Exp $ '''Implements a backend for SQLite. See https://pysqlite.sourceforge.net/ for pysqlite info @@ -36,6 +36,7 @@ hyperdb.Password : str, hyperdb.Boolean : int, hyperdb.Number : lambda x: x, + hyperdb.Multilink : lambda x: x, # used in journal marshalling } sql_to_hyperdb_value = { hyperdb.String : str, @@ -45,6 +46,7 @@ hyperdb.Password : lambda x: password.Password(encrypted=x), hyperdb.Boolean : int, hyperdb.Number : rdbms_common._num_cvt, + hyperdb.Multilink : lambda x: x, # used in journal marshalling } def sql_open_connection(self):
--- a/roundup/backends/rdbms_common.py Sat May 01 08:19:00 2004 +0000 +++ b/roundup/backends/rdbms_common.py Sun May 02 23:16:05 2004 +0000 @@ -1,4 +1,4 @@ -# $Id: rdbms_common.py,v 1.95 2004-04-26 20:59:25 richard Exp $ +# $Id: rdbms_common.py,v 1.96 2004-05-02 23:16:05 richard Exp $ ''' Relational database (SQL) backend common code. Basics: @@ -438,7 +438,7 @@ for x in 'nodeid date tag action params'.split()]) sql = '''create table %s__journal ( nodeid integer, date timestamp, tag varchar(255), - action varchar(255), params varchar(25))'''%spec.classname + action varchar(255), params text)'''%spec.classname if __debug__: print >>hyperdb.DEBUG, 'create_journal_table', (self, sql) self.cursor.execute(sql) @@ -619,12 +619,14 @@ hyperdb_to_sql_value = { hyperdb.String : str, + # fractional seconds by default hyperdb.Date : lambda x: x.formal(sep=' ', sec='%.3f'), hyperdb.Link : int, hyperdb.Interval : str, hyperdb.Password : str, hyperdb.Boolean : lambda x: x and 'TRUE' or 'FALSE', hyperdb.Number : lambda x: x, + hyperdb.Multilink : lambda x: x, # used in journal marshalling } def addnode(self, classname, nodeid, node): ''' Add the specified node to its class's db. @@ -820,6 +822,7 @@ hyperdb.Password : lambda x: password.Password(encrypted=x), hyperdb.Boolean : int, hyperdb.Number : _num_cvt, + hyperdb.Multilink : lambda x: x, # used in journal marshalling } def getnode(self, classname, nodeid): ''' Get a node from the database. @@ -973,6 +976,26 @@ if __debug__: print >>hyperdb.DEBUG, 'addjournal', (nodeid, journaldate, journaltag, action, params) + + # make the journalled data marshallable + if isinstance(params, type({})): + properties = self.getclass(classname).getprops() + for param, value in params.items(): + property = properties[param] + cvt = self.hyperdb_to_sql_value[property.__class__] + if isinstance(property, Password): + params[param] = cvt(value) + elif isinstance(property, Date): + params[param] = cvt(value) + elif isinstance(property, Interval): + params[param] = cvt(value) + elif isinstance(property, Boolean): + params[param] = cvt(value) + + params = repr(params) + + dc = self.hyperdb_to_sql_value[hyperdb.Date] + journaldate = dc(journaldate) self.save_journal(classname, cols, nodeid, journaldate, journaltag, action, params) @@ -1001,16 +1024,34 @@ raise IndexError, '%s has no node %s'%(classname, nodeid) cols = ','.join('nodeid date tag action params'.split()) - return self.load_journal(classname, cols, nodeid) + journal = self.load_journal(classname, cols, nodeid) + + # now unmarshal the data + dc = self.sql_to_hyperdb_value[hyperdb.Date] + res = [] + properties = self.getclass(classname).getprops() + for nodeid, date_stamp, user, action, params in journal: + params = eval(params) + for param, value in params.items(): + property = properties[param] + cvt = self.sql_to_hyperdb_value[property.__class__] + if isinstance(property, Password): + params[param] = cvt(value) + elif isinstance(property, Date): + params[param] = cvt(value) + elif isinstance(property, Interval): + params[param] = cvt(value) + elif isinstance(property, Boolean): + params[param] = cvt(value) + # XXX numeric ids + res.append((str(nodeid), dc(date_stamp), user, action, params)) + return res def save_journal(self, classname, cols, nodeid, journaldate, journaltag, action, params): ''' Save the journal entry to the database ''' - # make the params db-friendly - params = repr(params) - dc = self.hyperdb_to_sql_value[hyperdb.Date] - entry = (nodeid, dc(journaldate), journaltag, action, params) + entry = (nodeid, journaldate, journaltag, action, params) # do the insert a = self.arg @@ -1029,13 +1070,7 @@ if __debug__: print >>hyperdb.DEBUG, 'load_journal', (self, sql, nodeid) self.cursor.execute(sql, (nodeid,)) - res = [] - dc = self.sql_to_hyperdb_value[hyperdb.Date] - for nodeid, date_stamp, user, action, params in self.cursor.fetchall(): - params = eval(params) - # XXX numeric ids - res.append((str(nodeid), dc(date_stamp), user, action, params)) - return res + return self.cursor.fetchall() def pack(self, pack_before): ''' Delete all journal entries except "create" before 'pack_before'.
--- a/roundup/backends/sessions_rdbms.py Sat May 01 08:19:00 2004 +0000 +++ b/roundup/backends/sessions_rdbms.py Sun May 02 23:16:05 2004 +0000 @@ -1,4 +1,4 @@ -#$Id: sessions_rdbms.py,v 1.2 2004-03-31 23:08:39 richard Exp $ +#$Id: sessions_rdbms.py,v 1.3 2004-05-02 23:16:05 richard Exp $ """This module defines a very basic store that's used by the CGI interface to store session and one-time-key information. @@ -25,7 +25,7 @@ n = self.name self.cursor.execute('select count(*) from %ss where %s_key=%s'%(n, n, self.db.arg), (infoid,)) - return self.cursor.fetchone()[0] + return int(self.cursor.fetchone()[0]) _marker = [] def get(self, infoid, value, default=_marker):
--- a/roundup/cgi/actions.py Sat May 01 08:19:00 2004 +0000 +++ b/roundup/cgi/actions.py Sun May 02 23:16:05 2004 +0000 @@ -1,4 +1,4 @@ -#$Id: actions.py,v 1.23 2004-04-05 06:13:42 richard Exp $ +#$Id: actions.py,v 1.24 2004-05-02 23:16:05 richard Exp $ import re, cgi, StringIO, urllib, Cookie, time, random @@ -494,7 +494,8 @@ try: message = self._editnodes(props, links) except (ValueError, KeyError, IndexError, exceptions.Reject), message: - self.client.error_message.append(_('Apply Error: ') + str(message)) + import traceback;traceback.print_exc() + self.client.error_message.append(_('Edit Error: ') + str(message)) return # commit now that all the tricky stuff is done @@ -532,7 +533,6 @@ try: # when it hits the None element, it'll set self.nodeid messages = self._editnodes(props, links) - except (ValueError, KeyError, IndexError, exceptions.Reject), message: # these errors might just be indicative of user dumbness self.client.error_message.append(_('Error: ') + str(message))
--- a/roundup/cgi/client.py Sat May 01 08:19:00 2004 +0000 +++ b/roundup/cgi/client.py Sun May 02 23:16:05 2004 +0000 @@ -1,4 +1,4 @@ -# $Id: client.py,v 1.173 2004-04-25 22:19:15 richard Exp $ +# $Id: client.py,v 1.174 2004-05-02 23:16:05 richard Exp $ """WWW request handler (also used in the stand-alone server). """ @@ -624,13 +624,13 @@ """ sessions = self.db.getSessionManager() - # generate a session key - s = '%s%s'%(time.time(), random.random()) - print s - self.session = binascii.b2a_base64(s).strip() - while sessions.exists(self.session): + # generate a unique session key + while 1: s = '%s%s'%(time.time(), random.random()) - self.session = binascii.b2a_base64(s).strip() + s = binascii.b2a_base64(s).strip() + if not sessions.exists(s): + break + self.session = s # clean up the base64 if self.session[-1] == '=':
--- a/roundup/cgi/templating.py Sat May 01 08:19:00 2004 +0000 +++ b/roundup/cgi/templating.py Sun May 02 23:16:05 2004 +0000 @@ -289,10 +289,11 @@ for item in l: if item == 'user': m.append(HTMLUserClass(self._client, item)) - m.append(HTMLClass(self._client, item)) + else: + m.append(HTMLClass(self._client, item)) return m -def lookupIds(db, prop, ids, fail_ok=0, num_re=re.compile('-?\d+')): +def lookupIds(db, prop, ids, fail_ok=0, num_re=re.compile('^-?\d+$')): ''' "fail_ok" should be specified if we wish to pass through bad values (most likely form values that we wish to represent back to the user) ''' @@ -310,7 +311,7 @@ l.append(entry) return l -def lookupKeys(linkcl, key, ids, num_re=re.compile('-?\d+')): +def lookupKeys(linkcl, key, ids, num_re=re.compile('^-?\d+$')): ''' Look up the "key" values for "ids" list - though some may already be key values, not ids. ''' @@ -449,7 +450,7 @@ ''' Return this class' designator (classname) ''' return self._classname - def getItem(self, itemid, num_re=re.compile('-?\d+')): + def getItem(self, itemid, num_re=re.compile('^-?\d+$')): ''' Get an item of this class by its item id. ''' # make sure we're looking at an itemid @@ -680,7 +681,7 @@ # XXX do this return [] - def history(self, direction='descending', dre=re.compile('\d+')): + def history(self, direction='descending', dre=re.compile('^\d+$')): self.view_check() l = ['<table class="history">' @@ -1523,6 +1524,8 @@ def __init__(self, *args, **kwargs): HTMLProperty.__init__(self, *args, **kwargs) if self._value: + self._value = lookupIds(self._db, self._prop, self._value, + fail_ok=1) sortfun = make_sort_function(self._db, self._prop.classname) self._value.sort(sortfun)
--- a/roundup/hyperdb.py Sat May 01 08:19:00 2004 +0000 +++ b/roundup/hyperdb.py Sun May 02 23:16:05 2004 +0000 @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: hyperdb.py,v 1.96 2004-02-11 23:55:08 richard Exp $ +# $Id: hyperdb.py,v 1.97 2004-05-02 23:16:05 richard Exp $ """Hyperdatabase implementation, especially field types. """ @@ -592,7 +592,7 @@ ''' Error converting a raw value into a Hyperdb value ''' pass -def convertLinkValue(db, propname, prop, value, idre=re.compile('\d+')): +def convertLinkValue(db, propname, prop, value, idre=re.compile('^\d+$')): ''' Convert the link value (may be id or key value) to an id value. ''' linkcl = db.classes[prop.classname] if not idre.match(value):
--- a/templates/classic/dbinit.py Sat May 01 08:19:00 2004 +0000 +++ b/templates/classic/dbinit.py Sun May 02 23:16:05 2004 +0000 @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: dbinit.py,v 1.7 2004-03-26 04:50:50 richard Exp $ +# $Id: dbinit.py,v 1.8 2004-05-02 23:16:05 richard Exp $ import os @@ -150,11 +150,11 @@ return db def init(adminpw): - ''' as from the roundupdb method initDB + '''Invoked by the "roundup-admin initialise" command to set up the + initial state of the hyperdb. - Open the new database, and add new nodes - used for initialisation. You - can edit this before running the "roundup-admin initialise" command to - change the initial database entries. + If you wish to change the hyperdb *after* running that command, see + the customisation doc "Database Content" section. ''' dbdir = os.path.join(config.DATABASE, 'files') if not os.path.isdir(dbdir):
