@@ -19,8 +19,12 @@ class Maintenance:
1919 def db_nuke (self , config ):
2020 """Clear all database contents and drop database itself"""
2121 db = Database (config , 'admin' )
22- db .sql ("DROP DATABASE %s" % config .MYSQL_DBNAME )
23- db .sql ("CREATE DATABASE %s" % config .MYSQL_DBNAME )
22+ try :
23+ db .sql_commit ()
24+ db .sql ("DROP DATABASE %s" % config .MYSQL_DBNAME )
25+ db .sql ("CREATE DATABASE %s" % config .MYSQL_DBNAME )
26+ finally :
27+ db .close ()
2428 if os .path .exists (config .DATABASE ):
2529 shutil .rmtree (config .DATABASE )
2630
@@ -30,10 +34,14 @@ def db_exists(self, config):
3034 # selecting a database to prevent creation of some tables
3135 config .MYSQL_DATABASE = (config .MYSQL_DBHOST , config .MYSQL_DBUSER , config .MYSQL_DBPASSWORD )
3236 db = Database (config , 'admin' )
33- db .conn .select_db (config .MYSQL_DBNAME )
34- config .MYSQL_DATABASE = (config .MYSQL_DBHOST , config .MYSQL_DBUSER , config .MYSQL_DBPASSWORD , config .MYSQL_DBNAME )
35- db .sql ("SHOW TABLES" )
36- tables = db .sql_fetchall ()
37+ try :
38+ db .conn .select_db (config .MYSQL_DBNAME )
39+ config .MYSQL_DATABASE = (config .MYSQL_DBHOST , config .MYSQL_DBUSER ,
40+ config .MYSQL_DBPASSWORD , config .MYSQL_DBNAME )
41+ db .sql ("SHOW TABLES" )
42+ tables = db .sql_fetchall ()
43+ finally :
44+ db .close ()
3745 if tables or os .path .exists (config .DATABASE ):
3846 return 1
3947 return 0
@@ -64,12 +72,6 @@ def open_connection(self):
6472 self .sql ("CREATE TABLE schema (schema TEXT) TYPE=BDB" )
6573 self .sql ("CREATE TABLE ids (name varchar(255), num INT) TYPE=BDB" )
6674
67- def close (self ):
68- try :
69- self .conn .close ()
70- except MySQLdb .OperationalError , message :
71- raise
72-
7375 def __repr__ (self ):
7476 return '<myroundsql 0x%x>' % id (self )
7577
@@ -179,6 +181,8 @@ def find(self, **propspec):
179181 if type (values ) is type ('' ):
180182 allvalues += (values ,)
181183 where .append ('_%s = %s' % (prop , a ))
184+ elif values is None :
185+ where .append ('_%s is NULL' % prop )
182186 else :
183187 allvalues += tuple (values .keys ())
184188 where .append ('_%s in (%s)' % (prop , ',' .join ([a ]* len (values ))))
@@ -213,6 +217,213 @@ def find(self, **propspec):
213217
214218 return l
215219
220+ # we're overriding this method for ONE missing bit of functionality.
221+ # look for "I can't believe it's not a toy RDBMS" below
222+ def filter (self , search_matches , filterspec , sort = (None ,None ),
223+ group = (None ,None )):
224+ ''' Return a list of the ids of the active nodes in this class that
225+ match the 'filter' spec, sorted by the group spec and then the
226+ sort spec
227+
228+ "filterspec" is {propname: value(s)}
229+ "sort" and "group" are (dir, prop) where dir is '+', '-' or None
230+ and prop is a prop name or None
231+ "search_matches" is {nodeid: marker}
232+
233+ The filter must match all properties specificed - but if the
234+ property value to match is a list, any one of the values in the
235+ list may match for that property to match.
236+ '''
237+ # just don't bother if the full-text search matched diddly
238+ if search_matches == {}:
239+ return []
240+
241+ cn = self .classname
242+
243+ timezone = self .db .getUserTimezone ()
244+
245+ # figure the WHERE clause from the filterspec
246+ props = self .getprops ()
247+ frum = ['_' + cn ]
248+ where = []
249+ args = []
250+ a = self .db .arg
251+ for k , v in filterspec .items ():
252+ propclass = props [k ]
253+ # now do other where clause stuff
254+ if isinstance (propclass , Multilink ):
255+ tn = '%s_%s' % (cn , k )
256+ if v in ('-1' , ['-1' ]):
257+ # only match rows that have count(linkid)=0 in the
258+ # corresponding multilink table)
259+
260+ # "I can't believe it's not a toy RDBMS"
261+ # see, even toy RDBMSes like gadfly and sqlite can do
262+ # sub-selects...
263+ self .db .sql ('select nodeid from %s' % tn )
264+ s = ',' .join ([x [0 ] for x in self .db .sql_fetchall ()])
265+
266+ where .append ('id not in (%s)' % s )
267+ elif isinstance (v , type ([])):
268+ frum .append (tn )
269+ s = ',' .join ([a for x in v ])
270+ where .append ('id=%s.nodeid and %s.linkid in (%s)' % (tn ,tn ,s ))
271+ args = args + v
272+ else :
273+ frum .append (tn )
274+ where .append ('id=%s.nodeid and %s.linkid=%s' % (tn , tn , a ))
275+ args .append (v )
276+ elif k == 'id' :
277+ if isinstance (v , type ([])):
278+ s = ',' .join ([a for x in v ])
279+ where .append ('%s in (%s)' % (k , s ))
280+ args = args + v
281+ else :
282+ where .append ('%s=%s' % (k , a ))
283+ args .append (v )
284+ elif isinstance (propclass , String ):
285+ if not isinstance (v , type ([])):
286+ v = [v ]
287+
288+ # Quote the bits in the string that need it and then embed
289+ # in a "substring" search. Note - need to quote the '%' so
290+ # they make it through the python layer happily
291+ v = ['%%' + self .db .sql_stringquote (s )+ '%%' for s in v ]
292+
293+ # now add to the where clause
294+ where .append (' or ' .join (["_%s LIKE '%s'" % (k , s ) for s in v ]))
295+ # note: args are embedded in the query string now
296+ elif isinstance (propclass , Link ):
297+ if isinstance (v , type ([])):
298+ if '-1' in v :
299+ v = v [:]
300+ v .remove ('-1' )
301+ xtra = ' or _%s is NULL' % k
302+ else :
303+ xtra = ''
304+ if v :
305+ s = ',' .join ([a for x in v ])
306+ where .append ('(_%s in (%s)%s)' % (k , s , xtra ))
307+ args = args + v
308+ else :
309+ where .append ('_%s is NULL' % k )
310+ else :
311+ if v == '-1' :
312+ v = None
313+ where .append ('_%s is NULL' % k )
314+ else :
315+ where .append ('_%s=%s' % (k , a ))
316+ args .append (v )
317+ elif isinstance (propclass , Date ):
318+ if isinstance (v , type ([])):
319+ s = ',' .join ([a for x in v ])
320+ where .append ('_%s in (%s)' % (k , s ))
321+ args = args + [date .Date (x ).serialise () for x in v ]
322+ else :
323+ try :
324+ # Try to filter on range of dates
325+ date_rng = Range (v , date .Date , offset = timezone )
326+ if (date_rng .from_value ):
327+ where .append ('_%s >= %s' % (k , a ))
328+ args .append (date_rng .from_value .serialise ())
329+ if (date_rng .to_value ):
330+ where .append ('_%s <= %s' % (k , a ))
331+ args .append (date_rng .to_value .serialise ())
332+ except ValueError :
333+ # If range creation fails - ignore that search parameter
334+ pass
335+ elif isinstance (propclass , Interval ):
336+ if isinstance (v , type ([])):
337+ s = ',' .join ([a for x in v ])
338+ where .append ('_%s in (%s)' % (k , s ))
339+ args = args + [date .Interval (x ).serialise () for x in v ]
340+ else :
341+ try :
342+ # Try to filter on range of intervals
343+ date_rng = Range (v , date .Interval )
344+ if (date_rng .from_value ):
345+ where .append ('_%s >= %s' % (k , a ))
346+ args .append (date_rng .from_value .serialise ())
347+ if (date_rng .to_value ):
348+ where .append ('_%s <= %s' % (k , a ))
349+ args .append (date_rng .to_value .serialise ())
350+ except ValueError :
351+ # If range creation fails - ignore that search parameter
352+ pass
353+ #where.append('_%s=%s'%(k, a))
354+ #args.append(date.Interval(v).serialise())
355+ else :
356+ if isinstance (v , type ([])):
357+ s = ',' .join ([a for x in v ])
358+ where .append ('_%s in (%s)' % (k , s ))
359+ args = args + v
360+ else :
361+ where .append ('_%s=%s' % (k , a ))
362+ args .append (v )
363+
364+ # don't match retired nodes
365+ where .append ('__retired__ <> 1' )
366+
367+ # add results of full text search
368+ if search_matches is not None :
369+ v = search_matches .keys ()
370+ s = ',' .join ([a for x in v ])
371+ where .append ('id in (%s)' % s )
372+ args = args + v
373+
374+ # "grouping" is just the first-order sorting in the SQL fetch
375+ # can modify it...)
376+ orderby = []
377+ ordercols = []
378+ if group [0 ] is not None and group [1 ] is not None :
379+ if group [0 ] != '-' :
380+ orderby .append ('_' + group [1 ])
381+ ordercols .append ('_' + group [1 ])
382+ else :
383+ orderby .append ('_' + group [1 ]+ ' desc' )
384+ ordercols .append ('_' + group [1 ])
385+
386+ # now add in the sorting
387+ group = ''
388+ if sort [0 ] is not None and sort [1 ] is not None :
389+ direction , colname = sort
390+ if direction != '-' :
391+ if colname == 'id' :
392+ orderby .append (colname )
393+ else :
394+ orderby .append ('_' + colname )
395+ ordercols .append ('_' + colname )
396+ else :
397+ if colname == 'id' :
398+ orderby .append (colname + ' desc' )
399+ ordercols .append (colname )
400+ else :
401+ orderby .append ('_' + colname + ' desc' )
402+ ordercols .append ('_' + colname )
403+
404+ # construct the SQL
405+ frum = ',' .join (frum )
406+ if where :
407+ where = ' where ' + (' and ' .join (where ))
408+ else :
409+ where = ''
410+ cols = ['id' ]
411+ if orderby :
412+ cols = cols + ordercols
413+ order = ' order by %s' % (',' .join (orderby ))
414+ else :
415+ order = ''
416+ cols = ',' .join (cols )
417+ sql = 'select %s from %s %s%s%s' % (cols , frum , where , group , order )
418+ args = tuple (args )
419+ if __debug__ :
420+ print >> hyperdb .DEBUG , 'filter' , (self , sql , args )
421+ self .db .cursor .execute (sql , args )
422+ l = self .db .cursor .fetchall ()
423+
424+ # return the IDs (the first column)
425+ return [row [0 ] for row in l ]
426+
216427class Class (MysqlClass , rdbms_common .Class ):
217428 pass
218429class IssueClass (MysqlClass , rdbms_common .IssueClass ):
0 commit comments