|
20 | 20 |
|
21 | 21 | logger = logging.getLogger(__name__) |
22 | 22 |
|
23 | | -foregn_key_error_regexp = re.compile( |
| 23 | +foreign_key_full_error_regexp = re.compile( |
24 | 24 | r"[\w\s:]*\((?P<child>`[^`]+`.`[^`]+`), " |
25 | 25 | r"CONSTRAINT (?P<name>`[^`]+`) " |
26 | 26 | r"FOREIGN KEY \((?P<fk_attrs>[^)]+)\) " |
27 | | - r"REFERENCES (?P<parent>`[^`]+`(\.`[^`]+`)?) \((?P<pk_attrs>[^)]+)\)") |
| 27 | + r"REFERENCES (?P<parent>`[^`]+`(\.`[^`]+`)?) \((?P<pk_attrs>[^)]+)\)[\s\w]+\)") |
| 28 | + |
| 29 | +foreign_key_partial_error_regexp = re.compile( |
| 30 | + r"[\w\s:]*\((?P<child>`[^`]+?`.`[^`]+?`), " |
| 31 | + r"CONSTRAINT (?P<name>`[^`]+?`) ") |
28 | 32 |
|
29 | 33 |
|
30 | 34 | class _RenameMap(tuple): |
@@ -344,23 +348,47 @@ def _delete_cascade(self): |
344 | 348 | try: |
345 | 349 | delete_count += self.delete_quick(get_count=True) |
346 | 350 | except IntegrityError as error: |
347 | | - match = foregn_key_error_regexp.match(error.args[0]) |
348 | | - assert match is not None, "foreign key parsing error" |
| 351 | + try: |
| 352 | + # try to parse error for child info |
| 353 | + match = foreign_key_full_error_regexp.match(error.args[0]).groupdict() |
| 354 | + if "`.`" not in match['child']: # if schema name is not included, use self |
| 355 | + match['child'] = '{}.{}'.format(self.full_table_name.split(".")[0], |
| 356 | + match['child']) |
| 357 | + match['fk_attrs'] = [k.strip('`') for k in match['fk_attrs'].split(',')] |
| 358 | + match['pk_attrs'] = [k.strip('`') for k in match['pk_attrs'].split(',')] |
| 359 | + except AttributeError: |
| 360 | + # additional query required due to truncated error, trying partial regex |
| 361 | + match = foreign_key_partial_error_regexp.match(error.args[0]).groupdict() |
| 362 | + if "`.`" not in match['child']: # if schema name is not included, use self |
| 363 | + match['child'] = '{}.{}'.format(self.full_table_name.split(".")[0], |
| 364 | + match['child']) |
| 365 | + match['fk_attrs'], match['parent'], match['pk_attrs'] = list(map(list, zip( |
| 366 | + *self.connection.query( |
| 367 | + """ |
| 368 | + SELECT |
| 369 | + COLUMN_NAME as fk_attrs, |
| 370 | + CONCAT('`', REFERENCED_TABLE_SCHEMA, '`.`', |
| 371 | + REFERENCED_TABLE_NAME, '`') as parent, |
| 372 | + REFERENCED_COLUMN_NAME as pk_attrs |
| 373 | + FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE |
| 374 | + WHERE |
| 375 | + CONSTRAINT_NAME = %s AND TABLE_SCHEMA = %s AND TABLE_NAME = %s; |
| 376 | + """, |
| 377 | + args=(match['name'].strip('`'), |
| 378 | + *[_.strip('`') for _ in match['child'].split('`.`')]) |
| 379 | + ).fetchall()))) |
| 380 | + match['parent'] = match['parent'][0] |
349 | 381 | # restrict child by self if |
350 | 382 | # 1. if self's restriction attributes are not in child's primary key |
351 | 383 | # 2. if child renames any attributes |
352 | 384 | # 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) |
| 385 | + child = FreeTable(self.connection, match['child']) |
357 | 386 | if set(self.restriction_attributes) <= set(child.primary_key) and \ |
358 | | - match.group('fk_attrs') == match.group('pk_attrs'): |
| 387 | + match['fk_attrs'] == match['pk_attrs']: |
359 | 388 | 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))) |
| 389 | + elif match['fk_attrs'] != match['pk_attrs']: |
| 390 | + child &= self.proj(**dict(zip(match['fk_attrs'], |
| 391 | + match['pk_attrs']))) |
364 | 392 | else: |
365 | 393 | child &= self.proj() |
366 | 394 | delete_count += child._delete_cascade() |
|
0 commit comments