94 lines
2.6 KiB
Python
94 lines
2.6 KiB
Python
import io, re
|
||
|
||
BS = chr(92)
|
||
|
||
# Как MySQL экранирует спецсимволы в дампе. Всё, чего здесь нет (\' \" \\ и т.п.),
|
||
# подставляется как есть — снимается только сам слэш.
|
||
ESCAPES = {
|
||
'n': '\n',
|
||
'r': '\r',
|
||
't': '\t',
|
||
'0': '\0',
|
||
'b': '\b',
|
||
'Z': chr(26),
|
||
}
|
||
|
||
|
||
def columns(path, table):
|
||
cols, inside = [], False
|
||
for line in io.open(path, encoding='utf-8', errors='replace'):
|
||
if line.startswith('CREATE TABLE `%s`' % table):
|
||
inside = True
|
||
continue
|
||
if inside:
|
||
m = re.match(r'\s+`([^`]+)`', line)
|
||
if m:
|
||
cols.append(m.group(1))
|
||
elif line.startswith(')'):
|
||
break
|
||
return cols
|
||
|
||
|
||
def _scan(s, start=0):
|
||
"""Scan one (...) row starting at s[start]=='('. Returns (fields, next_index)."""
|
||
assert s[start] == '('
|
||
i = start + 1
|
||
fields, buf, q = [], [], False
|
||
while i < len(s):
|
||
c = s[i]
|
||
if q:
|
||
if c == BS:
|
||
# Escape-последовательности MySQL нужно раскодировать, а не просто
|
||
# снимать слэш: иначе \r\n превращался в буквы "rn" прямо в тексте.
|
||
nxt = s[i + 1] if i + 1 < len(s) else ''
|
||
buf.append(ESCAPES.get(nxt, nxt))
|
||
i += 2
|
||
continue
|
||
if c == "'":
|
||
q = False
|
||
i += 1
|
||
continue
|
||
buf.append(c)
|
||
i += 1
|
||
continue
|
||
if c == "'":
|
||
q = True
|
||
i += 1
|
||
continue
|
||
if c == ',':
|
||
fields.append(''.join(buf))
|
||
buf = []
|
||
i += 1
|
||
continue
|
||
if c == ')':
|
||
fields.append(''.join(buf))
|
||
return fields, i + 1
|
||
buf.append(c)
|
||
i += 1
|
||
return None, i
|
||
|
||
|
||
def rows(path, table):
|
||
cols = columns(path, table)
|
||
text = []
|
||
cur = False
|
||
for line in io.open(path, encoding='utf-8', errors='replace'):
|
||
if re.match(r'INSERT INTO `%s` VALUES' % re.escape(table), line):
|
||
cur = True
|
||
text.append(line.split('VALUES', 1)[1])
|
||
continue
|
||
if cur:
|
||
text.append(line)
|
||
if line.rstrip().endswith(';'):
|
||
cur = False
|
||
s = ''.join(text)
|
||
i = 0
|
||
while i < len(s):
|
||
if s[i] == '(':
|
||
fields, i = _scan(s, i)
|
||
if fields is None:
|
||
break
|
||
yield dict(zip(cols, [f.strip() for f in fields]))
|
||
else:
|
||
i += 1
|