Загрузить файлы в «venv/Lib/site-packages/sqlalchemy/dialects/mysql»
This commit is contained in:
158
venv/Lib/site-packages/sqlalchemy/dialects/mysql/pyodbc.py
Normal file
158
venv/Lib/site-packages/sqlalchemy/dialects/mysql/pyodbc.py
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
# dialects/mysql/pyodbc.py
|
||||||
|
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
||||||
|
# <see AUTHORS file>
|
||||||
|
#
|
||||||
|
# This module is part of SQLAlchemy and is released under
|
||||||
|
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||||
|
|
||||||
|
|
||||||
|
r"""
|
||||||
|
|
||||||
|
.. dialect:: mysql+pyodbc
|
||||||
|
:name: PyODBC
|
||||||
|
:dbapi: pyodbc
|
||||||
|
:connectstring: mysql+pyodbc://<username>:<password>@<dsnname>
|
||||||
|
:url: https://pypi.org/project/pyodbc/
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The PyODBC for MySQL dialect is **not tested as part of
|
||||||
|
SQLAlchemy's continuous integration**.
|
||||||
|
The recommended MySQL dialects are mysqlclient and PyMySQL.
|
||||||
|
However, if you want to use the mysql+pyodbc dialect and require
|
||||||
|
full support for ``utf8mb4`` characters (including supplementary
|
||||||
|
characters like emoji) be sure to use a current release of
|
||||||
|
MySQL Connector/ODBC and specify the "ANSI" (**not** "Unicode")
|
||||||
|
version of the driver in your DSN or connection string.
|
||||||
|
|
||||||
|
Pass through exact pyodbc connection string::
|
||||||
|
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
connection_string = (
|
||||||
|
"DRIVER=MySQL ODBC 8.0 ANSI Driver;"
|
||||||
|
"SERVER=localhost;"
|
||||||
|
"PORT=3307;"
|
||||||
|
"DATABASE=mydb;"
|
||||||
|
"UID=root;"
|
||||||
|
"PWD=(whatever);"
|
||||||
|
"charset=utf8mb4;"
|
||||||
|
)
|
||||||
|
params = urllib.parse.quote_plus(connection_string)
|
||||||
|
connection_uri = "mysql+pyodbc:///?odbc_connect=%s" % params
|
||||||
|
|
||||||
|
""" # noqa
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import re
|
||||||
|
from typing import Any
|
||||||
|
from typing import Callable
|
||||||
|
from typing import Optional
|
||||||
|
from typing import Tuple
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from .base import MySQLDialect
|
||||||
|
from .base import MySQLExecutionContext
|
||||||
|
from .types import TIME
|
||||||
|
from ... import exc
|
||||||
|
from ... import util
|
||||||
|
from ...connectors.pyodbc import PyODBCConnector
|
||||||
|
from ...sql.sqltypes import Time
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from ...engine import Connection
|
||||||
|
from ...engine.interfaces import DBAPIConnection
|
||||||
|
from ...engine.interfaces import Dialect
|
||||||
|
from ...sql.type_api import _ResultProcessorType
|
||||||
|
|
||||||
|
|
||||||
|
class _pyodbcTIME(TIME):
|
||||||
|
def result_processor(
|
||||||
|
self, dialect: Dialect, coltype: object
|
||||||
|
) -> _ResultProcessorType[datetime.time]:
|
||||||
|
def process(value: Any) -> Union[datetime.time, None]:
|
||||||
|
# pyodbc returns a datetime.time object; no need to convert
|
||||||
|
return value # type: ignore[no-any-return]
|
||||||
|
|
||||||
|
return process
|
||||||
|
|
||||||
|
|
||||||
|
class MySQLExecutionContext_pyodbc(MySQLExecutionContext):
|
||||||
|
def get_lastrowid(self) -> int:
|
||||||
|
cursor = self.create_cursor()
|
||||||
|
cursor.execute("SELECT LAST_INSERT_ID()")
|
||||||
|
lastrowid = cursor.fetchone()[0] # type: ignore[index]
|
||||||
|
cursor.close()
|
||||||
|
return lastrowid # type: ignore[no-any-return]
|
||||||
|
|
||||||
|
|
||||||
|
class MySQLDialect_pyodbc(PyODBCConnector, MySQLDialect):
|
||||||
|
supports_statement_cache = True
|
||||||
|
colspecs = util.update_copy(MySQLDialect.colspecs, {Time: _pyodbcTIME})
|
||||||
|
supports_unicode_statements = True
|
||||||
|
execution_ctx_cls = MySQLExecutionContext_pyodbc
|
||||||
|
|
||||||
|
pyodbc_driver_name = "MySQL"
|
||||||
|
|
||||||
|
def _detect_charset(self, connection: Connection) -> str:
|
||||||
|
"""Sniff out the character set in use for connection results."""
|
||||||
|
|
||||||
|
# Prefer 'character_set_results' for the current connection over the
|
||||||
|
# value in the driver. SET NAMES or individual variable SETs will
|
||||||
|
# change the charset without updating the driver's view of the world.
|
||||||
|
#
|
||||||
|
# If it's decided that issuing that sort of SQL leaves you SOL, then
|
||||||
|
# this can prefer the driver value.
|
||||||
|
|
||||||
|
# set this to None as _fetch_setting attempts to use it (None is OK)
|
||||||
|
self._connection_charset = None
|
||||||
|
try:
|
||||||
|
value = self._fetch_setting(connection, "character_set_client")
|
||||||
|
if value:
|
||||||
|
return value
|
||||||
|
except exc.DBAPIError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
util.warn(
|
||||||
|
"Could not detect the connection character set. "
|
||||||
|
"Assuming latin1."
|
||||||
|
)
|
||||||
|
return "latin1"
|
||||||
|
|
||||||
|
def _get_server_version_info(
|
||||||
|
self, connection: Connection
|
||||||
|
) -> Tuple[int, ...]:
|
||||||
|
return MySQLDialect._get_server_version_info(self, connection)
|
||||||
|
|
||||||
|
def _extract_error_code(self, exception: BaseException) -> Optional[int]:
|
||||||
|
m = re.compile(r"\((\d+)\)").search(str(exception.args))
|
||||||
|
if m is None:
|
||||||
|
return None
|
||||||
|
c: Optional[str] = m.group(1)
|
||||||
|
if c:
|
||||||
|
return int(c)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def on_connect(self) -> Callable[[DBAPIConnection], None]:
|
||||||
|
super_ = super().on_connect()
|
||||||
|
|
||||||
|
def on_connect(conn: DBAPIConnection) -> None:
|
||||||
|
if super_ is not None:
|
||||||
|
super_(conn)
|
||||||
|
|
||||||
|
# declare Unicode encoding for pyodbc as per
|
||||||
|
# https://github.com/mkleehammer/pyodbc/wiki/Unicode
|
||||||
|
pyodbc_SQL_CHAR = 1 # pyodbc.SQL_CHAR
|
||||||
|
pyodbc_SQL_WCHAR = -8 # pyodbc.SQL_WCHAR
|
||||||
|
conn.setdecoding(pyodbc_SQL_CHAR, encoding="utf-8")
|
||||||
|
conn.setdecoding(pyodbc_SQL_WCHAR, encoding="utf-8")
|
||||||
|
conn.setencoding(encoding="utf-8")
|
||||||
|
|
||||||
|
return on_connect
|
||||||
|
|
||||||
|
|
||||||
|
dialect = MySQLDialect_pyodbc
|
||||||
727
venv/Lib/site-packages/sqlalchemy/dialects/mysql/reflection.py
Normal file
727
venv/Lib/site-packages/sqlalchemy/dialects/mysql/reflection.py
Normal file
@@ -0,0 +1,727 @@
|
|||||||
|
# dialects/mysql/reflection.py
|
||||||
|
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
||||||
|
# <see AUTHORS file>
|
||||||
|
#
|
||||||
|
# This module is part of SQLAlchemy and is released under
|
||||||
|
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
from typing import Any
|
||||||
|
from typing import Callable
|
||||||
|
from typing import Dict
|
||||||
|
from typing import List
|
||||||
|
from typing import Optional
|
||||||
|
from typing import overload
|
||||||
|
from typing import Sequence
|
||||||
|
from typing import Tuple
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from .enumerated import ENUM
|
||||||
|
from .enumerated import SET
|
||||||
|
from .types import DATETIME
|
||||||
|
from .types import TIME
|
||||||
|
from .types import TIMESTAMP
|
||||||
|
from ... import types as sqltypes
|
||||||
|
from ... import util
|
||||||
|
from ...util.typing import Literal
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .base import MySQLDialect
|
||||||
|
from .base import MySQLIdentifierPreparer
|
||||||
|
from ...engine.interfaces import ReflectedColumn
|
||||||
|
|
||||||
|
|
||||||
|
class ReflectedState:
|
||||||
|
"""Stores raw information about a SHOW CREATE TABLE statement."""
|
||||||
|
|
||||||
|
charset: Optional[str]
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.columns: List[ReflectedColumn] = []
|
||||||
|
self.table_options: Dict[str, str] = {}
|
||||||
|
self.table_name: Optional[str] = None
|
||||||
|
self.keys: List[Dict[str, Any]] = []
|
||||||
|
self.fk_constraints: List[Dict[str, Any]] = []
|
||||||
|
self.ck_constraints: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
|
|
||||||
|
class MySQLTableDefinitionParser:
|
||||||
|
"""Parses the results of a SHOW CREATE TABLE statement."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, dialect: MySQLDialect, preparer: MySQLIdentifierPreparer
|
||||||
|
):
|
||||||
|
self.dialect = dialect
|
||||||
|
self.preparer = preparer
|
||||||
|
self._prep_regexes()
|
||||||
|
|
||||||
|
def parse(
|
||||||
|
self, show_create: str, charset: Optional[str]
|
||||||
|
) -> ReflectedState:
|
||||||
|
state = ReflectedState()
|
||||||
|
state.charset = charset
|
||||||
|
for line in re.split(r"\r?\n", show_create):
|
||||||
|
if line.startswith(" " + self.preparer.initial_quote):
|
||||||
|
self._parse_column(line, state)
|
||||||
|
# a regular table options line
|
||||||
|
elif line.startswith(") "):
|
||||||
|
self._parse_table_options(line, state)
|
||||||
|
# an ANSI-mode table options line
|
||||||
|
elif line == ")":
|
||||||
|
pass
|
||||||
|
elif line.startswith("CREATE "):
|
||||||
|
self._parse_table_name(line, state)
|
||||||
|
elif "PARTITION" in line:
|
||||||
|
self._parse_partition_options(line, state)
|
||||||
|
# Not present in real reflection, but may be if
|
||||||
|
# loading from a file.
|
||||||
|
elif not line:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
type_, spec = self._parse_constraints(line)
|
||||||
|
if type_ is None:
|
||||||
|
util.warn("Unknown schema content: %r" % line)
|
||||||
|
elif type_ == "key":
|
||||||
|
state.keys.append(spec) # type: ignore[arg-type]
|
||||||
|
elif type_ == "fk_constraint":
|
||||||
|
state.fk_constraints.append(spec) # type: ignore[arg-type]
|
||||||
|
elif type_ == "ck_constraint":
|
||||||
|
state.ck_constraints.append(spec) # type: ignore[arg-type]
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
return state
|
||||||
|
|
||||||
|
def _check_view(self, sql: str) -> bool:
|
||||||
|
return bool(self._re_is_view.match(sql))
|
||||||
|
|
||||||
|
def _parse_constraints(self, line: str) -> Union[
|
||||||
|
Tuple[None, str],
|
||||||
|
Tuple[Literal["partition"], str],
|
||||||
|
Tuple[
|
||||||
|
Literal["ck_constraint", "fk_constraint", "key"], Dict[str, str]
|
||||||
|
],
|
||||||
|
]:
|
||||||
|
"""Parse a KEY or CONSTRAINT line.
|
||||||
|
|
||||||
|
:param line: A line of SHOW CREATE TABLE output
|
||||||
|
"""
|
||||||
|
|
||||||
|
# KEY
|
||||||
|
m = self._re_key.match(line)
|
||||||
|
if m:
|
||||||
|
spec = m.groupdict()
|
||||||
|
# convert columns into name, length pairs
|
||||||
|
# NOTE: we may want to consider SHOW INDEX as the
|
||||||
|
# format of indexes in MySQL becomes more complex
|
||||||
|
spec["columns"] = self._parse_keyexprs(spec["columns"])
|
||||||
|
if spec["version_sql"]:
|
||||||
|
m2 = self._re_key_version_sql.match(spec["version_sql"])
|
||||||
|
if m2 and m2.groupdict()["parser"]:
|
||||||
|
spec["parser"] = m2.groupdict()["parser"]
|
||||||
|
if spec["parser"]:
|
||||||
|
spec["parser"] = self.preparer.unformat_identifiers(
|
||||||
|
spec["parser"]
|
||||||
|
)[0]
|
||||||
|
return "key", spec
|
||||||
|
|
||||||
|
# FOREIGN KEY CONSTRAINT
|
||||||
|
m = self._re_fk_constraint.match(line)
|
||||||
|
if m:
|
||||||
|
spec = m.groupdict()
|
||||||
|
spec["table"] = self.preparer.unformat_identifiers(spec["table"])
|
||||||
|
spec["local"] = [c[0] for c in self._parse_keyexprs(spec["local"])]
|
||||||
|
spec["foreign"] = [
|
||||||
|
c[0] for c in self._parse_keyexprs(spec["foreign"])
|
||||||
|
]
|
||||||
|
return "fk_constraint", spec
|
||||||
|
|
||||||
|
# CHECK constraint
|
||||||
|
m = self._re_ck_constraint.match(line)
|
||||||
|
if m:
|
||||||
|
spec = m.groupdict()
|
||||||
|
return "ck_constraint", spec
|
||||||
|
|
||||||
|
# PARTITION and SUBPARTITION
|
||||||
|
m = self._re_partition.match(line)
|
||||||
|
if m:
|
||||||
|
# Punt!
|
||||||
|
return "partition", line
|
||||||
|
|
||||||
|
# No match.
|
||||||
|
return (None, line)
|
||||||
|
|
||||||
|
def _parse_table_name(self, line: str, state: ReflectedState) -> None:
|
||||||
|
"""Extract the table name.
|
||||||
|
|
||||||
|
:param line: The first line of SHOW CREATE TABLE
|
||||||
|
"""
|
||||||
|
|
||||||
|
regex, cleanup = self._pr_name
|
||||||
|
m = regex.match(line)
|
||||||
|
if m:
|
||||||
|
state.table_name = cleanup(m.group("name"))
|
||||||
|
|
||||||
|
def _parse_table_options(self, line: str, state: ReflectedState) -> None:
|
||||||
|
"""Build a dictionary of all reflected table-level options.
|
||||||
|
|
||||||
|
:param line: The final line of SHOW CREATE TABLE output.
|
||||||
|
"""
|
||||||
|
|
||||||
|
options = {}
|
||||||
|
|
||||||
|
if line and line != ")":
|
||||||
|
rest_of_line = line
|
||||||
|
for regex, cleanup in self._pr_options:
|
||||||
|
m = regex.search(rest_of_line)
|
||||||
|
if not m:
|
||||||
|
continue
|
||||||
|
directive, value = m.group("directive"), m.group("val")
|
||||||
|
if cleanup:
|
||||||
|
value = cleanup(value)
|
||||||
|
options[directive.lower()] = value
|
||||||
|
rest_of_line = regex.sub("", rest_of_line)
|
||||||
|
|
||||||
|
for nope in ("auto_increment", "data directory", "index directory"):
|
||||||
|
options.pop(nope, None)
|
||||||
|
|
||||||
|
for opt, val in options.items():
|
||||||
|
state.table_options["%s_%s" % (self.dialect.name, opt)] = val
|
||||||
|
|
||||||
|
def _parse_partition_options(
|
||||||
|
self, line: str, state: ReflectedState
|
||||||
|
) -> None:
|
||||||
|
options = {}
|
||||||
|
new_line = line[:]
|
||||||
|
|
||||||
|
while new_line.startswith("(") or new_line.startswith(" "):
|
||||||
|
new_line = new_line[1:]
|
||||||
|
|
||||||
|
for regex, cleanup in self._pr_options:
|
||||||
|
m = regex.search(new_line)
|
||||||
|
if not m or "PARTITION" not in regex.pattern:
|
||||||
|
continue
|
||||||
|
|
||||||
|
directive = m.group("directive")
|
||||||
|
directive = directive.lower()
|
||||||
|
is_subpartition = directive == "subpartition"
|
||||||
|
|
||||||
|
if directive == "partition" or is_subpartition:
|
||||||
|
new_line = new_line.replace(") */", "")
|
||||||
|
new_line = new_line.replace(",", "")
|
||||||
|
if is_subpartition and new_line.endswith(")"):
|
||||||
|
new_line = new_line[:-1]
|
||||||
|
if self.dialect.name == "mariadb" and new_line.endswith(")"):
|
||||||
|
if (
|
||||||
|
"MAXVALUE" in new_line
|
||||||
|
or "MINVALUE" in new_line
|
||||||
|
or "ENGINE" in new_line
|
||||||
|
):
|
||||||
|
# final line of MariaDB partition endswith ")"
|
||||||
|
new_line = new_line[:-1]
|
||||||
|
|
||||||
|
defs = "%s_%s_definitions" % (self.dialect.name, directive)
|
||||||
|
options[defs] = new_line
|
||||||
|
|
||||||
|
else:
|
||||||
|
directive = directive.replace(" ", "_")
|
||||||
|
value = m.group("val")
|
||||||
|
if cleanup:
|
||||||
|
value = cleanup(value)
|
||||||
|
options[directive] = value
|
||||||
|
break
|
||||||
|
|
||||||
|
for opt, val in options.items():
|
||||||
|
part_def = "%s_partition_definitions" % (self.dialect.name)
|
||||||
|
subpart_def = "%s_subpartition_definitions" % (self.dialect.name)
|
||||||
|
if opt == part_def or opt == subpart_def:
|
||||||
|
# builds a string of definitions
|
||||||
|
if opt not in state.table_options:
|
||||||
|
state.table_options[opt] = val
|
||||||
|
else:
|
||||||
|
state.table_options[opt] = "%s, %s" % (
|
||||||
|
state.table_options[opt],
|
||||||
|
val,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
state.table_options["%s_%s" % (self.dialect.name, opt)] = val
|
||||||
|
|
||||||
|
def _parse_column(self, line: str, state: ReflectedState) -> None:
|
||||||
|
"""Extract column details.
|
||||||
|
|
||||||
|
Falls back to a 'minimal support' variant if full parse fails.
|
||||||
|
|
||||||
|
:param line: Any column-bearing line from SHOW CREATE TABLE
|
||||||
|
"""
|
||||||
|
|
||||||
|
spec = None
|
||||||
|
m = self._re_column.match(line)
|
||||||
|
if m:
|
||||||
|
spec = m.groupdict()
|
||||||
|
spec["full"] = True
|
||||||
|
else:
|
||||||
|
m = self._re_column_loose.match(line)
|
||||||
|
if m:
|
||||||
|
spec = m.groupdict()
|
||||||
|
spec["full"] = False
|
||||||
|
if not spec:
|
||||||
|
util.warn("Unknown column definition %r" % line)
|
||||||
|
return
|
||||||
|
if not spec["full"]:
|
||||||
|
util.warn("Incomplete reflection of column definition %r" % line)
|
||||||
|
|
||||||
|
name, type_, args = spec["name"], spec["coltype"], spec["arg"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
col_type = self.dialect.ischema_names[type_]
|
||||||
|
except KeyError:
|
||||||
|
util.warn(
|
||||||
|
"Did not recognize type '%s' of column '%s'" % (type_, name)
|
||||||
|
)
|
||||||
|
col_type = sqltypes.NullType
|
||||||
|
|
||||||
|
# Column type positional arguments eg. varchar(32)
|
||||||
|
if args is None or args == "":
|
||||||
|
type_args = []
|
||||||
|
elif args[0] == "'" and args[-1] == "'":
|
||||||
|
type_args = self._re_csv_str.findall(args)
|
||||||
|
else:
|
||||||
|
type_args = [int(v) for v in self._re_csv_int.findall(args)]
|
||||||
|
|
||||||
|
# Column type keyword options
|
||||||
|
type_kw = {}
|
||||||
|
|
||||||
|
if issubclass(col_type, (DATETIME, TIME, TIMESTAMP)):
|
||||||
|
if type_args:
|
||||||
|
type_kw["fsp"] = type_args.pop(0)
|
||||||
|
|
||||||
|
for kw in ("unsigned", "zerofill"):
|
||||||
|
if spec.get(kw, False):
|
||||||
|
type_kw[kw] = True
|
||||||
|
for kw in ("charset", "collate"):
|
||||||
|
if spec.get(kw, False):
|
||||||
|
type_kw[kw] = spec[kw]
|
||||||
|
if issubclass(col_type, (ENUM, SET)):
|
||||||
|
type_args = _strip_values(type_args)
|
||||||
|
|
||||||
|
if issubclass(col_type, SET) and "" in type_args:
|
||||||
|
type_kw["retrieve_as_bitwise"] = True
|
||||||
|
|
||||||
|
type_instance = col_type(*type_args, **type_kw)
|
||||||
|
|
||||||
|
col_kw: Dict[str, Any] = {}
|
||||||
|
|
||||||
|
# NOT NULL
|
||||||
|
col_kw["nullable"] = True
|
||||||
|
# this can be "NULL" in the case of TIMESTAMP
|
||||||
|
if spec.get("notnull", False) == "NOT NULL":
|
||||||
|
col_kw["nullable"] = False
|
||||||
|
# For generated columns, the nullability is marked in a different place
|
||||||
|
if spec.get("notnull_generated", False) == "NOT NULL":
|
||||||
|
col_kw["nullable"] = False
|
||||||
|
|
||||||
|
# AUTO_INCREMENT
|
||||||
|
if spec.get("autoincr", False):
|
||||||
|
col_kw["autoincrement"] = True
|
||||||
|
elif issubclass(col_type, sqltypes.Integer):
|
||||||
|
col_kw["autoincrement"] = False
|
||||||
|
|
||||||
|
# DEFAULT
|
||||||
|
default = spec.get("default", None)
|
||||||
|
|
||||||
|
if default == "NULL":
|
||||||
|
# eliminates the need to deal with this later.
|
||||||
|
default = None
|
||||||
|
|
||||||
|
comment = spec.get("comment", None)
|
||||||
|
|
||||||
|
if comment is not None:
|
||||||
|
comment = cleanup_text(comment)
|
||||||
|
|
||||||
|
sqltext = spec.get("generated")
|
||||||
|
if sqltext is not None:
|
||||||
|
computed = dict(sqltext=sqltext)
|
||||||
|
persisted = spec.get("persistence")
|
||||||
|
if persisted is not None:
|
||||||
|
computed["persisted"] = persisted == "STORED"
|
||||||
|
col_kw["computed"] = computed
|
||||||
|
|
||||||
|
col_d = dict(
|
||||||
|
name=name, type=type_instance, default=default, comment=comment
|
||||||
|
)
|
||||||
|
col_d.update(col_kw)
|
||||||
|
state.columns.append(col_d) # type: ignore[arg-type]
|
||||||
|
|
||||||
|
def _describe_to_create(
|
||||||
|
self,
|
||||||
|
table_name: str,
|
||||||
|
columns: Sequence[Tuple[str, str, str, str, str, str]],
|
||||||
|
) -> str:
|
||||||
|
"""Re-format DESCRIBE output as a SHOW CREATE TABLE string.
|
||||||
|
|
||||||
|
DESCRIBE is a much simpler reflection and is sufficient for
|
||||||
|
reflecting views for runtime use. This method formats DDL
|
||||||
|
for columns only- keys are omitted.
|
||||||
|
|
||||||
|
:param columns: A sequence of DESCRIBE or SHOW COLUMNS 6-tuples.
|
||||||
|
SHOW FULL COLUMNS FROM rows must be rearranged for use with
|
||||||
|
this function.
|
||||||
|
"""
|
||||||
|
|
||||||
|
buffer = []
|
||||||
|
for row in columns:
|
||||||
|
name, col_type, nullable, default, extra = (
|
||||||
|
row[i] for i in (0, 1, 2, 4, 5)
|
||||||
|
)
|
||||||
|
|
||||||
|
line = [" "]
|
||||||
|
line.append(self.preparer.quote_identifier(name))
|
||||||
|
line.append(col_type)
|
||||||
|
if not nullable:
|
||||||
|
line.append("NOT NULL")
|
||||||
|
if default:
|
||||||
|
if "auto_increment" in default:
|
||||||
|
pass
|
||||||
|
elif col_type.startswith("timestamp") and default.startswith(
|
||||||
|
"C"
|
||||||
|
):
|
||||||
|
line.append("DEFAULT")
|
||||||
|
line.append(default)
|
||||||
|
elif default == "NULL":
|
||||||
|
line.append("DEFAULT")
|
||||||
|
line.append(default)
|
||||||
|
else:
|
||||||
|
line.append("DEFAULT")
|
||||||
|
line.append("'%s'" % default.replace("'", "''"))
|
||||||
|
if extra:
|
||||||
|
line.append(extra)
|
||||||
|
|
||||||
|
buffer.append(" ".join(line))
|
||||||
|
|
||||||
|
return "".join(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"CREATE TABLE %s (\n"
|
||||||
|
% self.preparer.quote_identifier(table_name)
|
||||||
|
),
|
||||||
|
",\n".join(buffer),
|
||||||
|
"\n) ",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def _parse_keyexprs(
|
||||||
|
self, identifiers: str
|
||||||
|
) -> List[Tuple[str, Optional[int], str]]:
|
||||||
|
"""Unpack '"col"(2),"col" ASC'-ish strings into components."""
|
||||||
|
|
||||||
|
return [
|
||||||
|
(colname, int(length) if length else None, modifiers)
|
||||||
|
for colname, length, modifiers in self._re_keyexprs.findall(
|
||||||
|
identifiers
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
def _prep_regexes(self) -> None:
|
||||||
|
"""Pre-compile regular expressions."""
|
||||||
|
|
||||||
|
self._pr_options: List[
|
||||||
|
Tuple[re.Pattern[Any], Optional[Callable[[str], str]]]
|
||||||
|
] = []
|
||||||
|
|
||||||
|
_final = self.preparer.final_quote
|
||||||
|
|
||||||
|
quotes = dict(
|
||||||
|
zip(
|
||||||
|
("iq", "fq", "esc_fq"),
|
||||||
|
[
|
||||||
|
re.escape(s)
|
||||||
|
for s in (
|
||||||
|
self.preparer.initial_quote,
|
||||||
|
_final,
|
||||||
|
self.preparer._escape_identifier(_final),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self._pr_name = _pr_compile(
|
||||||
|
r"^CREATE (?:\w+ +)?TABLE +"
|
||||||
|
r"%(iq)s(?P<name>(?:%(esc_fq)s|[^%(fq)s])+)%(fq)s +\($" % quotes,
|
||||||
|
self.preparer._unescape_identifier,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._re_is_view = _re_compile(r"^CREATE(?! TABLE)(\s.*)?\sVIEW")
|
||||||
|
|
||||||
|
# `col`,`col2`(32),`col3`(15) DESC
|
||||||
|
#
|
||||||
|
self._re_keyexprs = _re_compile(
|
||||||
|
r"(?:"
|
||||||
|
r"(?:%(iq)s((?:%(esc_fq)s|[^%(fq)s])+)%(fq)s)"
|
||||||
|
r"(?:\((\d+)\))?(?: +(ASC|DESC))?(?=\,|$))+" % quotes
|
||||||
|
)
|
||||||
|
|
||||||
|
# 'foo' or 'foo','bar' or 'fo,o','ba''a''r'
|
||||||
|
self._re_csv_str = _re_compile(r"\x27(?:\x27\x27|[^\x27])*\x27")
|
||||||
|
|
||||||
|
# 123 or 123,456
|
||||||
|
self._re_csv_int = _re_compile(r"\d+")
|
||||||
|
|
||||||
|
# `colname` <type> [type opts]
|
||||||
|
# (NOT NULL | NULL)
|
||||||
|
# DEFAULT ('value' | CURRENT_TIMESTAMP...)
|
||||||
|
# COMMENT 'comment'
|
||||||
|
# COLUMN_FORMAT (FIXED|DYNAMIC|DEFAULT)
|
||||||
|
# STORAGE (DISK|MEMORY)
|
||||||
|
self._re_column = _re_compile(
|
||||||
|
r" "
|
||||||
|
r"%(iq)s(?P<name>(?:%(esc_fq)s|[^%(fq)s])+)%(fq)s +"
|
||||||
|
r"(?P<coltype>\w+)"
|
||||||
|
r"(?:\((?P<arg>(?:\d+|\d+,\d+|"
|
||||||
|
r"(?:'(?:''|[^'])*',?)+))\))?"
|
||||||
|
r"(?: +(?P<unsigned>UNSIGNED))?"
|
||||||
|
r"(?: +(?P<zerofill>ZEROFILL))?"
|
||||||
|
r"(?: +CHARACTER SET +(?P<charset>[\w_]+))?"
|
||||||
|
r"(?: +COLLATE +(?P<collate>[\w_]+))?"
|
||||||
|
r"(?: +(?P<notnull>(?:NOT )?NULL))?"
|
||||||
|
r"(?: +DEFAULT +(?P<default>"
|
||||||
|
r"(?:NULL|'(?:''|[^'])*'|\(.+?\)|[\-\w\.\(\)]+"
|
||||||
|
r"(?: +ON UPDATE [\-\w\.\(\)]+)?)"
|
||||||
|
r"))?"
|
||||||
|
r"(?: +(?:GENERATED ALWAYS)? ?AS +(?P<generated>\("
|
||||||
|
r".*\))? ?(?P<persistence>VIRTUAL|STORED)?"
|
||||||
|
r"(?: +(?P<notnull_generated>(?:NOT )?NULL))?"
|
||||||
|
r")?"
|
||||||
|
r"(?: +(?P<autoincr>AUTO_INCREMENT))?"
|
||||||
|
r"(?: +COMMENT +'(?P<comment>(?:''|[^'])*)')?"
|
||||||
|
r"(?: +COLUMN_FORMAT +(?P<colfmt>\w+))?"
|
||||||
|
r"(?: +STORAGE +(?P<storage>\w+))?"
|
||||||
|
r"(?: +(?P<extra>.*))?"
|
||||||
|
r",?$" % quotes
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fallback, try to parse as little as possible
|
||||||
|
self._re_column_loose = _re_compile(
|
||||||
|
r" "
|
||||||
|
r"%(iq)s(?P<name>(?:%(esc_fq)s|[^%(fq)s])+)%(fq)s +"
|
||||||
|
r"(?P<coltype>\w+)"
|
||||||
|
r"(?:\((?P<arg>(?:\d+|\d+,\d+|\x27(?:\x27\x27|[^\x27])+\x27))\))?"
|
||||||
|
r".*?(?P<notnull>(?:NOT )NULL)?" % quotes
|
||||||
|
)
|
||||||
|
|
||||||
|
# (PRIMARY|UNIQUE|FULLTEXT|SPATIAL) INDEX `name` (USING (BTREE|HASH))?
|
||||||
|
# (`col` (ASC|DESC)?, `col` (ASC|DESC)?)
|
||||||
|
# KEY_BLOCK_SIZE size | WITH PARSER name /*!50100 WITH PARSER name */
|
||||||
|
self._re_key = _re_compile(
|
||||||
|
r" "
|
||||||
|
r"(?:(?P<type>\S+) )?KEY"
|
||||||
|
r"(?: +%(iq)s(?P<name>(?:%(esc_fq)s|[^%(fq)s])+)%(fq)s)?"
|
||||||
|
r"(?: +USING +(?P<using_pre>\S+))?"
|
||||||
|
r" +\((?P<columns>.+?)\)"
|
||||||
|
r"(?: +USING +(?P<using_post>\S+))?"
|
||||||
|
r"(?: +KEY_BLOCK_SIZE *[ =]? *(?P<keyblock>\S+))?"
|
||||||
|
r"(?: +WITH PARSER +(?P<parser>\S+))?"
|
||||||
|
r"(?: +COMMENT +(?P<comment>(\x27\x27|\x27([^\x27])*?\x27)+))?"
|
||||||
|
r"(?: +/\*(?P<version_sql>.+)\*/ *)?"
|
||||||
|
r",?$" % quotes
|
||||||
|
)
|
||||||
|
|
||||||
|
# https://forums.mysql.com/read.php?20,567102,567111#msg-567111
|
||||||
|
# It means if the MySQL version >= \d+, execute what's in the comment
|
||||||
|
self._re_key_version_sql = _re_compile(
|
||||||
|
r"\!\d+ " r"(?: *WITH PARSER +(?P<parser>\S+) *)?"
|
||||||
|
)
|
||||||
|
|
||||||
|
# CONSTRAINT `name` FOREIGN KEY (`local_col`)
|
||||||
|
# REFERENCES `remote` (`remote_col`)
|
||||||
|
# MATCH FULL | MATCH PARTIAL | MATCH SIMPLE
|
||||||
|
# ON DELETE CASCADE ON UPDATE RESTRICT
|
||||||
|
#
|
||||||
|
# unique constraints come back as KEYs
|
||||||
|
kw = quotes.copy()
|
||||||
|
kw["on"] = "RESTRICT|CASCADE|SET NULL|NO ACTION|SET DEFAULT"
|
||||||
|
self._re_fk_constraint = _re_compile(
|
||||||
|
r" "
|
||||||
|
r"CONSTRAINT +"
|
||||||
|
r"%(iq)s(?P<name>(?:%(esc_fq)s|[^%(fq)s])+)%(fq)s +"
|
||||||
|
r"FOREIGN KEY +"
|
||||||
|
r"\((?P<local>[^\)]+?)\) REFERENCES +"
|
||||||
|
r"(?P<table>%(iq)s[^%(fq)s]+%(fq)s"
|
||||||
|
r"(?:\.%(iq)s[^%(fq)s]+%(fq)s)?) +"
|
||||||
|
r"\((?P<foreign>(?:%(iq)s[^%(fq)s]+%(fq)s(?: *, *)?)+)\)"
|
||||||
|
r"(?: +(?P<match>MATCH \w+))?"
|
||||||
|
r"(?: +ON DELETE (?P<ondelete>%(on)s))?"
|
||||||
|
r"(?: +ON UPDATE (?P<onupdate>%(on)s))?" % kw
|
||||||
|
)
|
||||||
|
|
||||||
|
# CONSTRAINT `CONSTRAINT_1` CHECK (`x` > 5)'
|
||||||
|
# testing on MariaDB 10.2 shows that the CHECK constraint
|
||||||
|
# is returned on a line by itself, so to match without worrying
|
||||||
|
# about parenthesis in the expression we go to the end of the line
|
||||||
|
self._re_ck_constraint = _re_compile(
|
||||||
|
r" "
|
||||||
|
r"CONSTRAINT +"
|
||||||
|
r"%(iq)s(?P<name>(?:%(esc_fq)s|[^%(fq)s])+)%(fq)s +"
|
||||||
|
r"CHECK +"
|
||||||
|
r"\((?P<sqltext>.+)\),?" % kw
|
||||||
|
)
|
||||||
|
|
||||||
|
# PARTITION
|
||||||
|
#
|
||||||
|
# punt!
|
||||||
|
self._re_partition = _re_compile(r"(?:.*)(?:SUB)?PARTITION(?:.*)")
|
||||||
|
|
||||||
|
# Table-level options (COLLATE, ENGINE, etc.)
|
||||||
|
# Do the string options first, since they have quoted
|
||||||
|
# strings we need to get rid of.
|
||||||
|
for option in _options_of_type_string:
|
||||||
|
self._add_option_string(option)
|
||||||
|
|
||||||
|
for option in (
|
||||||
|
"ENGINE",
|
||||||
|
"TYPE",
|
||||||
|
"AUTO_INCREMENT",
|
||||||
|
"AVG_ROW_LENGTH",
|
||||||
|
"CHARACTER SET",
|
||||||
|
"DEFAULT CHARSET",
|
||||||
|
"CHECKSUM",
|
||||||
|
"COLLATE",
|
||||||
|
"DELAY_KEY_WRITE",
|
||||||
|
"INSERT_METHOD",
|
||||||
|
"MAX_ROWS",
|
||||||
|
"MIN_ROWS",
|
||||||
|
"PACK_KEYS",
|
||||||
|
"ROW_FORMAT",
|
||||||
|
"KEY_BLOCK_SIZE",
|
||||||
|
"STATS_SAMPLE_PAGES",
|
||||||
|
):
|
||||||
|
self._add_option_word(option)
|
||||||
|
|
||||||
|
for option in (
|
||||||
|
"PARTITION BY",
|
||||||
|
"SUBPARTITION BY",
|
||||||
|
"PARTITIONS",
|
||||||
|
"SUBPARTITIONS",
|
||||||
|
"PARTITION",
|
||||||
|
"SUBPARTITION",
|
||||||
|
):
|
||||||
|
self._add_partition_option_word(option)
|
||||||
|
|
||||||
|
self._add_option_regex("UNION", r"\([^\)]+\)")
|
||||||
|
self._add_option_regex("TABLESPACE", r".*? STORAGE DISK")
|
||||||
|
self._add_option_regex(
|
||||||
|
"RAID_TYPE",
|
||||||
|
r"\w+\s+RAID_CHUNKS\s*\=\s*\w+RAID_CHUNKSIZE\s*=\s*\w+",
|
||||||
|
)
|
||||||
|
|
||||||
|
_optional_equals = r"(?:\s*(?:=\s*)|\s+)"
|
||||||
|
|
||||||
|
def _add_option_string(self, directive: str) -> None:
|
||||||
|
regex = r"(?P<directive>%s)%s" r"'(?P<val>(?:[^']|'')*?)'(?!')" % (
|
||||||
|
re.escape(directive),
|
||||||
|
self._optional_equals,
|
||||||
|
)
|
||||||
|
self._pr_options.append(_pr_compile(regex, cleanup_text))
|
||||||
|
|
||||||
|
def _add_option_word(self, directive: str) -> None:
|
||||||
|
regex = r"(?P<directive>%s)%s" r"(?P<val>\w+)" % (
|
||||||
|
re.escape(directive),
|
||||||
|
self._optional_equals,
|
||||||
|
)
|
||||||
|
self._pr_options.append(_pr_compile(regex))
|
||||||
|
|
||||||
|
def _add_partition_option_word(self, directive: str) -> None:
|
||||||
|
if directive == "PARTITION BY" or directive == "SUBPARTITION BY":
|
||||||
|
regex = r"(?<!\S)(?P<directive>%s)%s" r"(?P<val>\w+.*)" % (
|
||||||
|
re.escape(directive),
|
||||||
|
self._optional_equals,
|
||||||
|
)
|
||||||
|
elif directive == "SUBPARTITIONS" or directive == "PARTITIONS":
|
||||||
|
regex = r"(?<!\S)(?P<directive>%s)%s" r"(?P<val>\d+)" % (
|
||||||
|
re.escape(directive),
|
||||||
|
self._optional_equals,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
regex = r"(?<!\S)(?P<directive>%s)(?!\S)" % (re.escape(directive),)
|
||||||
|
self._pr_options.append(_pr_compile(regex))
|
||||||
|
|
||||||
|
def _add_option_regex(self, directive: str, regex: str) -> None:
|
||||||
|
regex = r"(?P<directive>%s)%s" r"(?P<val>%s)" % (
|
||||||
|
re.escape(directive),
|
||||||
|
self._optional_equals,
|
||||||
|
regex,
|
||||||
|
)
|
||||||
|
self._pr_options.append(_pr_compile(regex))
|
||||||
|
|
||||||
|
|
||||||
|
_options_of_type_string = (
|
||||||
|
"COMMENT",
|
||||||
|
"DATA DIRECTORY",
|
||||||
|
"INDEX DIRECTORY",
|
||||||
|
"PASSWORD",
|
||||||
|
"CONNECTION",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def _pr_compile(
|
||||||
|
regex: str, cleanup: Callable[[str], str]
|
||||||
|
) -> Tuple[re.Pattern[Any], Callable[[str], str]]: ...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def _pr_compile(
|
||||||
|
regex: str, cleanup: None = None
|
||||||
|
) -> Tuple[re.Pattern[Any], None]: ...
|
||||||
|
|
||||||
|
|
||||||
|
def _pr_compile(
|
||||||
|
regex: str, cleanup: Optional[Callable[[str], str]] = None
|
||||||
|
) -> Tuple[re.Pattern[Any], Optional[Callable[[str], str]]]:
|
||||||
|
"""Prepare a 2-tuple of compiled regex and callable."""
|
||||||
|
|
||||||
|
return (_re_compile(regex), cleanup)
|
||||||
|
|
||||||
|
|
||||||
|
def _re_compile(regex: str) -> re.Pattern[Any]:
|
||||||
|
"""Compile a string to regex, I and UNICODE."""
|
||||||
|
|
||||||
|
return re.compile(regex, re.I | re.UNICODE)
|
||||||
|
|
||||||
|
|
||||||
|
def _strip_values(values: Sequence[str]) -> List[str]:
|
||||||
|
"Strip reflected values quotes"
|
||||||
|
strip_values: List[str] = []
|
||||||
|
for a in values:
|
||||||
|
if a[0:1] == '"' or a[0:1] == "'":
|
||||||
|
# strip enclosing quotes and unquote interior
|
||||||
|
a = a[1:-1].replace(a[0] * 2, a[0])
|
||||||
|
strip_values.append(a)
|
||||||
|
return strip_values
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup_text(raw_text: str) -> str:
|
||||||
|
if "\\" in raw_text:
|
||||||
|
raw_text = re.sub(
|
||||||
|
_control_char_regexp,
|
||||||
|
lambda s: _control_char_map[s[0]], # type: ignore[unused-ignore,index] # noqa: E501
|
||||||
|
raw_text,
|
||||||
|
)
|
||||||
|
return raw_text.replace("''", "'")
|
||||||
|
|
||||||
|
|
||||||
|
_control_char_map = {
|
||||||
|
"\\\\": "\\",
|
||||||
|
"\\0": "\0",
|
||||||
|
"\\a": "\a",
|
||||||
|
"\\b": "\b",
|
||||||
|
"\\t": "\t",
|
||||||
|
"\\n": "\n",
|
||||||
|
"\\v": "\v",
|
||||||
|
"\\f": "\f",
|
||||||
|
"\\r": "\r",
|
||||||
|
# '\\e':'\e',
|
||||||
|
}
|
||||||
|
_control_char_regexp = re.compile(
|
||||||
|
"|".join(re.escape(k) for k in _control_char_map)
|
||||||
|
)
|
||||||
@@ -0,0 +1,570 @@
|
|||||||
|
# dialects/mysql/reserved_words.py
|
||||||
|
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
||||||
|
# <see AUTHORS file>
|
||||||
|
#
|
||||||
|
# This module is part of SQLAlchemy and is released under
|
||||||
|
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||||
|
|
||||||
|
# generated using:
|
||||||
|
# https://gist.github.com/kkirsche/4f31f2153ed7a3248be1ec44ca6ddbc9
|
||||||
|
#
|
||||||
|
# https://mariadb.com/kb/en/reserved-words/
|
||||||
|
# includes: Reserved Words, Oracle Mode (separate set unioned)
|
||||||
|
# excludes: Exceptions, Function Names
|
||||||
|
|
||||||
|
RESERVED_WORDS_MARIADB = {
|
||||||
|
"accessible",
|
||||||
|
"add",
|
||||||
|
"all",
|
||||||
|
"alter",
|
||||||
|
"analyze",
|
||||||
|
"and",
|
||||||
|
"as",
|
||||||
|
"asc",
|
||||||
|
"asensitive",
|
||||||
|
"before",
|
||||||
|
"between",
|
||||||
|
"bigint",
|
||||||
|
"binary",
|
||||||
|
"blob",
|
||||||
|
"both",
|
||||||
|
"by",
|
||||||
|
"call",
|
||||||
|
"cascade",
|
||||||
|
"case",
|
||||||
|
"change",
|
||||||
|
"char",
|
||||||
|
"character",
|
||||||
|
"check",
|
||||||
|
"collate",
|
||||||
|
"column",
|
||||||
|
"condition",
|
||||||
|
"constraint",
|
||||||
|
"continue",
|
||||||
|
"convert",
|
||||||
|
"create",
|
||||||
|
"cross",
|
||||||
|
"current_date",
|
||||||
|
"current_role",
|
||||||
|
"current_time",
|
||||||
|
"current_timestamp",
|
||||||
|
"current_user",
|
||||||
|
"cursor",
|
||||||
|
"database",
|
||||||
|
"databases",
|
||||||
|
"day_hour",
|
||||||
|
"day_microsecond",
|
||||||
|
"day_minute",
|
||||||
|
"day_second",
|
||||||
|
"dec",
|
||||||
|
"decimal",
|
||||||
|
"declare",
|
||||||
|
"default",
|
||||||
|
"delayed",
|
||||||
|
"delete",
|
||||||
|
"desc",
|
||||||
|
"describe",
|
||||||
|
"deterministic",
|
||||||
|
"distinct",
|
||||||
|
"distinctrow",
|
||||||
|
"div",
|
||||||
|
"do_domain_ids",
|
||||||
|
"double",
|
||||||
|
"drop",
|
||||||
|
"dual",
|
||||||
|
"each",
|
||||||
|
"else",
|
||||||
|
"elseif",
|
||||||
|
"enclosed",
|
||||||
|
"escaped",
|
||||||
|
"except",
|
||||||
|
"exists",
|
||||||
|
"exit",
|
||||||
|
"explain",
|
||||||
|
"false",
|
||||||
|
"fetch",
|
||||||
|
"float",
|
||||||
|
"float4",
|
||||||
|
"float8",
|
||||||
|
"for",
|
||||||
|
"force",
|
||||||
|
"foreign",
|
||||||
|
"from",
|
||||||
|
"fulltext",
|
||||||
|
"general",
|
||||||
|
"grant",
|
||||||
|
"group",
|
||||||
|
"having",
|
||||||
|
"high_priority",
|
||||||
|
"hour_microsecond",
|
||||||
|
"hour_minute",
|
||||||
|
"hour_second",
|
||||||
|
"if",
|
||||||
|
"ignore",
|
||||||
|
"ignore_domain_ids",
|
||||||
|
"ignore_server_ids",
|
||||||
|
"in",
|
||||||
|
"index",
|
||||||
|
"infile",
|
||||||
|
"inner",
|
||||||
|
"inout",
|
||||||
|
"insensitive",
|
||||||
|
"insert",
|
||||||
|
"int",
|
||||||
|
"int1",
|
||||||
|
"int2",
|
||||||
|
"int3",
|
||||||
|
"int4",
|
||||||
|
"int8",
|
||||||
|
"integer",
|
||||||
|
"intersect",
|
||||||
|
"interval",
|
||||||
|
"into",
|
||||||
|
"is",
|
||||||
|
"iterate",
|
||||||
|
"join",
|
||||||
|
"key",
|
||||||
|
"keys",
|
||||||
|
"kill",
|
||||||
|
"leading",
|
||||||
|
"leave",
|
||||||
|
"left",
|
||||||
|
"like",
|
||||||
|
"limit",
|
||||||
|
"linear",
|
||||||
|
"lines",
|
||||||
|
"load",
|
||||||
|
"localtime",
|
||||||
|
"localtimestamp",
|
||||||
|
"lock",
|
||||||
|
"long",
|
||||||
|
"longblob",
|
||||||
|
"longtext",
|
||||||
|
"loop",
|
||||||
|
"low_priority",
|
||||||
|
"master_heartbeat_period",
|
||||||
|
"master_ssl_verify_server_cert",
|
||||||
|
"match",
|
||||||
|
"maxvalue",
|
||||||
|
"mediumblob",
|
||||||
|
"mediumint",
|
||||||
|
"mediumtext",
|
||||||
|
"middleint",
|
||||||
|
"minute_microsecond",
|
||||||
|
"minute_second",
|
||||||
|
"mod",
|
||||||
|
"modifies",
|
||||||
|
"natural",
|
||||||
|
"no_write_to_binlog",
|
||||||
|
"not",
|
||||||
|
"null",
|
||||||
|
"numeric",
|
||||||
|
"offset",
|
||||||
|
"on",
|
||||||
|
"optimize",
|
||||||
|
"option",
|
||||||
|
"optionally",
|
||||||
|
"or",
|
||||||
|
"order",
|
||||||
|
"out",
|
||||||
|
"outer",
|
||||||
|
"outfile",
|
||||||
|
"over",
|
||||||
|
"page_checksum",
|
||||||
|
"parse_vcol_expr",
|
||||||
|
"partition",
|
||||||
|
"position",
|
||||||
|
"precision",
|
||||||
|
"primary",
|
||||||
|
"procedure",
|
||||||
|
"purge",
|
||||||
|
"range",
|
||||||
|
"read",
|
||||||
|
"read_write",
|
||||||
|
"reads",
|
||||||
|
"real",
|
||||||
|
"recursive",
|
||||||
|
"ref_system_id",
|
||||||
|
"references",
|
||||||
|
"regexp",
|
||||||
|
"release",
|
||||||
|
"rename",
|
||||||
|
"repeat",
|
||||||
|
"replace",
|
||||||
|
"require",
|
||||||
|
"resignal",
|
||||||
|
"restrict",
|
||||||
|
"return",
|
||||||
|
"returning",
|
||||||
|
"revoke",
|
||||||
|
"right",
|
||||||
|
"rlike",
|
||||||
|
"rows",
|
||||||
|
"row_number",
|
||||||
|
"schema",
|
||||||
|
"schemas",
|
||||||
|
"second_microsecond",
|
||||||
|
"select",
|
||||||
|
"sensitive",
|
||||||
|
"separator",
|
||||||
|
"set",
|
||||||
|
"show",
|
||||||
|
"signal",
|
||||||
|
"slow",
|
||||||
|
"smallint",
|
||||||
|
"spatial",
|
||||||
|
"specific",
|
||||||
|
"sql",
|
||||||
|
"sql_big_result",
|
||||||
|
"sql_calc_found_rows",
|
||||||
|
"sql_small_result",
|
||||||
|
"sqlexception",
|
||||||
|
"sqlstate",
|
||||||
|
"sqlwarning",
|
||||||
|
"ssl",
|
||||||
|
"starting",
|
||||||
|
"stats_auto_recalc",
|
||||||
|
"stats_persistent",
|
||||||
|
"stats_sample_pages",
|
||||||
|
"straight_join",
|
||||||
|
"table",
|
||||||
|
"terminated",
|
||||||
|
"then",
|
||||||
|
"tinyblob",
|
||||||
|
"tinyint",
|
||||||
|
"tinytext",
|
||||||
|
"to",
|
||||||
|
"trailing",
|
||||||
|
"trigger",
|
||||||
|
"true",
|
||||||
|
"undo",
|
||||||
|
"union",
|
||||||
|
"unique",
|
||||||
|
"unlock",
|
||||||
|
"unsigned",
|
||||||
|
"update",
|
||||||
|
"usage",
|
||||||
|
"use",
|
||||||
|
"using",
|
||||||
|
"utc_date",
|
||||||
|
"utc_time",
|
||||||
|
"utc_timestamp",
|
||||||
|
"values",
|
||||||
|
"varbinary",
|
||||||
|
"varchar",
|
||||||
|
"varcharacter",
|
||||||
|
"varying",
|
||||||
|
"when",
|
||||||
|
"where",
|
||||||
|
"while",
|
||||||
|
"window",
|
||||||
|
"with",
|
||||||
|
"write",
|
||||||
|
"xor",
|
||||||
|
"year_month",
|
||||||
|
"zerofill",
|
||||||
|
}.union(
|
||||||
|
{
|
||||||
|
"body",
|
||||||
|
"elsif",
|
||||||
|
"goto",
|
||||||
|
"history",
|
||||||
|
"others",
|
||||||
|
"package",
|
||||||
|
"period",
|
||||||
|
"raise",
|
||||||
|
"rowtype",
|
||||||
|
"system",
|
||||||
|
"system_time",
|
||||||
|
"versioning",
|
||||||
|
"without",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# https://dev.mysql.com/doc/refman/8.3/en/keywords.html
|
||||||
|
# https://dev.mysql.com/doc/refman/8.0/en/keywords.html
|
||||||
|
# https://dev.mysql.com/doc/refman/5.7/en/keywords.html
|
||||||
|
# https://dev.mysql.com/doc/refman/5.6/en/keywords.html
|
||||||
|
# includes: MySQL x.0 Keywords and Reserved Words
|
||||||
|
# excludes: MySQL x.0 New Keywords and Reserved Words,
|
||||||
|
# MySQL x.0 Removed Keywords and Reserved Words
|
||||||
|
RESERVED_WORDS_MYSQL = {
|
||||||
|
"accessible",
|
||||||
|
"add",
|
||||||
|
"admin",
|
||||||
|
"all",
|
||||||
|
"alter",
|
||||||
|
"analyze",
|
||||||
|
"and",
|
||||||
|
"array",
|
||||||
|
"as",
|
||||||
|
"asc",
|
||||||
|
"asensitive",
|
||||||
|
"before",
|
||||||
|
"between",
|
||||||
|
"bigint",
|
||||||
|
"binary",
|
||||||
|
"blob",
|
||||||
|
"both",
|
||||||
|
"by",
|
||||||
|
"call",
|
||||||
|
"cascade",
|
||||||
|
"case",
|
||||||
|
"change",
|
||||||
|
"char",
|
||||||
|
"character",
|
||||||
|
"check",
|
||||||
|
"collate",
|
||||||
|
"column",
|
||||||
|
"condition",
|
||||||
|
"constraint",
|
||||||
|
"continue",
|
||||||
|
"convert",
|
||||||
|
"create",
|
||||||
|
"cross",
|
||||||
|
"cube",
|
||||||
|
"cume_dist",
|
||||||
|
"current_date",
|
||||||
|
"current_time",
|
||||||
|
"current_timestamp",
|
||||||
|
"current_user",
|
||||||
|
"cursor",
|
||||||
|
"database",
|
||||||
|
"databases",
|
||||||
|
"day_hour",
|
||||||
|
"day_microsecond",
|
||||||
|
"day_minute",
|
||||||
|
"day_second",
|
||||||
|
"dec",
|
||||||
|
"decimal",
|
||||||
|
"declare",
|
||||||
|
"default",
|
||||||
|
"delayed",
|
||||||
|
"delete",
|
||||||
|
"dense_rank",
|
||||||
|
"desc",
|
||||||
|
"describe",
|
||||||
|
"deterministic",
|
||||||
|
"distinct",
|
||||||
|
"distinctrow",
|
||||||
|
"div",
|
||||||
|
"double",
|
||||||
|
"drop",
|
||||||
|
"dual",
|
||||||
|
"each",
|
||||||
|
"else",
|
||||||
|
"elseif",
|
||||||
|
"empty",
|
||||||
|
"enclosed",
|
||||||
|
"escaped",
|
||||||
|
"except",
|
||||||
|
"exists",
|
||||||
|
"exit",
|
||||||
|
"explain",
|
||||||
|
"false",
|
||||||
|
"fetch",
|
||||||
|
"first_value",
|
||||||
|
"float",
|
||||||
|
"float4",
|
||||||
|
"float8",
|
||||||
|
"for",
|
||||||
|
"force",
|
||||||
|
"foreign",
|
||||||
|
"from",
|
||||||
|
"fulltext",
|
||||||
|
"function",
|
||||||
|
"general",
|
||||||
|
"generated",
|
||||||
|
"get",
|
||||||
|
"get_master_public_key",
|
||||||
|
"grant",
|
||||||
|
"group",
|
||||||
|
"grouping",
|
||||||
|
"groups",
|
||||||
|
"having",
|
||||||
|
"high_priority",
|
||||||
|
"hour_microsecond",
|
||||||
|
"hour_minute",
|
||||||
|
"hour_second",
|
||||||
|
"if",
|
||||||
|
"ignore",
|
||||||
|
"ignore_server_ids",
|
||||||
|
"in",
|
||||||
|
"index",
|
||||||
|
"infile",
|
||||||
|
"inner",
|
||||||
|
"inout",
|
||||||
|
"insensitive",
|
||||||
|
"insert",
|
||||||
|
"int",
|
||||||
|
"int1",
|
||||||
|
"int2",
|
||||||
|
"int3",
|
||||||
|
"int4",
|
||||||
|
"int8",
|
||||||
|
"integer",
|
||||||
|
"intersect",
|
||||||
|
"interval",
|
||||||
|
"into",
|
||||||
|
"io_after_gtids",
|
||||||
|
"io_before_gtids",
|
||||||
|
"is",
|
||||||
|
"iterate",
|
||||||
|
"join",
|
||||||
|
"json_table",
|
||||||
|
"key",
|
||||||
|
"keys",
|
||||||
|
"kill",
|
||||||
|
"lag",
|
||||||
|
"last_value",
|
||||||
|
"lateral",
|
||||||
|
"lead",
|
||||||
|
"leading",
|
||||||
|
"leave",
|
||||||
|
"left",
|
||||||
|
"like",
|
||||||
|
"limit",
|
||||||
|
"linear",
|
||||||
|
"lines",
|
||||||
|
"load",
|
||||||
|
"localtime",
|
||||||
|
"localtimestamp",
|
||||||
|
"lock",
|
||||||
|
"long",
|
||||||
|
"longblob",
|
||||||
|
"longtext",
|
||||||
|
"loop",
|
||||||
|
"low_priority",
|
||||||
|
"master_bind",
|
||||||
|
"master_heartbeat_period",
|
||||||
|
"master_ssl_verify_server_cert",
|
||||||
|
"match",
|
||||||
|
"maxvalue",
|
||||||
|
"mediumblob",
|
||||||
|
"mediumint",
|
||||||
|
"mediumtext",
|
||||||
|
"member",
|
||||||
|
"middleint",
|
||||||
|
"minute_microsecond",
|
||||||
|
"minute_second",
|
||||||
|
"mod",
|
||||||
|
"modifies",
|
||||||
|
"natural",
|
||||||
|
"no_write_to_binlog",
|
||||||
|
"not",
|
||||||
|
"nth_value",
|
||||||
|
"ntile",
|
||||||
|
"null",
|
||||||
|
"numeric",
|
||||||
|
"of",
|
||||||
|
"on",
|
||||||
|
"optimize",
|
||||||
|
"optimizer_costs",
|
||||||
|
"option",
|
||||||
|
"optionally",
|
||||||
|
"or",
|
||||||
|
"order",
|
||||||
|
"out",
|
||||||
|
"outer",
|
||||||
|
"outfile",
|
||||||
|
"over",
|
||||||
|
"parse_gcol_expr",
|
||||||
|
"parallel",
|
||||||
|
"partition",
|
||||||
|
"percent_rank",
|
||||||
|
"persist",
|
||||||
|
"persist_only",
|
||||||
|
"precision",
|
||||||
|
"primary",
|
||||||
|
"procedure",
|
||||||
|
"purge",
|
||||||
|
"qualify",
|
||||||
|
"range",
|
||||||
|
"rank",
|
||||||
|
"read",
|
||||||
|
"read_write",
|
||||||
|
"reads",
|
||||||
|
"real",
|
||||||
|
"recursive",
|
||||||
|
"references",
|
||||||
|
"regexp",
|
||||||
|
"release",
|
||||||
|
"rename",
|
||||||
|
"repeat",
|
||||||
|
"replace",
|
||||||
|
"require",
|
||||||
|
"resignal",
|
||||||
|
"restrict",
|
||||||
|
"return",
|
||||||
|
"revoke",
|
||||||
|
"right",
|
||||||
|
"rlike",
|
||||||
|
"role",
|
||||||
|
"row",
|
||||||
|
"row_number",
|
||||||
|
"rows",
|
||||||
|
"schema",
|
||||||
|
"schemas",
|
||||||
|
"second_microsecond",
|
||||||
|
"select",
|
||||||
|
"sensitive",
|
||||||
|
"separator",
|
||||||
|
"set",
|
||||||
|
"show",
|
||||||
|
"signal",
|
||||||
|
"slow",
|
||||||
|
"smallint",
|
||||||
|
"spatial",
|
||||||
|
"specific",
|
||||||
|
"sql",
|
||||||
|
"sql_after_gtids",
|
||||||
|
"sql_before_gtids",
|
||||||
|
"sql_big_result",
|
||||||
|
"sql_calc_found_rows",
|
||||||
|
"sql_small_result",
|
||||||
|
"sqlexception",
|
||||||
|
"sqlstate",
|
||||||
|
"sqlwarning",
|
||||||
|
"ssl",
|
||||||
|
"starting",
|
||||||
|
"stored",
|
||||||
|
"straight_join",
|
||||||
|
"system",
|
||||||
|
"table",
|
||||||
|
"terminated",
|
||||||
|
"then",
|
||||||
|
"tinyblob",
|
||||||
|
"tinyint",
|
||||||
|
"tinytext",
|
||||||
|
"to",
|
||||||
|
"trailing",
|
||||||
|
"trigger",
|
||||||
|
"true",
|
||||||
|
"undo",
|
||||||
|
"union",
|
||||||
|
"unique",
|
||||||
|
"unlock",
|
||||||
|
"unsigned",
|
||||||
|
"update",
|
||||||
|
"usage",
|
||||||
|
"use",
|
||||||
|
"using",
|
||||||
|
"utc_date",
|
||||||
|
"utc_time",
|
||||||
|
"utc_timestamp",
|
||||||
|
"values",
|
||||||
|
"varbinary",
|
||||||
|
"varchar",
|
||||||
|
"varcharacter",
|
||||||
|
"varying",
|
||||||
|
"virtual",
|
||||||
|
"when",
|
||||||
|
"where",
|
||||||
|
"while",
|
||||||
|
"window",
|
||||||
|
"with",
|
||||||
|
"write",
|
||||||
|
"xor",
|
||||||
|
"year_month",
|
||||||
|
"zerofill",
|
||||||
|
}
|
||||||
835
venv/Lib/site-packages/sqlalchemy/dialects/mysql/types.py
Normal file
835
venv/Lib/site-packages/sqlalchemy/dialects/mysql/types.py
Normal file
@@ -0,0 +1,835 @@
|
|||||||
|
# dialects/mysql/types.py
|
||||||
|
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
||||||
|
# <see AUTHORS file>
|
||||||
|
#
|
||||||
|
# This module is part of SQLAlchemy and is released under
|
||||||
|
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import decimal
|
||||||
|
from typing import Any
|
||||||
|
from typing import Iterable
|
||||||
|
from typing import Optional
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from ... import exc
|
||||||
|
from ... import util
|
||||||
|
from ...sql import sqltypes
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .base import MySQLDialect
|
||||||
|
from ...engine.interfaces import Dialect
|
||||||
|
from ...sql.type_api import _BindProcessorType
|
||||||
|
from ...sql.type_api import _ResultProcessorType
|
||||||
|
from ...sql.type_api import TypeEngine
|
||||||
|
|
||||||
|
|
||||||
|
class _NumericType:
|
||||||
|
"""Base for MySQL numeric types.
|
||||||
|
|
||||||
|
This is the base both for NUMERIC as well as INTEGER, hence
|
||||||
|
it's a mixin.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, unsigned: bool = False, zerofill: bool = False, **kw: Any
|
||||||
|
):
|
||||||
|
self.unsigned = unsigned
|
||||||
|
self.zerofill = zerofill
|
||||||
|
super().__init__(**kw)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return util.generic_repr(
|
||||||
|
self, to_inspect=[_NumericType, sqltypes.Numeric]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class _FloatType(_NumericType, sqltypes.Float[Union[decimal.Decimal, float]]):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
precision: Optional[int] = None,
|
||||||
|
scale: Optional[int] = None,
|
||||||
|
asdecimal: bool = True,
|
||||||
|
**kw: Any,
|
||||||
|
):
|
||||||
|
if isinstance(self, (REAL, DOUBLE)) and (
|
||||||
|
(precision is None and scale is not None)
|
||||||
|
or (precision is not None and scale is None)
|
||||||
|
):
|
||||||
|
raise exc.ArgumentError(
|
||||||
|
"You must specify both precision and scale or omit "
|
||||||
|
"both altogether."
|
||||||
|
)
|
||||||
|
super().__init__(precision=precision, asdecimal=asdecimal, **kw)
|
||||||
|
self.scale = scale
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return util.generic_repr(
|
||||||
|
self, to_inspect=[_FloatType, _NumericType, sqltypes.Float]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class _IntegerType(_NumericType, sqltypes.Integer):
|
||||||
|
def __init__(self, display_width: Optional[int] = None, **kw: Any):
|
||||||
|
self.display_width = display_width
|
||||||
|
super().__init__(**kw)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return util.generic_repr(
|
||||||
|
self, to_inspect=[_IntegerType, _NumericType, sqltypes.Integer]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class _StringType(sqltypes.String):
|
||||||
|
"""Base for MySQL string types."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
charset: Optional[str] = None,
|
||||||
|
collation: Optional[str] = None,
|
||||||
|
ascii: bool = False, # noqa
|
||||||
|
binary: bool = False,
|
||||||
|
unicode: bool = False,
|
||||||
|
national: bool = False,
|
||||||
|
**kw: Any,
|
||||||
|
):
|
||||||
|
self.charset = charset
|
||||||
|
|
||||||
|
# allow collate= or collation=
|
||||||
|
kw.setdefault("collation", kw.pop("collate", collation))
|
||||||
|
|
||||||
|
self.ascii = ascii
|
||||||
|
self.unicode = unicode
|
||||||
|
self.binary = binary
|
||||||
|
self.national = national
|
||||||
|
super().__init__(**kw)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return util.generic_repr(
|
||||||
|
self, to_inspect=[_StringType, sqltypes.String]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class _MatchType(
|
||||||
|
sqltypes.Float[Union[decimal.Decimal, float]], sqltypes.MatchType
|
||||||
|
):
|
||||||
|
def __init__(self, **kw: Any):
|
||||||
|
# TODO: float arguments?
|
||||||
|
sqltypes.Float.__init__(self) # type: ignore[arg-type]
|
||||||
|
sqltypes.MatchType.__init__(self)
|
||||||
|
|
||||||
|
|
||||||
|
class NUMERIC(_NumericType, sqltypes.NUMERIC[Union[decimal.Decimal, float]]):
|
||||||
|
"""MySQL NUMERIC type."""
|
||||||
|
|
||||||
|
__visit_name__ = "NUMERIC"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
precision: Optional[int] = None,
|
||||||
|
scale: Optional[int] = None,
|
||||||
|
asdecimal: bool = True,
|
||||||
|
**kw: Any,
|
||||||
|
):
|
||||||
|
"""Construct a NUMERIC.
|
||||||
|
|
||||||
|
:param precision: Total digits in this number. If scale and precision
|
||||||
|
are both None, values are stored to limits allowed by the server.
|
||||||
|
|
||||||
|
:param scale: The number of digits after the decimal point.
|
||||||
|
|
||||||
|
:param unsigned: a boolean, optional.
|
||||||
|
|
||||||
|
:param zerofill: Optional. If true, values will be stored as strings
|
||||||
|
left-padded with zeros. Note that this does not effect the values
|
||||||
|
returned by the underlying database API, which continue to be
|
||||||
|
numeric.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(
|
||||||
|
precision=precision, scale=scale, asdecimal=asdecimal, **kw
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DECIMAL(_NumericType, sqltypes.DECIMAL[Union[decimal.Decimal, float]]):
|
||||||
|
"""MySQL DECIMAL type."""
|
||||||
|
|
||||||
|
__visit_name__ = "DECIMAL"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
precision: Optional[int] = None,
|
||||||
|
scale: Optional[int] = None,
|
||||||
|
asdecimal: bool = True,
|
||||||
|
**kw: Any,
|
||||||
|
):
|
||||||
|
"""Construct a DECIMAL.
|
||||||
|
|
||||||
|
:param precision: Total digits in this number. If scale and precision
|
||||||
|
are both None, values are stored to limits allowed by the server.
|
||||||
|
|
||||||
|
:param scale: The number of digits after the decimal point.
|
||||||
|
|
||||||
|
:param unsigned: a boolean, optional.
|
||||||
|
|
||||||
|
:param zerofill: Optional. If true, values will be stored as strings
|
||||||
|
left-padded with zeros. Note that this does not effect the values
|
||||||
|
returned by the underlying database API, which continue to be
|
||||||
|
numeric.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(
|
||||||
|
precision=precision, scale=scale, asdecimal=asdecimal, **kw
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DOUBLE(_FloatType, sqltypes.DOUBLE[Union[decimal.Decimal, float]]):
|
||||||
|
"""MySQL DOUBLE type."""
|
||||||
|
|
||||||
|
__visit_name__ = "DOUBLE"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
precision: Optional[int] = None,
|
||||||
|
scale: Optional[int] = None,
|
||||||
|
asdecimal: bool = True,
|
||||||
|
**kw: Any,
|
||||||
|
):
|
||||||
|
"""Construct a DOUBLE.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The :class:`.DOUBLE` type by default converts from float
|
||||||
|
to Decimal, using a truncation that defaults to 10 digits.
|
||||||
|
Specify either ``scale=n`` or ``decimal_return_scale=n`` in order
|
||||||
|
to change this scale, or ``asdecimal=False`` to return values
|
||||||
|
directly as Python floating points.
|
||||||
|
|
||||||
|
:param precision: Total digits in this number. If scale and precision
|
||||||
|
are both None, values are stored to limits allowed by the server.
|
||||||
|
|
||||||
|
:param scale: The number of digits after the decimal point.
|
||||||
|
|
||||||
|
:param unsigned: a boolean, optional.
|
||||||
|
|
||||||
|
:param zerofill: Optional. If true, values will be stored as strings
|
||||||
|
left-padded with zeros. Note that this does not effect the values
|
||||||
|
returned by the underlying database API, which continue to be
|
||||||
|
numeric.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(
|
||||||
|
precision=precision, scale=scale, asdecimal=asdecimal, **kw
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class REAL(_FloatType, sqltypes.REAL[Union[decimal.Decimal, float]]):
|
||||||
|
"""MySQL REAL type."""
|
||||||
|
|
||||||
|
__visit_name__ = "REAL"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
precision: Optional[int] = None,
|
||||||
|
scale: Optional[int] = None,
|
||||||
|
asdecimal: bool = True,
|
||||||
|
**kw: Any,
|
||||||
|
):
|
||||||
|
"""Construct a REAL.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The :class:`.REAL` type by default converts from float
|
||||||
|
to Decimal, using a truncation that defaults to 10 digits.
|
||||||
|
Specify either ``scale=n`` or ``decimal_return_scale=n`` in order
|
||||||
|
to change this scale, or ``asdecimal=False`` to return values
|
||||||
|
directly as Python floating points.
|
||||||
|
|
||||||
|
:param precision: Total digits in this number. If scale and precision
|
||||||
|
are both None, values are stored to limits allowed by the server.
|
||||||
|
|
||||||
|
:param scale: The number of digits after the decimal point.
|
||||||
|
|
||||||
|
:param unsigned: a boolean, optional.
|
||||||
|
|
||||||
|
:param zerofill: Optional. If true, values will be stored as strings
|
||||||
|
left-padded with zeros. Note that this does not effect the values
|
||||||
|
returned by the underlying database API, which continue to be
|
||||||
|
numeric.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(
|
||||||
|
precision=precision, scale=scale, asdecimal=asdecimal, **kw
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FLOAT(_FloatType, sqltypes.FLOAT[Union[decimal.Decimal, float]]):
|
||||||
|
"""MySQL FLOAT type."""
|
||||||
|
|
||||||
|
__visit_name__ = "FLOAT"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
precision: Optional[int] = None,
|
||||||
|
scale: Optional[int] = None,
|
||||||
|
asdecimal: bool = False,
|
||||||
|
**kw: Any,
|
||||||
|
):
|
||||||
|
"""Construct a FLOAT.
|
||||||
|
|
||||||
|
:param precision: Total digits in this number. If scale and precision
|
||||||
|
are both None, values are stored to limits allowed by the server.
|
||||||
|
|
||||||
|
:param scale: The number of digits after the decimal point.
|
||||||
|
|
||||||
|
:param unsigned: a boolean, optional.
|
||||||
|
|
||||||
|
:param zerofill: Optional. If true, values will be stored as strings
|
||||||
|
left-padded with zeros. Note that this does not effect the values
|
||||||
|
returned by the underlying database API, which continue to be
|
||||||
|
numeric.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(
|
||||||
|
precision=precision, scale=scale, asdecimal=asdecimal, **kw
|
||||||
|
)
|
||||||
|
|
||||||
|
def bind_processor(
|
||||||
|
self, dialect: Dialect
|
||||||
|
) -> Optional[_BindProcessorType[Union[decimal.Decimal, float]]]:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class INTEGER(_IntegerType, sqltypes.INTEGER):
|
||||||
|
"""MySQL INTEGER type."""
|
||||||
|
|
||||||
|
__visit_name__ = "INTEGER"
|
||||||
|
|
||||||
|
def __init__(self, display_width: Optional[int] = None, **kw: Any):
|
||||||
|
"""Construct an INTEGER.
|
||||||
|
|
||||||
|
:param display_width: Optional, maximum display width for this number.
|
||||||
|
|
||||||
|
:param unsigned: a boolean, optional.
|
||||||
|
|
||||||
|
:param zerofill: Optional. If true, values will be stored as strings
|
||||||
|
left-padded with zeros. Note that this does not effect the values
|
||||||
|
returned by the underlying database API, which continue to be
|
||||||
|
numeric.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(display_width=display_width, **kw)
|
||||||
|
|
||||||
|
|
||||||
|
class BIGINT(_IntegerType, sqltypes.BIGINT):
|
||||||
|
"""MySQL BIGINTEGER type."""
|
||||||
|
|
||||||
|
__visit_name__ = "BIGINT"
|
||||||
|
|
||||||
|
def __init__(self, display_width: Optional[int] = None, **kw: Any):
|
||||||
|
"""Construct a BIGINTEGER.
|
||||||
|
|
||||||
|
:param display_width: Optional, maximum display width for this number.
|
||||||
|
|
||||||
|
:param unsigned: a boolean, optional.
|
||||||
|
|
||||||
|
:param zerofill: Optional. If true, values will be stored as strings
|
||||||
|
left-padded with zeros. Note that this does not effect the values
|
||||||
|
returned by the underlying database API, which continue to be
|
||||||
|
numeric.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(display_width=display_width, **kw)
|
||||||
|
|
||||||
|
|
||||||
|
class MEDIUMINT(_IntegerType):
|
||||||
|
"""MySQL MEDIUMINTEGER type."""
|
||||||
|
|
||||||
|
__visit_name__ = "MEDIUMINT"
|
||||||
|
|
||||||
|
def __init__(self, display_width: Optional[int] = None, **kw: Any):
|
||||||
|
"""Construct a MEDIUMINTEGER
|
||||||
|
|
||||||
|
:param display_width: Optional, maximum display width for this number.
|
||||||
|
|
||||||
|
:param unsigned: a boolean, optional.
|
||||||
|
|
||||||
|
:param zerofill: Optional. If true, values will be stored as strings
|
||||||
|
left-padded with zeros. Note that this does not effect the values
|
||||||
|
returned by the underlying database API, which continue to be
|
||||||
|
numeric.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(display_width=display_width, **kw)
|
||||||
|
|
||||||
|
|
||||||
|
class TINYINT(_IntegerType):
|
||||||
|
"""MySQL TINYINT type."""
|
||||||
|
|
||||||
|
__visit_name__ = "TINYINT"
|
||||||
|
|
||||||
|
def __init__(self, display_width: Optional[int] = None, **kw: Any):
|
||||||
|
"""Construct a TINYINT.
|
||||||
|
|
||||||
|
:param display_width: Optional, maximum display width for this number.
|
||||||
|
|
||||||
|
:param unsigned: a boolean, optional.
|
||||||
|
|
||||||
|
:param zerofill: Optional. If true, values will be stored as strings
|
||||||
|
left-padded with zeros. Note that this does not effect the values
|
||||||
|
returned by the underlying database API, which continue to be
|
||||||
|
numeric.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(display_width=display_width, **kw)
|
||||||
|
|
||||||
|
def _compare_type_affinity(self, other: TypeEngine[Any]) -> bool:
|
||||||
|
return (
|
||||||
|
self._type_affinity is other._type_affinity
|
||||||
|
or other._type_affinity is sqltypes.Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SMALLINT(_IntegerType, sqltypes.SMALLINT):
|
||||||
|
"""MySQL SMALLINTEGER type."""
|
||||||
|
|
||||||
|
__visit_name__ = "SMALLINT"
|
||||||
|
|
||||||
|
def __init__(self, display_width: Optional[int] = None, **kw: Any):
|
||||||
|
"""Construct a SMALLINTEGER.
|
||||||
|
|
||||||
|
:param display_width: Optional, maximum display width for this number.
|
||||||
|
|
||||||
|
:param unsigned: a boolean, optional.
|
||||||
|
|
||||||
|
:param zerofill: Optional. If true, values will be stored as strings
|
||||||
|
left-padded with zeros. Note that this does not effect the values
|
||||||
|
returned by the underlying database API, which continue to be
|
||||||
|
numeric.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(display_width=display_width, **kw)
|
||||||
|
|
||||||
|
|
||||||
|
class BIT(sqltypes.TypeEngine[Any]):
|
||||||
|
"""MySQL BIT type.
|
||||||
|
|
||||||
|
This type is for MySQL 5.0.3 or greater for MyISAM, and 5.0.5 or greater
|
||||||
|
for MyISAM, MEMORY, InnoDB and BDB. For older versions, use a
|
||||||
|
MSTinyInteger() type.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
__visit_name__ = "BIT"
|
||||||
|
|
||||||
|
def __init__(self, length: Optional[int] = None):
|
||||||
|
"""Construct a BIT.
|
||||||
|
|
||||||
|
:param length: Optional, number of bits.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.length = length
|
||||||
|
|
||||||
|
def result_processor(
|
||||||
|
self, dialect: MySQLDialect, coltype: object # type: ignore[override]
|
||||||
|
) -> Optional[_ResultProcessorType[Any]]:
|
||||||
|
"""Convert a MySQL's 64 bit, variable length binary string to a
|
||||||
|
long."""
|
||||||
|
|
||||||
|
if dialect.supports_native_bit:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def process(value: Optional[Iterable[int]]) -> Optional[int]:
|
||||||
|
if value is not None:
|
||||||
|
v = 0
|
||||||
|
for i in value:
|
||||||
|
v = v << 8 | i
|
||||||
|
return v
|
||||||
|
return value
|
||||||
|
|
||||||
|
return process
|
||||||
|
|
||||||
|
|
||||||
|
class TIME(sqltypes.TIME):
|
||||||
|
"""MySQL TIME type."""
|
||||||
|
|
||||||
|
__visit_name__ = "TIME"
|
||||||
|
|
||||||
|
def __init__(self, timezone: bool = False, fsp: Optional[int] = None):
|
||||||
|
"""Construct a MySQL TIME type.
|
||||||
|
|
||||||
|
:param timezone: not used by the MySQL dialect.
|
||||||
|
:param fsp: fractional seconds precision value.
|
||||||
|
MySQL 5.6 supports storage of fractional seconds;
|
||||||
|
this parameter will be used when emitting DDL
|
||||||
|
for the TIME type.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
DBAPI driver support for fractional seconds may
|
||||||
|
be limited; current support includes
|
||||||
|
MySQL Connector/Python.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(timezone=timezone)
|
||||||
|
self.fsp = fsp
|
||||||
|
|
||||||
|
def result_processor(
|
||||||
|
self, dialect: Dialect, coltype: object
|
||||||
|
) -> _ResultProcessorType[datetime.time]:
|
||||||
|
time = datetime.time
|
||||||
|
|
||||||
|
def process(value: Any) -> Optional[datetime.time]:
|
||||||
|
# convert from a timedelta value
|
||||||
|
if value is not None:
|
||||||
|
microseconds = value.microseconds
|
||||||
|
seconds = value.seconds
|
||||||
|
minutes = seconds // 60
|
||||||
|
return time(
|
||||||
|
minutes // 60,
|
||||||
|
minutes % 60,
|
||||||
|
seconds - minutes * 60,
|
||||||
|
microsecond=microseconds,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return process
|
||||||
|
|
||||||
|
|
||||||
|
class TIMESTAMP(sqltypes.TIMESTAMP):
|
||||||
|
"""MySQL TIMESTAMP type."""
|
||||||
|
|
||||||
|
__visit_name__ = "TIMESTAMP"
|
||||||
|
|
||||||
|
def __init__(self, timezone: bool = False, fsp: Optional[int] = None):
|
||||||
|
"""Construct a MySQL TIMESTAMP type.
|
||||||
|
|
||||||
|
:param timezone: not used by the MySQL dialect.
|
||||||
|
:param fsp: fractional seconds precision value.
|
||||||
|
MySQL 5.6.4 supports storage of fractional seconds;
|
||||||
|
this parameter will be used when emitting DDL
|
||||||
|
for the TIMESTAMP type.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
DBAPI driver support for fractional seconds may
|
||||||
|
be limited; current support includes
|
||||||
|
MySQL Connector/Python.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(timezone=timezone)
|
||||||
|
self.fsp = fsp
|
||||||
|
|
||||||
|
|
||||||
|
class DATETIME(sqltypes.DATETIME):
|
||||||
|
"""MySQL DATETIME type."""
|
||||||
|
|
||||||
|
__visit_name__ = "DATETIME"
|
||||||
|
|
||||||
|
def __init__(self, timezone: bool = False, fsp: Optional[int] = None):
|
||||||
|
"""Construct a MySQL DATETIME type.
|
||||||
|
|
||||||
|
:param timezone: not used by the MySQL dialect.
|
||||||
|
:param fsp: fractional seconds precision value.
|
||||||
|
MySQL 5.6.4 supports storage of fractional seconds;
|
||||||
|
this parameter will be used when emitting DDL
|
||||||
|
for the DATETIME type.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
DBAPI driver support for fractional seconds may
|
||||||
|
be limited; current support includes
|
||||||
|
MySQL Connector/Python.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(timezone=timezone)
|
||||||
|
self.fsp = fsp
|
||||||
|
|
||||||
|
|
||||||
|
class YEAR(sqltypes.TypeEngine[Any]):
|
||||||
|
"""MySQL YEAR type, for single byte storage of years 1901-2155."""
|
||||||
|
|
||||||
|
__visit_name__ = "YEAR"
|
||||||
|
|
||||||
|
def __init__(self, display_width: Optional[int] = None):
|
||||||
|
self.display_width = display_width
|
||||||
|
|
||||||
|
|
||||||
|
class TEXT(_StringType, sqltypes.TEXT):
|
||||||
|
"""MySQL TEXT type, for character storage encoded up to 2^16 bytes."""
|
||||||
|
|
||||||
|
__visit_name__ = "TEXT"
|
||||||
|
|
||||||
|
def __init__(self, length: Optional[int] = None, **kw: Any):
|
||||||
|
"""Construct a TEXT.
|
||||||
|
|
||||||
|
:param length: Optional, if provided the server may optimize storage
|
||||||
|
by substituting the smallest TEXT type sufficient to store
|
||||||
|
``length`` bytes of characters.
|
||||||
|
|
||||||
|
:param charset: Optional, a column-level character set for this string
|
||||||
|
value. Takes precedence to 'ascii' or 'unicode' short-hand.
|
||||||
|
|
||||||
|
:param collation: Optional, a column-level collation for this string
|
||||||
|
value. Takes precedence to 'binary' short-hand.
|
||||||
|
|
||||||
|
:param ascii: Defaults to False: short-hand for the ``latin1``
|
||||||
|
character set, generates ASCII in schema.
|
||||||
|
|
||||||
|
:param unicode: Defaults to False: short-hand for the ``ucs2``
|
||||||
|
character set, generates UNICODE in schema.
|
||||||
|
|
||||||
|
:param national: Optional. If true, use the server's configured
|
||||||
|
national character set.
|
||||||
|
|
||||||
|
:param binary: Defaults to False: short-hand, pick the binary
|
||||||
|
collation type that matches the column's character set. Generates
|
||||||
|
BINARY in schema. This does not affect the type of data stored,
|
||||||
|
only the collation of character data.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(length=length, **kw)
|
||||||
|
|
||||||
|
|
||||||
|
class TINYTEXT(_StringType):
|
||||||
|
"""MySQL TINYTEXT type, for character storage encoded up to 2^8 bytes."""
|
||||||
|
|
||||||
|
__visit_name__ = "TINYTEXT"
|
||||||
|
|
||||||
|
def __init__(self, **kwargs: Any):
|
||||||
|
"""Construct a TINYTEXT.
|
||||||
|
|
||||||
|
:param charset: Optional, a column-level character set for this string
|
||||||
|
value. Takes precedence to 'ascii' or 'unicode' short-hand.
|
||||||
|
|
||||||
|
:param collation: Optional, a column-level collation for this string
|
||||||
|
value. Takes precedence to 'binary' short-hand.
|
||||||
|
|
||||||
|
:param ascii: Defaults to False: short-hand for the ``latin1``
|
||||||
|
character set, generates ASCII in schema.
|
||||||
|
|
||||||
|
:param unicode: Defaults to False: short-hand for the ``ucs2``
|
||||||
|
character set, generates UNICODE in schema.
|
||||||
|
|
||||||
|
:param national: Optional. If true, use the server's configured
|
||||||
|
national character set.
|
||||||
|
|
||||||
|
:param binary: Defaults to False: short-hand, pick the binary
|
||||||
|
collation type that matches the column's character set. Generates
|
||||||
|
BINARY in schema. This does not affect the type of data stored,
|
||||||
|
only the collation of character data.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class MEDIUMTEXT(_StringType):
|
||||||
|
"""MySQL MEDIUMTEXT type, for character storage encoded up
|
||||||
|
to 2^24 bytes."""
|
||||||
|
|
||||||
|
__visit_name__ = "MEDIUMTEXT"
|
||||||
|
|
||||||
|
def __init__(self, **kwargs: Any):
|
||||||
|
"""Construct a MEDIUMTEXT.
|
||||||
|
|
||||||
|
:param charset: Optional, a column-level character set for this string
|
||||||
|
value. Takes precedence to 'ascii' or 'unicode' short-hand.
|
||||||
|
|
||||||
|
:param collation: Optional, a column-level collation for this string
|
||||||
|
value. Takes precedence to 'binary' short-hand.
|
||||||
|
|
||||||
|
:param ascii: Defaults to False: short-hand for the ``latin1``
|
||||||
|
character set, generates ASCII in schema.
|
||||||
|
|
||||||
|
:param unicode: Defaults to False: short-hand for the ``ucs2``
|
||||||
|
character set, generates UNICODE in schema.
|
||||||
|
|
||||||
|
:param national: Optional. If true, use the server's configured
|
||||||
|
national character set.
|
||||||
|
|
||||||
|
:param binary: Defaults to False: short-hand, pick the binary
|
||||||
|
collation type that matches the column's character set. Generates
|
||||||
|
BINARY in schema. This does not affect the type of data stored,
|
||||||
|
only the collation of character data.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class LONGTEXT(_StringType):
|
||||||
|
"""MySQL LONGTEXT type, for character storage encoded up to 2^32 bytes."""
|
||||||
|
|
||||||
|
__visit_name__ = "LONGTEXT"
|
||||||
|
|
||||||
|
def __init__(self, **kwargs: Any):
|
||||||
|
"""Construct a LONGTEXT.
|
||||||
|
|
||||||
|
:param charset: Optional, a column-level character set for this string
|
||||||
|
value. Takes precedence to 'ascii' or 'unicode' short-hand.
|
||||||
|
|
||||||
|
:param collation: Optional, a column-level collation for this string
|
||||||
|
value. Takes precedence to 'binary' short-hand.
|
||||||
|
|
||||||
|
:param ascii: Defaults to False: short-hand for the ``latin1``
|
||||||
|
character set, generates ASCII in schema.
|
||||||
|
|
||||||
|
:param unicode: Defaults to False: short-hand for the ``ucs2``
|
||||||
|
character set, generates UNICODE in schema.
|
||||||
|
|
||||||
|
:param national: Optional. If true, use the server's configured
|
||||||
|
national character set.
|
||||||
|
|
||||||
|
:param binary: Defaults to False: short-hand, pick the binary
|
||||||
|
collation type that matches the column's character set. Generates
|
||||||
|
BINARY in schema. This does not affect the type of data stored,
|
||||||
|
only the collation of character data.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class VARCHAR(_StringType, sqltypes.VARCHAR):
|
||||||
|
"""MySQL VARCHAR type, for variable-length character data."""
|
||||||
|
|
||||||
|
__visit_name__ = "VARCHAR"
|
||||||
|
|
||||||
|
def __init__(self, length: Optional[int] = None, **kwargs: Any) -> None:
|
||||||
|
"""Construct a VARCHAR.
|
||||||
|
|
||||||
|
:param charset: Optional, a column-level character set for this string
|
||||||
|
value. Takes precedence to 'ascii' or 'unicode' short-hand.
|
||||||
|
|
||||||
|
:param collation: Optional, a column-level collation for this string
|
||||||
|
value. Takes precedence to 'binary' short-hand.
|
||||||
|
|
||||||
|
:param ascii: Defaults to False: short-hand for the ``latin1``
|
||||||
|
character set, generates ASCII in schema.
|
||||||
|
|
||||||
|
:param unicode: Defaults to False: short-hand for the ``ucs2``
|
||||||
|
character set, generates UNICODE in schema.
|
||||||
|
|
||||||
|
:param national: Optional. If true, use the server's configured
|
||||||
|
national character set.
|
||||||
|
|
||||||
|
:param binary: Defaults to False: short-hand, pick the binary
|
||||||
|
collation type that matches the column's character set. Generates
|
||||||
|
BINARY in schema. This does not affect the type of data stored,
|
||||||
|
only the collation of character data.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(length=length, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class CHAR(_StringType, sqltypes.CHAR):
|
||||||
|
"""MySQL CHAR type, for fixed-length character data."""
|
||||||
|
|
||||||
|
__visit_name__ = "CHAR"
|
||||||
|
|
||||||
|
def __init__(self, length: Optional[int] = None, **kwargs: Any):
|
||||||
|
"""Construct a CHAR.
|
||||||
|
|
||||||
|
:param length: Maximum data length, in characters.
|
||||||
|
|
||||||
|
:param binary: Optional, use the default binary collation for the
|
||||||
|
national character set. This does not affect the type of data
|
||||||
|
stored, use a BINARY type for binary data.
|
||||||
|
|
||||||
|
:param collation: Optional, request a particular collation. Must be
|
||||||
|
compatible with the national character set.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(length=length, **kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _adapt_string_for_cast(cls, type_: sqltypes.String) -> sqltypes.CHAR:
|
||||||
|
# copy the given string type into a CHAR
|
||||||
|
# for the purposes of rendering a CAST expression
|
||||||
|
type_ = sqltypes.to_instance(type_)
|
||||||
|
if isinstance(type_, sqltypes.CHAR):
|
||||||
|
return type_
|
||||||
|
elif isinstance(type_, _StringType):
|
||||||
|
return CHAR(
|
||||||
|
length=type_.length,
|
||||||
|
charset=type_.charset,
|
||||||
|
collation=type_.collation,
|
||||||
|
ascii=type_.ascii,
|
||||||
|
binary=type_.binary,
|
||||||
|
unicode=type_.unicode,
|
||||||
|
national=False, # not supported in CAST
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return CHAR(length=type_.length)
|
||||||
|
|
||||||
|
|
||||||
|
class NVARCHAR(_StringType, sqltypes.NVARCHAR):
|
||||||
|
"""MySQL NVARCHAR type.
|
||||||
|
|
||||||
|
For variable-length character data in the server's configured national
|
||||||
|
character set.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__visit_name__ = "NVARCHAR"
|
||||||
|
|
||||||
|
def __init__(self, length: Optional[int] = None, **kwargs: Any):
|
||||||
|
"""Construct an NVARCHAR.
|
||||||
|
|
||||||
|
:param length: Maximum data length, in characters.
|
||||||
|
|
||||||
|
:param binary: Optional, use the default binary collation for the
|
||||||
|
national character set. This does not affect the type of data
|
||||||
|
stored, use a BINARY type for binary data.
|
||||||
|
|
||||||
|
:param collation: Optional, request a particular collation. Must be
|
||||||
|
compatible with the national character set.
|
||||||
|
|
||||||
|
"""
|
||||||
|
kwargs["national"] = True
|
||||||
|
super().__init__(length=length, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class NCHAR(_StringType, sqltypes.NCHAR):
|
||||||
|
"""MySQL NCHAR type.
|
||||||
|
|
||||||
|
For fixed-length character data in the server's configured national
|
||||||
|
character set.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__visit_name__ = "NCHAR"
|
||||||
|
|
||||||
|
def __init__(self, length: Optional[int] = None, **kwargs: Any):
|
||||||
|
"""Construct an NCHAR.
|
||||||
|
|
||||||
|
:param length: Maximum data length, in characters.
|
||||||
|
|
||||||
|
:param binary: Optional, use the default binary collation for the
|
||||||
|
national character set. This does not affect the type of data
|
||||||
|
stored, use a BINARY type for binary data.
|
||||||
|
|
||||||
|
:param collation: Optional, request a particular collation. Must be
|
||||||
|
compatible with the national character set.
|
||||||
|
|
||||||
|
"""
|
||||||
|
kwargs["national"] = True
|
||||||
|
super().__init__(length=length, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class TINYBLOB(sqltypes._Binary):
|
||||||
|
"""MySQL TINYBLOB type, for binary data up to 2^8 bytes."""
|
||||||
|
|
||||||
|
__visit_name__ = "TINYBLOB"
|
||||||
|
|
||||||
|
|
||||||
|
class MEDIUMBLOB(sqltypes._Binary):
|
||||||
|
"""MySQL MEDIUMBLOB type, for binary data up to 2^24 bytes."""
|
||||||
|
|
||||||
|
__visit_name__ = "MEDIUMBLOB"
|
||||||
|
|
||||||
|
|
||||||
|
class LONGBLOB(sqltypes._Binary):
|
||||||
|
"""MySQL LONGBLOB type, for binary data up to 2^32 bytes."""
|
||||||
|
|
||||||
|
__visit_name__ = "LONGBLOB"
|
||||||
Reference in New Issue
Block a user