Source code for dbtk.dialects.mysql
# dbtk/dialects/mysql.py
from textwrap import dedent
from typing import List, Tuple
from .base import DatabaseDialect
from ..utils import wrap_at_comma
[docs]
class MySQLDialect(DatabaseDialect):
"""MySQL/MariaDB dialect. Uses INSERT … ON DUPLICATE KEY UPDATE."""
[docs]
def upsert_sql(self, table_name: str, cols_str: str, placeholders_str: str,
key_cols: List[str], update_cols: List[Tuple]) -> str:
# MySQL/MariaDB: VALUES(col) syntax works on both MySQL and MariaDB
update_assignments = []
for col, ident, bind_name, db_expr in update_cols:
if db_expr and '#' in db_expr:
assignment = f"{ident} = {db_expr.replace('#', f'VALUES({ident})')}"
elif db_expr:
assignment = f"{ident} = {db_expr}"
else:
assignment = f"{ident} = VALUES({ident})"
update_assignments.append(assignment)
update_clause = ', '.join(update_assignments)
if len(update_assignments) > 4:
update_clause = wrap_at_comma(update_clause)
return dedent(f"""\
INSERT INTO {table_name} ({cols_str})
VALUES ({placeholders_str})
ON DUPLICATE KEY UPDATE {update_clause}""")
[docs]
def table_metadata(self, cursor, table_name: str, add_comments: bool) -> dict:
table_comment = None
if add_comments:
cmt_query = '''
SELECT table_comment
FROM information_schema.tables
WHERE table_name = %s AND table_schema = DATABASE()
'''
cursor.execute(cmt_query, (table_name,))
row = cursor.fetchone()
if row and row[0]:
table_comment = row[0]
col_query = '''
SELECT
c.column_name,
c.data_type,
c.column_type,
c.is_nullable,
CASE WHEN c.column_key = 'PRI' THEN 'Y' ELSE 'N' END as key_column,
COALESCE(c.column_comment, '') as comments
FROM information_schema.columns c
WHERE c.table_name = %s
AND c.table_schema = DATABASE()
ORDER BY c.ordinal_position
'''
cursor.execute(col_query, (table_name,))
columns = {}
column_comments = {}
for row in cursor:
col_name, data_type, column_type, is_nullable, is_key, comment = row
if add_comments and comment:
column_comments[col_name] = comment
if col_name in ('created_at', 'updated_at') and data_type in ('datetime', 'timestamp'):
columns[col_name] = {'db_expr': 'CURRENT_TIMESTAMP'}
continue
col_config = {'field': col_name}
if data_type == 'date':
col_config['fn'] = 'date'
elif data_type in ('datetime', 'timestamp'):
col_config['fn'] = 'datetime'
elif data_type == 'time':
col_config['fn'] = 'time'
elif data_type == 'tinyint' and column_type == 'tinyint(1)':
col_config['fn'] = 'bool'
elif data_type in ('tinyint', 'smallint', 'mediumint', 'int', 'bigint'):
col_config['fn'] = 'int'
elif data_type in ('decimal', 'numeric', 'float', 'double'):
col_config['fn'] = 'float'
if is_key == 'Y':
col_config['primary_key'] = True
elif is_nullable == 'NO':
col_config['nullable'] = False
columns[col_name] = col_config
return {
'name': table_name,
'columns': columns,
'table_comment': table_comment,
'column_comments': column_comments,
}