1515from . import blob
1616from .utils import user_choice
1717from .heading import Heading
18- from .errors import DuplicateError , AccessError , DataJointError , UnknownAttributeError , IntegrityError
18+ from .errors import (DuplicateError , AccessError , DataJointError , UnknownAttributeError ,
19+ IntegrityError )
1920from .version import __version__ as version
2021
2122logger = logging .getLogger (__name__ )
2223
23- foregn_key_error_regexp = re .compile (
24+ foreign_key_error_regexp = re .compile (
2425 r"[\w\s:]*\((?P<child>`[^`]+`.`[^`]+`), "
2526 r"CONSTRAINT (?P<name>`[^`]+`) "
26- r"FOREIGN KEY \((?P<fk_attrs>[^)]+)\) "
27- r"REFERENCES (?P<parent>`[^`]+`(\.`[^`]+`)?) \((?P<pk_attrs>[^)]+)\)" )
27+ r"(FOREIGN KEY \((?P<fk_attrs>[^)]+)\) "
28+ r"REFERENCES (?P<parent>`[^`]+`(\.`[^`]+`)?) \((?P<pk_attrs>[^)]+)\)[\s\w]+\))?" )
29+
30+ constraint_info_query = ' ' .join ("""
31+ SELECT
32+ COLUMN_NAME as fk_attrs,
33+ CONCAT('`', REFERENCED_TABLE_SCHEMA, '`.`', REFERENCED_TABLE_NAME, '`') as parent,
34+ REFERENCED_COLUMN_NAME as pk_attrs
35+ FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
36+ WHERE
37+ CONSTRAINT_NAME = %s AND TABLE_SCHEMA = %s AND TABLE_NAME = %s;
38+ """ .split ())
2839
2940
3041class _RenameMap (tuple ):
@@ -344,23 +355,31 @@ def _delete_cascade(self):
344355 try :
345356 delete_count += self .delete_quick (get_count = True )
346357 except IntegrityError as error :
347- match = foregn_key_error_regexp .match (error .args [0 ])
348- assert match is not None , "foreign key parsing error"
358+ match = foreign_key_error_regexp .match (error .args [0 ]).groupdict ()
359+ if "`.`" not in match ['child' ]: # if schema name missing, use self
360+ match ['child' ] = '{}.{}' .format (self .full_table_name .split ("." )[0 ],
361+ match ['child' ])
362+ if match ['pk_attrs' ] is not None : # fully matched, adjusting the keys
363+ match ['fk_attrs' ] = [k .strip ('`' ) for k in match ['fk_attrs' ].split (',' )]
364+ match ['pk_attrs' ] = [k .strip ('`' ) for k in match ['pk_attrs' ].split (',' )]
365+ else : # only partially matched, querying with constraint to determine keys
366+ match ['fk_attrs' ], match ['parent' ], match ['pk_attrs' ] = list (map (
367+ list , zip (* self .connection .query (constraint_info_query , args = (
368+ match ['name' ].strip ('`' ),
369+ * [_ .strip ('`' ) for _ in match ['child' ].split ('`.`' )]
370+ )).fetchall ())))
371+ match ['parent' ] = match ['parent' ][0 ]
349372 # restrict child by self if
350373 # 1. if self's restriction attributes are not in child's primary key
351374 # 2. if child renames any attributes
352375 # otherwise restrict child by self's restriction.
353- child = match .group ('child' )
354- if "`.`" not in child : # if schema name is not included, take it from self
355- child = self .full_table_name .split ("`." )[0 ] + child
356- child = FreeTable (self .connection , child )
376+ child = FreeTable (self .connection , match ['child' ])
357377 if set (self .restriction_attributes ) <= set (child .primary_key ) and \
358- match . group ( 'fk_attrs' ) == match . group ( 'pk_attrs' ) :
378+ match [ 'fk_attrs' ] == match [ 'pk_attrs' ] :
359379 child ._restriction = self ._restriction
360- elif match .group ('fk_attrs' ) != match .group ('pk_attrs' ):
361- fk_attrs = [k .strip ('`' ) for k in match .group ('fk_attrs' ).split (',' )]
362- pk_attrs = [k .strip ('`' ) for k in match .group ('pk_attrs' ).split (',' )]
363- child &= self .proj (** dict (zip (fk_attrs , pk_attrs )))
380+ elif match ['fk_attrs' ] != match ['pk_attrs' ]:
381+ child &= self .proj (** dict (zip (match ['fk_attrs' ],
382+ match ['pk_attrs' ])))
364383 else :
365384 child &= self .proj ()
366385 delete_count += child ._delete_cascade ()
@@ -375,8 +394,10 @@ def _delete_cascade(self):
375394 def delete (self , transaction = True , safemode = None ):
376395 """
377396 Deletes the contents of the table and its dependent tables, recursively.
397+
378398 :param transaction: if True, use the entire delete becomes an atomic transaction.
379- :param safemode: If True, prohibit nested transactions and prompt to confirm. Default is dj.config['safemode'].
399+ :param safemode: If True, prohibit nested transactions and prompt to confirm. Default
400+ is dj.config['safemode'].
380401 """
381402 safemode = config ['safemode' ] if safemode is None else safemode
382403
0 commit comments