@@ -1558,6 +1558,17 @@ class Class(hyperdb.Class):
15581558 # We define the default here, can be changed in derivative class
15591559 case_sensitive_equal = '='
15601560
1561+ # Some DBs order NULL values last. Set this variable in the backend
1562+ # for prepending an order by clause for each attribute that causes
1563+ # correct sort order for NULLs. Examples:
1564+ # order_by_null_values = '(%s is not NULL)'
1565+ # order_by_null_values = 'notnull(%s)'
1566+ # The format parameter is replaced with the attribute.
1567+ order_by_null_values = None
1568+
1569+ # Assuming DBs can do subselects, overwrite if they cannot.
1570+ supports_subselects = True
1571+
15611572 def schema (self ):
15621573 """ A dumpable version of the schema that we can store in the
15631574 database
@@ -2383,19 +2394,6 @@ def _subselect(self, proptree):
23832394 return '_%s.id not in (select %s from %s%s)' % (classname , nodeid_name ,
23842395 multilink_table , w )
23852396
2386- # Some DBs order NULL values last. Set this variable in the backend
2387- # for prepending an order by clause for each attribute that causes
2388- # correct sort order for NULLs. Examples:
2389- # order_by_null_values = '(%s is not NULL)'
2390- # order_by_null_values = 'notnull(%s)'
2391- # The format parameter is replaced with the attribute.
2392- order_by_null_values = None
2393-
2394- def supports_subselects (self ):
2395- '''Assuming DBs can do subselects, overwrite if they cannot.
2396- '''
2397- return True
2398-
23992397 def _filter_multilink_expression_fallback (
24002398 self , classname , multilink_table , expr ):
24012399 '''This is a fallback for database that do not support
@@ -2441,27 +2439,32 @@ def _filter_multilink_expression_fallback(
24412439 # we have ids of the classname table
24422440 return ids .where ("_%s.id" % classname , self .db .arg )
24432441
2444- def _filter_multilink_expression (self , classname , multilink_table ,
2445- linkid_name , nodeid_name , v ):
2442+ def _filter_multilink_expression (self , proptree , v ):
24462443 """ Filters out elements of the classname table that do not
24472444 match the given expression.
24482445 Returns tuple of 'WHERE' introns for the overall filter.
24492446 """
2447+ classname = proptree .parent .uniqname
2448+ multilink_table = proptree .propclass .table_name
2449+ nid = proptree .propclass .nodeid_name
2450+ lid = proptree .propclass .linkid_name
2451+
24502452 try :
24512453 opcodes = [int (x ) for x in v ]
2452- if min (opcodes ) >= - 1 : raise ValueError ()
2454+ if min (opcodes ) >= - 1 :
2455+ raise ValueError ()
24532456
24542457 expr = compile_expression (opcodes )
24552458
2456- if not self .supports_subselects () :
2459+ if not self .supports_subselects :
24572460 # We heavily rely on subselects. If there is
24582461 # no decent support fall back to slower variant.
24592462 return self ._filter_multilink_expression_fallback (
24602463 classname , multilink_table , expr )
24612464
24622465 atom = \
24632466 "%s IN(SELECT %s FROM %s WHERE %s=a.id)" % (
2464- self .db .arg , linkid_name , multilink_table , nodeid_name )
2467+ self .db .arg , lid , multilink_table , nid )
24652468
24662469 intron = \
24672470 "_%(classname)s.id in (SELECT id " \
@@ -2475,10 +2478,20 @@ def collect_values(n): values.append(n.x)
24752478
24762479 return intron , values
24772480 except :
2478- # original behavior
2479- where = "%s.%s in (%s)" % (
2480- multilink_table , linkid_name , ',' .join ([self .db .arg ] * len (v )))
2481- return where , v , True # True to indicate original
2481+ # fallback behavior when expression parsing above fails
2482+ orclause = ''
2483+ if '-1' in v :
2484+ v = [x for x in v if int (x ) > 0 ]
2485+ orclause = self ._subselect (proptree )
2486+ where = []
2487+ where .append ("%s.%s in (%s)" % (multilink_table , lid ,
2488+ ',' .join ([self .db .arg ] * len (v ))))
2489+ where .append ('_%s.id=%s.%s' % (classname , multilink_table , nid ))
2490+ where = ' and ' .join (where )
2491+ if orclause :
2492+ where = '((' + ' or ' .join ((where + ')' , orclause )) + ')'
2493+
2494+ return where , v
24822495
24832496 def _filter_sql (self , search_matches , filterspec , srt = [], grp = [], retr = 0 ,
24842497 retired = False , exact_match_spec = {}, limit = None ,
@@ -2550,27 +2563,22 @@ def _filter_sql (self, search_matches, filterspec, srt=[], grp=[], retr=0,
25502563 where .append (self ._subselect (p ))
25512564 else :
25522565 frum .append (tn )
2553- gen_join = True
2554-
2555- if p .has_values and isinstance (v , type ([])):
2556- result = self ._filter_multilink_expression (pln ,
2557- tn , lid , nid , v )
2558- # XXX: We dont need an id join if we used the filter
2559- gen_join = len (result ) == 3
2560-
2561- if gen_join :
2562- where .append ('_%s.id=%s.%s' % (pln , tn , nid ))
25632566
25642567 if p .children or p .need_child_retired :
25652568 frum .append ('_%s as _%s' % (cn , ln ))
25662569 where .append ('%s.%s=_%s.id' % (tn , lid , ln ))
25672570 if p .need_child_retired :
25682571 where .append ('_%s.__retired__=0' % (ln ))
25692572
2573+ if not p .has_values or not isinstance (v , type ([])):
2574+ where .append ('_%s.id=%s.%s' % (pln , tn , nid ))
25702575 if p .has_values :
25712576 if isinstance (v , type ([])):
2572- where .append (result [0 ])
2573- args += result [1 ]
2577+ # The where-clause above is conditionally
2578+ # created in _filter_multilink_expression
2579+ w , arg = self ._filter_multilink_expression (p , v )
2580+ where .append (w )
2581+ args += arg
25742582 else :
25752583 where .append ('%s.%s=%s' % (tn , lid , a ))
25762584 args .append (v )
0 commit comments