comparison roundup/backends/rdbms_common.py @ 2217:98d3bf8ffb19

store Intervals as two columns (and other fixes
author Richard Jones <richard@users.sourceforge.net>
date Sun, 18 Apr 2004 05:31:03 +0000
parents 85954067e496
children 1d1362c54c94
comparison
equal deleted inserted replaced
2216:759ed26e24dd 2217:98d3bf8ffb19
1 # $Id: rdbms_common.py,v 1.90 2004-04-08 00:40:20 richard Exp $ 1 # $Id: rdbms_common.py,v 1.91 2004-04-18 05:31:02 richard Exp $
2 ''' Relational database (SQL) backend common code. 2 ''' Relational database (SQL) backend common code.
3 3
4 Basics: 4 Basics:
5 5
6 - map roundup classes to relational tables 6 - map roundup classes to relational tables
203 # change the schema structure 203 # change the schema structure
204 self.database_schema = {'tables': self.database_schema} 204 self.database_schema = {'tables': self.database_schema}
205 205
206 # version 1 didn't have the actor column (note that in 206 # version 1 didn't have the actor column (note that in
207 # MySQL this will also transition the tables to typed columns) 207 # MySQL this will also transition the tables to typed columns)
208 self.add_actor_column() 208 self.add_new_columns_v2()
209 209
210 # version 1 doesn't have the OTK, session and indexing in the 210 # version 1 doesn't have the OTK, session and indexing in the
211 # database 211 # database
212 self.create_version_2_tables() 212 self.create_version_2_tables()
213 213
221 def reindex(self): 221 def reindex(self):
222 for klass in self.classes.values(): 222 for klass in self.classes.values():
223 for nodeid in klass.list(): 223 for nodeid in klass.list():
224 klass.index(nodeid) 224 klass.index(nodeid)
225 self.indexer.save_index() 225 self.indexer.save_index()
226
227 226
228 hyperdb_to_sql_datatypes = { 227 hyperdb_to_sql_datatypes = {
229 hyperdb.String : 'VARCHAR(255)', 228 hyperdb.String : 'VARCHAR(255)',
230 hyperdb.Date : 'TIMESTAMP', 229 hyperdb.Date : 'TIMESTAMP',
231 hyperdb.Link : 'INTEGER', 230 hyperdb.Link : 'INTEGER',
258 #and prop.find('Multilink') != -1: 257 #and prop.find('Multilink') != -1:
259 #mls.append(col) 258 #mls.append(col)
260 259
261 datatype = self.hyperdb_to_sql_datatypes[prop.__class__] 260 datatype = self.hyperdb_to_sql_datatypes[prop.__class__]
262 cols.append(('_'+col, datatype)) 261 cols.append(('_'+col, datatype))
262
263 # Intervals stored as two columns
264 if isinstance(prop, Interval):
265 cols.append(('__'+col+'_int__', 'BIGINT'))
263 266
264 cols.sort() 267 cols.sort()
265 return cols, mls 268 return cols, mls
266 269
267 def update_class(self, spec, old_spec, force=0): 270 def update_class(self, spec, old_spec, force=0):
611 614
612 hyperdb_to_sql_value = { 615 hyperdb_to_sql_value = {
613 hyperdb.String : str, 616 hyperdb.String : str,
614 hyperdb.Date : lambda x: x.formal(sep=' ', sec='%.3f'), 617 hyperdb.Date : lambda x: x.formal(sep=' ', sec='%.3f'),
615 hyperdb.Link : int, 618 hyperdb.Link : int,
616 hyperdb.Interval : lambda x: x.serialise(), 619 hyperdb.Interval : str,
617 hyperdb.Password : str, 620 hyperdb.Password : str,
618 hyperdb.Boolean : lambda x: x and 'TRUE' or 'FALSE', 621 hyperdb.Boolean : lambda x: x and 'TRUE' or 'FALSE',
619 hyperdb.Number : lambda x: x, 622 hyperdb.Number : lambda x: x,
620 } 623 }
621 def addnode(self, classname, nodeid, node): 624 def addnode(self, classname, nodeid, node):
655 self.cache_lru.remove(key) 658 self.cache_lru.remove(key)
656 659
657 # figure the values to insert 660 # figure the values to insert
658 vals = [] 661 vals = []
659 for col,dt in cols: 662 for col,dt in cols:
663 # this is somewhat dodgy....
664 if col.endswith('_int__'):
665 # XXX eugh, this test suxxors
666 value = values[col[2:-6]]
667 # this is an Interval special "int" column
668 if value is not None:
669 vals.append(value.as_seconds())
670 else:
671 vals.append(value)
672 continue
673
660 prop = props[col[1:]] 674 prop = props[col[1:]]
661 value = values[col[1:]] 675 value = values[col[1:]]
662 if value: 676 if value:
663 value = self.hyperdb_to_sql_value[prop.__class__](value) 677 value = self.hyperdb_to_sql_value[prop.__class__](value)
664 vals.append(value) 678 vals.append(value)
708 # add the multilinks separately 722 # add the multilinks separately
709 for col in values.keys(): 723 for col in values.keys():
710 prop = props[col] 724 prop = props[col]
711 if isinstance(prop, Multilink): 725 if isinstance(prop, Multilink):
712 mls.append(col) 726 mls.append(col)
727 elif isinstance(prop, Interval):
728 # Intervals store the seconds value too
729 cols.append(col)
730 # extra leading '_' added by code below
731 cols.append('_' +col + '_int__')
713 else: 732 else:
714 cols.append(col) 733 cols.append(col)
715 cols.sort() 734 cols.sort()
716 735
717 # figure the values to insert 736 # figure the values to insert
718 vals = [] 737 vals = []
719 for col in cols: 738 for col in cols:
720 prop = props[col] 739 if col.endswith('_int__'):
721 value = values[col] 740 # XXX eugh, this test suxxors
722 if value is not None: 741 # Intervals store the seconds value too
723 value = self.hyperdb_to_sql_value[prop.__class__](value) 742 col = col[1:-6]
724 vals.append(value) 743 prop = props[col]
744 value = values[col]
745 if value is None:
746 vals.append(None)
747 else:
748 vals.append(value.as_seconds())
749 else:
750 prop = props[col]
751 value = values[col]
752 if value is None:
753 e = None
754 else:
755 e = self.hyperdb_to_sql_value[prop.__class__](value)
756 vals.append(e)
757
725 vals.append(int(nodeid)) 758 vals.append(int(nodeid))
726 vals = tuple(vals) 759 vals = tuple(vals)
727 760
728 # if there's any updates to regular columns, do them 761 # if there's any updates to regular columns, do them
729 if cols: 762 if cols:
814 # make up the node 847 # make up the node
815 node = {} 848 node = {}
816 props = cl.getprops(protected=1) 849 props = cl.getprops(protected=1)
817 for col in range(len(cols)): 850 for col in range(len(cols)):
818 name = cols[col][0][1:] 851 name = cols[col][0][1:]
852 if name.endswith('_int__'):
853 # XXX eugh, this test suxxors
854 # ignore the special Interval-as-seconds column
855 continue
819 value = values[col] 856 value = values[col]
820 if value is not None: 857 if value is not None:
821 value = self.sql_to_hyperdb_value[props[name].__class__](value) 858 value = self.sql_to_hyperdb_value[props[name].__class__](value)
822 node[name] = value 859 node[name] = value
823 860
1838 where.append(propname) 1875 where.append(propname)
1839 args.append(requirements[propname].lower()) 1876 args.append(requirements[propname].lower())
1840 1877
1841 # generate the where clause 1878 # generate the where clause
1842 s = ' and '.join(['lower(_%s)=%s'%(col, self.db.arg) for col in where]) 1879 s = ' and '.join(['lower(_%s)=%s'%(col, self.db.arg) for col in where])
1843 sql = 'select id from _%s where %s and __retired__=%s'%(self.classname, 1880 sql = 'select id from _%s where %s and __retired__<>%s'%(
1844 s, self.db.arg) 1881 self.classname, s, self.db.arg)
1845 args.append(0) 1882 args.append(1)
1846 self.db.sql(sql, tuple(args)) 1883 self.db.sql(sql, tuple(args))
1847 # XXX numeric ids 1884 # XXX numeric ids
1848 l = [str(x[0]) for x in self.db.sql_fetchall()] 1885 l = [str(x[0]) for x in self.db.sql_fetchall()]
1849 if __debug__: 1886 if __debug__:
1850 print >>hyperdb.DEBUG, 'find ... ', l 1887 print >>hyperdb.DEBUG, 'find ... ', l
1987 args.append(dc(date_rng.to_value)) 2024 args.append(dc(date_rng.to_value))
1988 except ValueError: 2025 except ValueError:
1989 # If range creation fails - ignore that search parameter 2026 # If range creation fails - ignore that search parameter
1990 pass 2027 pass
1991 elif isinstance(propclass, Interval): 2028 elif isinstance(propclass, Interval):
2029 # filter using the __<prop>_int__ column
1992 if isinstance(v, type([])): 2030 if isinstance(v, type([])):
1993 s = ','.join([a for x in v]) 2031 s = ','.join([a for x in v])
1994 where.append('_%s in (%s)'%(k, s)) 2032 where.append('__%s_int__ in (%s)'%(k, s))
1995 args = args + [date.Interval(x).serialise() for x in v] 2033 args = args + [date.Interval(x).as_seconds() for x in v]
1996 else: 2034 else:
1997 try: 2035 try:
1998 # Try to filter on range of intervals 2036 # Try to filter on range of intervals
1999 date_rng = Range(v, date.Interval) 2037 date_rng = Range(v, date.Interval)
2000 if date_rng.from_value: 2038 if date_rng.from_value:
2001 where.append('_%s >= %s'%(k, a)) 2039 where.append('__%s_int__ >= %s'%(k, a))
2002 args.append(date_rng.from_value.serialise()) 2040 args.append(date_rng.from_value.as_seconds())
2003 if date_rng.to_value: 2041 if date_rng.to_value:
2004 where.append('_%s <= %s'%(k, a)) 2042 where.append('__%s_int__ <= %s'%(k, a))
2005 args.append(date_rng.to_value.serialise()) 2043 args.append(date_rng.to_value.as_seconds())
2006 except ValueError: 2044 except ValueError:
2007 # If range creation fails - ignore that search parameter 2045 # If range creation fails - ignore that search parameter
2008 pass 2046 pass
2009 #where.append('_%s=%s'%(k, a))
2010 #args.append(date.Interval(v).serialise())
2011 else: 2047 else:
2012 if isinstance(v, type([])): 2048 if isinstance(v, type([])):
2013 s = ','.join([a for x in v]) 2049 s = ','.join([a for x in v])
2014 where.append('_%s in (%s)'%(k, s)) 2050 where.append('_%s in (%s)'%(k, s))
2015 args = args + v 2051 args = args + v
2035 sdir, prop = sortby 2071 sdir, prop = sortby
2036 if sdir and prop: 2072 if sdir and prop:
2037 if isinstance(props[prop], Multilink): 2073 if isinstance(props[prop], Multilink):
2038 mlsort.append(sortby) 2074 mlsort.append(sortby)
2039 continue 2075 continue
2076 elif isinstance(props[prop], Interval):
2077 # use the int column for sorting
2078 o = '__'+prop+'_int__'
2079 ordercols.append(o)
2040 elif prop == 'id': 2080 elif prop == 'id':
2041 o = 'id' 2081 o = 'id'
2042 else: 2082 else:
2043 o = '_'+prop 2083 o = '_'+prop
2044 ordercols.append(o) 2084 ordercols.append(o)
2070 self.db.cursor.execute(sql) 2110 self.db.cursor.execute(sql)
2071 l = self.db.sql_fetchall() 2111 l = self.db.sql_fetchall()
2072 2112
2073 # return the IDs (the first column) 2113 # return the IDs (the first column)
2074 # XXX numeric ids 2114 # XXX numeric ids
2075 l = [str(row[0]) for row in l] 2115 l = [str(row[0]) for row in l]
2076 2116
2077 if not mlsort: 2117 if not mlsort:
2078 return l 2118 return l
2079 2119
2080 # ergh. someone wants to sort by a multilink. 2120 # ergh. someone wants to sort by a multilink.
2245 pwd.unpack(value) 2285 pwd.unpack(value)
2246 value = pwd 2286 value = pwd
2247 d[propname] = value 2287 d[propname] = value
2248 2288
2249 # get a new id if necessary 2289 # get a new id if necessary
2250 if newid is None or not self.hasnode(newid): 2290 if newid is None:
2251 newid = self.db.newid(self.classname) 2291 newid = self.db.newid(self.classname)
2252 self.db.addnode(self.classname, newid, d) 2292
2293 # insert new node or update existing?
2294 if not self.hasnode(newid):
2295 self.db.addnode(self.classname, newid, d) # insert
2253 else: 2296 else:
2254 # update 2297 self.db.setnode(self.classname, newid, d) # update
2255 self.db.setnode(self.classname, newid, d)
2256 2298
2257 # retire? 2299 # retire?
2258 if retire: 2300 if retire:
2259 # use the arg for __retired__ to cope with any odd database type 2301 # use the arg for __retired__ to cope with any odd database type
2260 # conversion (hello, sqlite) 2302 # conversion (hello, sqlite)

Roundup Issue Tracker: http://roundup-tracker.org/