158158callable_ = lambda c : isinstance (c , Callable )
159159multi_types = (list , tuple , frozenset , set , range , types .GeneratorType )
160160
161- def qesc (s ):
162- return s .replace ('"' , '""' )
161+ def qesc (s , q = '"' ):
162+ return s .replace (q , q + q )
163163
164164def unqesc (part ):
165165 part = part .strip ()
@@ -896,10 +896,7 @@ def alias(self, name):
896896 self ._update_hash ()
897897
898898 def clone (self ):
899- # clone() copies __dict__ without running __init__, so the copy would
900- # otherwise inherit the source's hash. An anonymous sub-select hashes
901- # on id(self), and the source may be garbage-collected, leaving the
902- # copy keyed on an address a later object can be allocated at.
899+ # The copy would otherwise keep the source's id()-based hash.
903900 clone = super (_HashableSource , self ).clone ()
904901 clone ._update_hash ()
905902 return clone
@@ -2446,12 +2443,7 @@ def __sql__(self, ctx):
24462443 super (CompoundSelectQuery , self ).__sql__ (ctx )
24472444
24482445 with ctx (parentheses = self ._subquery_parens (ctx )):
2449- # Snapshot the aliases assigned by the enclosing scope before
2450- # rendering either side. A correlated reference from the right-hand
2451- # query must resolve to one of these outer aliases; otherwise the
2452- # right-hand query's fresh alias scope assigns the outer source a
2453- # new, dangling alias. The left-hand query shares the enclosing
2454- # scope, so only the right-hand query needs the seed.
2446+ # Correlated rhs refs must resolve to the enclosing aliases.
24552447 outer_aliases = dict (ctx .alias_manager .mapping )
24562448
24572449 # Should the left-hand query be wrapped in parentheses?
@@ -2460,9 +2452,7 @@ def __sql__(self, ctx):
24602452 ctx .sql (self .lhs )
24612453 ctx .literal (' %s ' % self .op )
24622454 with ctx .push_alias ():
2463- # Seed with only the outer aliases (not the left-hand query's
2464- # own sources) so the right-hand query's own sources still
2465- # receive fresh aliases.
2455+ # Seed only the outer aliases so rhs sources get fresh ones.
24662456 ctx .alias_manager .mapping .update (outer_aliases )
24672457 # Should the right-hand query be wrapped in parentheses?
24682458 rhs_parens = self ._wrap_parens (ctx , self .rhs )
@@ -4702,9 +4692,8 @@ def get_indexes(self, table, schema=None):
47024692 WHERE t.relname = %s AND t.relkind = %s AND n.nspname = %s
47034693 ORDER BY idx.indisunique DESC, i.relname;"""
47044694 cursor = self .execute_sql (query , (table , 'r' , schema or 'public' ))
4705- unesc = lambda cols : [unqesc (c ) for c in cols ]
4706- return [IndexMetadata (name , sql .rstrip (' ;' ), unesc (cols ), unique ,
4707- table )
4695+ return [IndexMetadata (name , sql .rstrip (' ;' ),
4696+ [unqesc (c ) for c in cols ], unique , table )
47084697 for name , sql , unique , cols in cursor .fetchall ()]
47094698
47104699 def get_columns (self , table , schema = None ):
@@ -4950,9 +4939,9 @@ def get_views(self, schema=None):
49504939 return [ViewMetadata (* row ) for row in cursor .fetchall ()]
49514940
49524941 def _show_index_target (self , table , schema ):
4953- table = table . replace ( '`' , '` `' )
4942+ table = qesc ( table , '`' )
49544943 if schema :
4955- return '`%s`.`%s`' % (schema . replace ( '`' , '` `' ), table )
4944+ return '`%s`.`%s`' % (qesc ( schema , '`' ), table )
49564945 return '`%s`' % table
49574946
49584947 def get_indexes (self , table , schema = None ):
@@ -7940,11 +7929,8 @@ def dependencies(self, search_nullable=True, exclude_null_children=False):
79407929 .where (node ))
79417930 if not fk .null or search_nullable :
79427931 queries .setdefault (rel_model , []).append ((node , fk ))
7943- # A nullable child will be updated rather than deleted, so
7944- # its children do not need to be visited - but do not mark
7945- # it seen, as it may also be reachable (and deleted) via a
7946- # non-nullable path, in which case its children must be
7947- # processed.
7932+ # A nullable child is updated, not deleted, so skip its
7933+ # children but leave it unseen for non-nullable paths.
79487934 if not (fk .null and exclude_null_children ):
79497935 stack .append ((rel_model , subquery ))
79507936
@@ -9115,7 +9101,7 @@ def process_row(self, row):
91159101 if is_dict :
91169102 instance [attr ] = joined_instance
91179103 elif is_fk and joined_instance is None :
9118- # None in __rel__ marks a verified-absent row; fk id is kept .
9104+ # None in __rel__ marks a verified-absent row, fk id intact .
91199105 instance .__rel__ [attr ] = None
91209106 else :
91219107 setattr (instance , attr , joined_instance )
@@ -9297,15 +9283,6 @@ def _bucket(field, is_backref, children, parents):
92979283 # backref lists directly, skipping descriptor and dirty tracking.
92989284 name , rel_name = field .name , field .rel_field .name
92999285 if is_backref :
9300- # children are the referenced rows, parents carry the fk.
9301- id_map = {}
9302- for child in children :
9303- id_map [child .__data__ [rel_name ]] = child
9304- for parent in parents :
9305- key = parent .__data__ [name ]
9306- if key in id_map :
9307- parent .__rel__ [name ] = id_map [key ]
9308- else :
93099286 # children carry the fk, parents get backref lists.
93109287 buckets = {}
93119288 for child in children :
@@ -9316,6 +9293,15 @@ def _bucket(field, is_backref, children, parents):
93169293 for inst in rel :
93179294 inst .__rel__ [name ] = parent
93189295 setattr (parent , backref , rel )
9296+ else :
9297+ # children are the referenced rows, parents carry the fk.
9298+ id_map = {}
9299+ for child in children :
9300+ id_map [child .__data__ [rel_name ]] = child
9301+ for parent in parents :
9302+ key = parent .__data__ [name ]
9303+ if key in id_map :
9304+ parent .__rel__ [name ] = id_map [key ]
93199305
93209306
93219307class Load (Node ):
@@ -9403,7 +9389,7 @@ def _run(self, parents, parent_query, depth=0, database=None):
94039389 parent_query , parents )
94049390 # The whole tree runs on the database the parent ran against.
94059391 children = list (child_query .execute (database ))
9406- _bucket (field , not self ._is_backref , children , parents )
9392+ _bucket (field , self ._is_backref , children , parents )
94079393 return children , child_query
94089394
94099395 @staticmethod
0 commit comments