1169 lines
42 KiB
Python
1169 lines
42 KiB
Python
|
# Copyright (c) 2009, 2024, Oracle and/or its affiliates.
|
||
|
#
|
||
|
# This program is free software; you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License, version 2.0, as
|
||
|
# published by the Free Software Foundation.
|
||
|
#
|
||
|
# This program is designed to work with certain software (including
|
||
|
# but not limited to OpenSSL) that is licensed under separate terms,
|
||
|
# as designated in a particular file or component or in included license
|
||
|
# documentation. The authors of MySQL hereby grant you an
|
||
|
# additional permission to link the program and your derivative works
|
||
|
# with the separately licensed software that they have either included with
|
||
|
# the program or referenced in the documentation.
|
||
|
#
|
||
|
# Without limiting anything contained in the foregoing, this file,
|
||
|
# which is part of MySQL Connector/Python, is also subject to the
|
||
|
# Universal FOSS Exception, version 1.0, a copy of which can be found at
|
||
|
# http://oss.oracle.com/licenses/universal-foss-exception.
|
||
|
#
|
||
|
# This program is distributed in the hope that it will be useful, but
|
||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
|
# See the GNU General Public License, version 2.0, for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with this program; if not, write to the Free Software Foundation, Inc.,
|
||
|
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
|
||
|
"""Various MySQL constants and character sets."""
|
||
|
|
||
|
import warnings
|
||
|
|
||
|
from abc import ABC, ABCMeta
|
||
|
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union, ValuesView
|
||
|
|
||
|
from .charsets import MYSQL_CHARACTER_SETS, MYSQL_CHARACTER_SETS_57
|
||
|
from .errors import ProgrammingError
|
||
|
from .tls_ciphers import APPROVED_TLS_VERSIONS, DEPRECATED_TLS_VERSIONS
|
||
|
|
||
|
NET_BUFFER_LENGTH: int = 8192
|
||
|
MAX_MYSQL_TABLE_COLUMNS: int = 4096
|
||
|
PARAMETER_COUNT_AVAILABLE: int = 8
|
||
|
"""Flag used to send the Query Attributes with 0 (or more) parameters."""
|
||
|
MYSQL_VECTOR_TYPE_CODE = "f"
|
||
|
"""Expected `typecode` when decoding VECTOR values from
|
||
|
MySQL (blob) to Python (array.array).
|
||
|
"""
|
||
|
MYSQL_DEFAULT_CHARSET_ID_57 = 45
|
||
|
MYSQL_DEFAULT_CHARSET_ID_80 = 255
|
||
|
|
||
|
DEFAULT_CONFIGURATION: Dict[str, Optional[Union[str, bool, int]]] = {
|
||
|
"database": None,
|
||
|
"user": "",
|
||
|
"password": "",
|
||
|
"password1": "",
|
||
|
"password2": "",
|
||
|
"password3": "",
|
||
|
"host": "127.0.0.1",
|
||
|
"port": 3306,
|
||
|
"unix_socket": None,
|
||
|
"use_unicode": True,
|
||
|
"charset": "utf8mb4",
|
||
|
"collation": None,
|
||
|
"converter_class": None,
|
||
|
"converter_str_fallback": False,
|
||
|
"autocommit": False,
|
||
|
"time_zone": None,
|
||
|
"sql_mode": None,
|
||
|
"get_warnings": False,
|
||
|
"raise_on_warnings": False,
|
||
|
"connection_timeout": None,
|
||
|
"client_flags": 0,
|
||
|
"compress": False,
|
||
|
"buffered": False,
|
||
|
"raw": False,
|
||
|
"ssl_ca": None,
|
||
|
"ssl_cert": None,
|
||
|
"ssl_key": None,
|
||
|
"ssl_verify_cert": False,
|
||
|
"ssl_verify_identity": False,
|
||
|
"ssl_cipher": None,
|
||
|
"tls_ciphersuites": None,
|
||
|
"ssl_disabled": False,
|
||
|
"tls_versions": None,
|
||
|
"passwd": None,
|
||
|
"db": None,
|
||
|
"connect_timeout": None,
|
||
|
"dsn": None,
|
||
|
"force_ipv6": False,
|
||
|
"auth_plugin": None,
|
||
|
"allow_local_infile": False,
|
||
|
"allow_local_infile_in_path": None,
|
||
|
"consume_results": False,
|
||
|
"conn_attrs": None,
|
||
|
"dns_srv": False,
|
||
|
"use_pure": False,
|
||
|
"krb_service_principal": None,
|
||
|
"oci_config_file": None,
|
||
|
"oci_config_profile": None,
|
||
|
"webauthn_callback": None,
|
||
|
"kerberos_auth_mode": None,
|
||
|
"init_command": None,
|
||
|
}
|
||
|
|
||
|
CNX_POOL_ARGS: Tuple[str, str, str] = ("pool_name", "pool_size", "pool_reset_session")
|
||
|
|
||
|
CONN_ATTRS_DN: Tuple[str, ...] = (
|
||
|
"_pid",
|
||
|
"_platform",
|
||
|
"_source_host",
|
||
|
"_client_name",
|
||
|
"_client_license",
|
||
|
"_client_version",
|
||
|
"_os",
|
||
|
"_connector_name",
|
||
|
"_connector_license",
|
||
|
"_connector_version",
|
||
|
)
|
||
|
|
||
|
# List of deprecated cursor class names
|
||
|
DEPRECATED_CURSOR_CLASS_NAMES: Tuple[str, ...] = (
|
||
|
"MySQLCursorPreparedRaw",
|
||
|
"MySQLCursorNamedTuple",
|
||
|
"MySQLCursorBufferedNamedTuple",
|
||
|
"MySQLCursorPreparedNamedTuple",
|
||
|
"CMySQLCursorPreparedRaw",
|
||
|
"CMySQLCursorNamedTuple",
|
||
|
"CMySQLCursorBufferedNamedTuple",
|
||
|
"CMySQLCursorPreparedNamedTuple",
|
||
|
)
|
||
|
|
||
|
TLS_VERSIONS: List[str] = APPROVED_TLS_VERSIONS + DEPRECATED_TLS_VERSIONS
|
||
|
"""Accepted TLS versions. A warning is raised when using a deprecated version."""
|
||
|
|
||
|
# TLS v1.2 cipher suites IANI to OpenSSL name translation
|
||
|
TLSV1_2_CIPHER_SUITES: Dict[str, str] = {
|
||
|
"TLS_RSA_WITH_NULL_SHA256": "NULL-SHA256",
|
||
|
"TLS_RSA_WITH_AES_128_CBC_SHA256": "AES128-SHA256",
|
||
|
"TLS_RSA_WITH_AES_256_CBC_SHA256": "AES256-SHA256",
|
||
|
"TLS_RSA_WITH_AES_128_GCM_SHA256": "AES128-GCM-SHA256",
|
||
|
"TLS_RSA_WITH_AES_256_GCM_SHA384": "AES256-GCM-SHA384",
|
||
|
"TLS_DH_RSA_WITH_AES_128_CBC_SHA256": "DH-RSA-AES128-SHA256",
|
||
|
"TLS_DH_RSA_WITH_AES_256_CBC_SHA256": "DH-RSA-AES256-SHA256",
|
||
|
"TLS_DH_RSA_WITH_AES_128_GCM_SHA256": "DH-RSA-AES128-GCM-SHA256",
|
||
|
"TLS_DH_RSA_WITH_AES_256_GCM_SHA384": "DH-RSA-AES256-GCM-SHA384",
|
||
|
"TLS_DH_DSS_WITH_AES_128_CBC_SHA256": "DH-DSS-AES128-SHA256",
|
||
|
"TLS_DH_DSS_WITH_AES_256_CBC_SHA256": "DH-DSS-AES256-SHA256",
|
||
|
"TLS_DH_DSS_WITH_AES_128_GCM_SHA256": "DH-DSS-AES128-GCM-SHA256",
|
||
|
"TLS_DH_DSS_WITH_AES_256_GCM_SHA384": "DH-DSS-AES256-GCM-SHA384",
|
||
|
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256": "DHE-RSA-AES128-SHA256",
|
||
|
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256": "DHE-RSA-AES256-SHA256",
|
||
|
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256": "DHE-RSA-AES128-GCM-SHA256",
|
||
|
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384": "DHE-RSA-AES256-GCM-SHA384",
|
||
|
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA256": "DHE-DSS-AES128-SHA256",
|
||
|
"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256": "DHE-DSS-AES256-SHA256",
|
||
|
"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256": "DHE-DSS-AES128-GCM-SHA256",
|
||
|
"TLS_DHE_DSS_WITH_AES_256_GCM_SHA384": "DHE-DSS-AES256-GCM-SHA384",
|
||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": "ECDHE-RSA-AES128-SHA256",
|
||
|
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384": "ECDHE-RSA-AES256-SHA384",
|
||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": "ECDHE-RSA-AES128-GCM-SHA256",
|
||
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": "ECDHE-RSA-AES256-GCM-SHA384",
|
||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": "ECDHE-ECDSA-AES128-SHA256",
|
||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384": "ECDHE-ECDSA-AES256-SHA384",
|
||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": "ECDHE-ECDSA-AES128-GCM-SHA256",
|
||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": "ECDHE-ECDSA-AES256-GCM-SHA384",
|
||
|
"TLS_DH_anon_WITH_AES_128_CBC_SHA256": "ADH-AES128-SHA256",
|
||
|
"TLS_DH_anon_WITH_AES_256_CBC_SHA256": "ADH-AES256-SHA256",
|
||
|
"TLS_DH_anon_WITH_AES_128_GCM_SHA256": "ADH-AES128-GCM-SHA256",
|
||
|
"TLS_DH_anon_WITH_AES_256_GCM_SHA384": "ADH-AES256-GCM-SHA384",
|
||
|
"RSA_WITH_AES_128_CCM": "AES128-CCM",
|
||
|
"RSA_WITH_AES_256_CCM": "AES256-CCM",
|
||
|
"DHE_RSA_WITH_AES_128_CCM": "DHE-RSA-AES128-CCM",
|
||
|
"DHE_RSA_WITH_AES_256_CCM": "DHE-RSA-AES256-CCM",
|
||
|
"RSA_WITH_AES_128_CCM_8": "AES128-CCM8",
|
||
|
"RSA_WITH_AES_256_CCM_8": "AES256-CCM8",
|
||
|
"DHE_RSA_WITH_AES_128_CCM_8": "DHE-RSA-AES128-CCM8",
|
||
|
"DHE_RSA_WITH_AES_256_CCM_8": "DHE-RSA-AES256-CCM8",
|
||
|
"ECDHE_ECDSA_WITH_AES_128_CCM": "ECDHE-ECDSA-AES128-CCM",
|
||
|
"ECDHE_ECDSA_WITH_AES_256_CCM": "ECDHE-ECDSA-AES256-CCM",
|
||
|
"ECDHE_ECDSA_WITH_AES_128_CCM_8": "ECDHE-ECDSA-AES128-CCM8",
|
||
|
"ECDHE_ECDSA_WITH_AES_256_CCM_8": "ECDHE-ECDSA-AES256-CCM8",
|
||
|
# ARIA cipher suites from RFC6209, extending TLS v1.2
|
||
|
"TLS_RSA_WITH_ARIA_128_GCM_SHA256": "ARIA128-GCM-SHA256",
|
||
|
"TLS_RSA_WITH_ARIA_256_GCM_SHA384": "ARIA256-GCM-SHA384",
|
||
|
"TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256": "DHE-RSA-ARIA128-GCM-SHA256",
|
||
|
"TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384": "DHE-RSA-ARIA256-GCM-SHA384",
|
||
|
"TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256": "DHE-DSS-ARIA128-GCM-SHA256",
|
||
|
"TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384": "DHE-DSS-ARIA256-GCM-SHA384",
|
||
|
"TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256": "ECDHE-ECDSA-ARIA128-GCM-SHA256",
|
||
|
"TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384": "ECDHE-ECDSA-ARIA256-GCM-SHA384",
|
||
|
"TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256": "ECDHE-ARIA128-GCM-SHA256",
|
||
|
"TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384": "ECDHE-ARIA256-GCM-SHA384",
|
||
|
"TLS_PSK_WITH_ARIA_128_GCM_SHA256": "PSK-ARIA128-GCM-SHA256",
|
||
|
"TLS_PSK_WITH_ARIA_256_GCM_SHA384": "PSK-ARIA256-GCM-SHA384",
|
||
|
"TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256": "DHE-PSK-ARIA128-GCM-SHA256",
|
||
|
"TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384": "DHE-PSK-ARIA256-GCM-SHA384",
|
||
|
"TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256": "RSA-PSK-ARIA128-GCM-SHA256",
|
||
|
"TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384": "RSA-PSK-ARIA256-GCM-SHA384",
|
||
|
# Camellia HMAC-Based cipher suites from RFC6367, extending TLS v1.2
|
||
|
"TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256": "ECDHE-ECDSA-CAMELLIA128-SHA256",
|
||
|
"TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384": "ECDHE-ECDSA-CAMELLIA256-SHA384",
|
||
|
"TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256": "ECDHE-RSA-CAMELLIA128-SHA256",
|
||
|
"TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384": "ECDHE-RSA-CAMELLIA256-SHA384",
|
||
|
# Pre-shared keying (PSK) cipher suites",
|
||
|
"PSK_WITH_NULL_SHA": "PSK-NULL-SHA",
|
||
|
"DHE_PSK_WITH_NULL_SHA": "DHE-PSK-NULL-SHA",
|
||
|
"RSA_PSK_WITH_NULL_SHA": "RSA-PSK-NULL-SHA",
|
||
|
"PSK_WITH_RC4_128_SHA": "PSK-RC4-SHA",
|
||
|
"PSK_WITH_3DES_EDE_CBC_SHA": "PSK-3DES-EDE-CBC-SHA",
|
||
|
"PSK_WITH_AES_128_CBC_SHA": "PSK-AES128-CBC-SHA",
|
||
|
"PSK_WITH_AES_256_CBC_SHA": "PSK-AES256-CBC-SHA",
|
||
|
"DHE_PSK_WITH_RC4_128_SHA": "DHE-PSK-RC4-SHA",
|
||
|
"DHE_PSK_WITH_3DES_EDE_CBC_SHA": "DHE-PSK-3DES-EDE-CBC-SHA",
|
||
|
"DHE_PSK_WITH_AES_128_CBC_SHA": "DHE-PSK-AES128-CBC-SHA",
|
||
|
"DHE_PSK_WITH_AES_256_CBC_SHA": "DHE-PSK-AES256-CBC-SHA",
|
||
|
"RSA_PSK_WITH_RC4_128_SHA": "RSA-PSK-RC4-SHA",
|
||
|
"RSA_PSK_WITH_3DES_EDE_CBC_SHA": "RSA-PSK-3DES-EDE-CBC-SHA",
|
||
|
"RSA_PSK_WITH_AES_128_CBC_SHA": "RSA-PSK-AES128-CBC-SHA",
|
||
|
"RSA_PSK_WITH_AES_256_CBC_SHA": "RSA-PSK-AES256-CBC-SHA",
|
||
|
"PSK_WITH_AES_128_GCM_SHA256": "PSK-AES128-GCM-SHA256",
|
||
|
"PSK_WITH_AES_256_GCM_SHA384": "PSK-AES256-GCM-SHA384",
|
||
|
"DHE_PSK_WITH_AES_128_GCM_SHA256": "DHE-PSK-AES128-GCM-SHA256",
|
||
|
"DHE_PSK_WITH_AES_256_GCM_SHA384": "DHE-PSK-AES256-GCM-SHA384",
|
||
|
"RSA_PSK_WITH_AES_128_GCM_SHA256": "RSA-PSK-AES128-GCM-SHA256",
|
||
|
"RSA_PSK_WITH_AES_256_GCM_SHA384": "RSA-PSK-AES256-GCM-SHA384",
|
||
|
"PSK_WITH_AES_128_CBC_SHA256": "PSK-AES128-CBC-SHA256",
|
||
|
"PSK_WITH_AES_256_CBC_SHA384": "PSK-AES256-CBC-SHA384",
|
||
|
"PSK_WITH_NULL_SHA256": "PSK-NULL-SHA256",
|
||
|
"PSK_WITH_NULL_SHA384": "PSK-NULL-SHA384",
|
||
|
"DHE_PSK_WITH_AES_128_CBC_SHA256": "DHE-PSK-AES128-CBC-SHA256",
|
||
|
"DHE_PSK_WITH_AES_256_CBC_SHA384": "DHE-PSK-AES256-CBC-SHA384",
|
||
|
"DHE_PSK_WITH_NULL_SHA256": "DHE-PSK-NULL-SHA256",
|
||
|
"DHE_PSK_WITH_NULL_SHA384": "DHE-PSK-NULL-SHA384",
|
||
|
"RSA_PSK_WITH_AES_128_CBC_SHA256": "RSA-PSK-AES128-CBC-SHA256",
|
||
|
"RSA_PSK_WITH_AES_256_CBC_SHA384": "RSA-PSK-AES256-CBC-SHA384",
|
||
|
"RSA_PSK_WITH_NULL_SHA256": "RSA-PSK-NULL-SHA256",
|
||
|
"RSA_PSK_WITH_NULL_SHA384": "RSA-PSK-NULL-SHA384",
|
||
|
"ECDHE_PSK_WITH_RC4_128_SHA": "ECDHE-PSK-RC4-SHA",
|
||
|
"ECDHE_PSK_WITH_3DES_EDE_CBC_SHA": "ECDHE-PSK-3DES-EDE-CBC-SHA",
|
||
|
"ECDHE_PSK_WITH_AES_128_CBC_SHA": "ECDHE-PSK-AES128-CBC-SHA",
|
||
|
"ECDHE_PSK_WITH_AES_256_CBC_SHA": "ECDHE-PSK-AES256-CBC-SHA",
|
||
|
"ECDHE_PSK_WITH_AES_128_CBC_SHA256": "ECDHE-PSK-AES128-CBC-SHA256",
|
||
|
"ECDHE_PSK_WITH_AES_256_CBC_SHA384": "ECDHE-PSK-AES256-CBC-SHA384",
|
||
|
"ECDHE_PSK_WITH_NULL_SHA": "ECDHE-PSK-NULL-SHA",
|
||
|
"ECDHE_PSK_WITH_NULL_SHA256": "ECDHE-PSK-NULL-SHA256",
|
||
|
"ECDHE_PSK_WITH_NULL_SHA384": "ECDHE-PSK-NULL-SHA384",
|
||
|
"PSK_WITH_CAMELLIA_128_CBC_SHA256": "PSK-CAMELLIA128-SHA256",
|
||
|
"PSK_WITH_CAMELLIA_256_CBC_SHA384": "PSK-CAMELLIA256-SHA384",
|
||
|
"DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256": "DHE-PSK-CAMELLIA128-SHA256",
|
||
|
"DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384": "DHE-PSK-CAMELLIA256-SHA384",
|
||
|
"RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256": "RSA-PSK-CAMELLIA128-SHA256",
|
||
|
"RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384": "RSA-PSK-CAMELLIA256-SHA384",
|
||
|
"ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256": "ECDHE-PSK-CAMELLIA128-SHA256",
|
||
|
"ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384": "ECDHE-PSK-CAMELLIA256-SHA384",
|
||
|
"PSK_WITH_AES_128_CCM": "PSK-AES128-CCM",
|
||
|
"PSK_WITH_AES_256_CCM": "PSK-AES256-CCM",
|
||
|
"DHE_PSK_WITH_AES_128_CCM": "DHE-PSK-AES128-CCM",
|
||
|
"DHE_PSK_WITH_AES_256_CCM": "DHE-PSK-AES256-CCM",
|
||
|
"PSK_WITH_AES_128_CCM_8": "PSK-AES128-CCM8",
|
||
|
"PSK_WITH_AES_256_CCM_8": "PSK-AES256-CCM8",
|
||
|
"DHE_PSK_WITH_AES_128_CCM_8": "DHE-PSK-AES128-CCM8",
|
||
|
"DHE_PSK_WITH_AES_256_CCM_8": "DHE-PSK-AES256-CCM8",
|
||
|
# ChaCha20-Poly1305 cipher suites, extending TLS v1.2
|
||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": "ECDHE-RSA-CHACHA20-POLY1305",
|
||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": "ECDHE-ECDSA-CHACHA20-POLY1305",
|
||
|
"TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256": "DHE-RSA-CHACHA20-POLY1305",
|
||
|
"TLS_PSK_WITH_CHACHA20_POLY1305_SHA256": "PSK-CHACHA20-POLY1305",
|
||
|
"TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256": "ECDHE-PSK-CHACHA20-POLY1305",
|
||
|
"TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256": "DHE-PSK-CHACHA20-POLY1305",
|
||
|
"TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256": "RSA-PSK-CHACHA20-POLY1305",
|
||
|
}
|
||
|
|
||
|
# TLS v1.3 cipher suites IANI to OpenSSL name translation
|
||
|
TLSV1_3_CIPHER_SUITES: Dict[str, str] = {
|
||
|
"TLS_AES_128_GCM_SHA256": "TLS_AES_128_GCM_SHA256",
|
||
|
"TLS_AES_256_GCM_SHA384": "TLS_AES_256_GCM_SHA384",
|
||
|
"TLS_CHACHA20_POLY1305_SHA256": "TLS_CHACHA20_POLY1305_SHA256",
|
||
|
"TLS_AES_128_CCM_SHA256": "TLS_AES_128_CCM_SHA256",
|
||
|
"TLS_AES_128_CCM_8_SHA256": "TLS_AES_128_CCM_8_SHA256",
|
||
|
}
|
||
|
|
||
|
TLS_CIPHER_SUITES: Dict[str, Dict[str, str]] = {
|
||
|
"TLSv1.2": TLSV1_2_CIPHER_SUITES,
|
||
|
"TLSv1.3": TLSV1_3_CIPHER_SUITES,
|
||
|
}
|
||
|
|
||
|
OPENSSL_CS_NAMES: Dict[str, ValuesView[str]] = {
|
||
|
"TLSv1.2": TLSV1_2_CIPHER_SUITES.values(),
|
||
|
"TLSv1.3": TLSV1_3_CIPHER_SUITES.values(),
|
||
|
}
|
||
|
|
||
|
|
||
|
def flag_is_set(flag: int, flags: int) -> bool:
|
||
|
"""Checks if the flag is set
|
||
|
|
||
|
Returns boolean"""
|
||
|
if (flags & flag) > 0:
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
|
||
|
def _obsolete_option(name: str, new_name: str, value: int) -> int:
|
||
|
"""Raise a deprecation warning and advise a new option name.
|
||
|
|
||
|
Args:
|
||
|
name (str): The name of the option.
|
||
|
new_name (str): The new option name.
|
||
|
value (int): The value of the option.
|
||
|
|
||
|
Returns:
|
||
|
int: The value of the option.
|
||
|
"""
|
||
|
warnings.warn(
|
||
|
f"The option '{name}' has been deprecated, use '{new_name}' instead.",
|
||
|
category=DeprecationWarning,
|
||
|
)
|
||
|
return value
|
||
|
|
||
|
|
||
|
def raise_warning_against_deprecated_cursor_class(
|
||
|
cursor_name: str,
|
||
|
) -> None:
|
||
|
"""Raise a deprecation warning if the cursor class name matches against
|
||
|
any of DEPRECATED_CURSOR_CLASS_NAMES list.
|
||
|
|
||
|
Args:
|
||
|
cursor_name (str): cursor class name to be checked against
|
||
|
DEPRECATED_CURSOR_CLASS_NAMES list.
|
||
|
Returns:
|
||
|
None
|
||
|
"""
|
||
|
if cursor_name in DEPRECATED_CURSOR_CLASS_NAMES:
|
||
|
warnings.warn(
|
||
|
message=f"WARNING: Cursor type {cursor_name} is now "
|
||
|
"deprecated and will be removed in a future release of "
|
||
|
"MySQL Connector/Python.",
|
||
|
category=DeprecationWarning,
|
||
|
stacklevel=3,
|
||
|
)
|
||
|
|
||
|
|
||
|
class _Constants(ABC):
|
||
|
"""Base class for constants."""
|
||
|
|
||
|
prefix: str = ""
|
||
|
desc: Dict[str, Tuple[int, str]] = {}
|
||
|
|
||
|
@classmethod
|
||
|
def get_desc(cls, name: str) -> Optional[str]:
|
||
|
"""Get description of given constant"""
|
||
|
try:
|
||
|
return cls.desc[name][1]
|
||
|
except (IndexError, KeyError):
|
||
|
return None
|
||
|
|
||
|
@classmethod
|
||
|
def get_info(cls, setid: int) -> Union[Optional[str], Tuple[str, str]]:
|
||
|
"""Get information about given constant"""
|
||
|
for name, info in cls.desc.items():
|
||
|
if info[0] == setid:
|
||
|
return name
|
||
|
return None
|
||
|
|
||
|
@classmethod
|
||
|
def get_full_info(cls) -> Union[str, Sequence[str]]:
|
||
|
"""get full information about given constant"""
|
||
|
res: Union[str, List[str]] = []
|
||
|
try:
|
||
|
res = [f"{k} : {v[1]}" for k, v in cls.desc.items()]
|
||
|
except (AttributeError, IndexError) as err:
|
||
|
res = f"No information found in constant class. {err}"
|
||
|
|
||
|
return res
|
||
|
|
||
|
|
||
|
class _Flags(_Constants):
|
||
|
"""Base class for classes describing flags"""
|
||
|
|
||
|
@classmethod
|
||
|
def get_bit_info(cls, value: int) -> List[str]:
|
||
|
"""Get the name of all bits set
|
||
|
|
||
|
Returns a list of strings."""
|
||
|
res = []
|
||
|
for name, info in cls.desc.items():
|
||
|
if value & info[0]:
|
||
|
res.append(name)
|
||
|
return res
|
||
|
|
||
|
|
||
|
class FieldType(_Constants):
|
||
|
"""MySQL Field Types.
|
||
|
|
||
|
This class provides all supported MySQL field or data types. They can be useful
|
||
|
when dealing with raw data or defining your own converters. The field type is
|
||
|
stored with every cursor in the description for each column.
|
||
|
|
||
|
The `FieldType` class shouldn't be instantiated.
|
||
|
|
||
|
Examples:
|
||
|
The following example shows how to print the name of the data type for
|
||
|
each column in a result set.
|
||
|
|
||
|
```
|
||
|
from __future__ import print_function
|
||
|
import mysql.connector
|
||
|
from mysql.connector import FieldType
|
||
|
|
||
|
cnx = mysql.connector.connect(user='scott', database='test')
|
||
|
cursor = cnx.cursor()
|
||
|
|
||
|
cursor.execute(
|
||
|
"SELECT DATE(NOW()) AS `c1`, TIME(NOW()) AS `c2`, "
|
||
|
"NOW() AS `c3`, 'a string' AS `c4`, 42 AS `c5`")
|
||
|
rows = cursor.fetchall()
|
||
|
|
||
|
for desc in cursor.description:
|
||
|
colname = desc[0]
|
||
|
coltype = desc[1]
|
||
|
print("Column {} has type {}".format(
|
||
|
colname, FieldType.get_info(coltype)))
|
||
|
|
||
|
cursor.close()
|
||
|
cnx.close()
|
||
|
```
|
||
|
"""
|
||
|
|
||
|
prefix: str = "FIELD_TYPE_"
|
||
|
DECIMAL: int = 0x00
|
||
|
TINY: int = 0x01
|
||
|
SHORT: int = 0x02
|
||
|
LONG: int = 0x03
|
||
|
FLOAT: int = 0x04
|
||
|
DOUBLE: int = 0x05
|
||
|
NULL: int = 0x06
|
||
|
TIMESTAMP: int = 0x07
|
||
|
LONGLONG: int = 0x08
|
||
|
INT24: int = 0x09
|
||
|
DATE: int = 0x0A
|
||
|
TIME: int = 0x0B
|
||
|
DATETIME: int = 0x0C
|
||
|
YEAR: int = 0x0D
|
||
|
NEWDATE: int = 0x0E
|
||
|
VARCHAR: int = 0x0F
|
||
|
BIT: int = 0x10
|
||
|
VECTOR: int = 0xF2
|
||
|
JSON: int = 0xF5
|
||
|
NEWDECIMAL: int = 0xF6
|
||
|
ENUM: int = 0xF7
|
||
|
SET: int = 0xF8
|
||
|
TINY_BLOB: int = 0xF9
|
||
|
MEDIUM_BLOB: int = 0xFA
|
||
|
LONG_BLOB: int = 0xFB
|
||
|
BLOB: int = 0xFC
|
||
|
VAR_STRING: int = 0xFD
|
||
|
STRING: int = 0xFE
|
||
|
GEOMETRY: int = 0xFF
|
||
|
|
||
|
desc: Dict[str, Tuple[int, str]] = {
|
||
|
"DECIMAL": (DECIMAL, "DECIMAL"),
|
||
|
"TINY": (TINY, "TINY"),
|
||
|
"SHORT": (SHORT, "SHORT"),
|
||
|
"LONG": (LONG, "LONG"),
|
||
|
"FLOAT": (FLOAT, "FLOAT"),
|
||
|
"DOUBLE": (DOUBLE, "DOUBLE"),
|
||
|
"NULL": (NULL, "NULL"),
|
||
|
"TIMESTAMP": (TIMESTAMP, "TIMESTAMP"),
|
||
|
"LONGLONG": (LONGLONG, "LONGLONG"),
|
||
|
"INT24": (INT24, "INT24"),
|
||
|
"DATE": (DATE, "DATE"),
|
||
|
"TIME": (TIME, "TIME"),
|
||
|
"DATETIME": (DATETIME, "DATETIME"),
|
||
|
"YEAR": (YEAR, "YEAR"),
|
||
|
"NEWDATE": (NEWDATE, "NEWDATE"),
|
||
|
"VARCHAR": (VARCHAR, "VARCHAR"),
|
||
|
"BIT": (BIT, "BIT"),
|
||
|
"VECTOR": (VECTOR, "VECTOR"),
|
||
|
"JSON": (JSON, "JSON"),
|
||
|
"NEWDECIMAL": (NEWDECIMAL, "NEWDECIMAL"),
|
||
|
"ENUM": (ENUM, "ENUM"),
|
||
|
"SET": (SET, "SET"),
|
||
|
"TINY_BLOB": (TINY_BLOB, "TINY_BLOB"),
|
||
|
"MEDIUM_BLOB": (MEDIUM_BLOB, "MEDIUM_BLOB"),
|
||
|
"LONG_BLOB": (LONG_BLOB, "LONG_BLOB"),
|
||
|
"BLOB": (BLOB, "BLOB"),
|
||
|
"VAR_STRING": (VAR_STRING, "VAR_STRING"),
|
||
|
"STRING": (STRING, "STRING"),
|
||
|
"GEOMETRY": (GEOMETRY, "GEOMETRY"),
|
||
|
}
|
||
|
|
||
|
@classmethod
|
||
|
def get_string_types(cls) -> List[int]:
|
||
|
"""Get the list of all string types"""
|
||
|
return [
|
||
|
cls.VARCHAR,
|
||
|
cls.ENUM,
|
||
|
cls.VAR_STRING,
|
||
|
cls.STRING,
|
||
|
]
|
||
|
|
||
|
@classmethod
|
||
|
def get_binary_types(cls) -> List[int]:
|
||
|
"""Get the list of all binary types"""
|
||
|
return [
|
||
|
cls.TINY_BLOB,
|
||
|
cls.MEDIUM_BLOB,
|
||
|
cls.LONG_BLOB,
|
||
|
cls.BLOB,
|
||
|
]
|
||
|
|
||
|
@classmethod
|
||
|
def get_number_types(cls) -> List[int]:
|
||
|
"""Get the list of all number types"""
|
||
|
return [
|
||
|
cls.DECIMAL,
|
||
|
cls.NEWDECIMAL,
|
||
|
cls.TINY,
|
||
|
cls.SHORT,
|
||
|
cls.LONG,
|
||
|
cls.FLOAT,
|
||
|
cls.DOUBLE,
|
||
|
cls.LONGLONG,
|
||
|
cls.INT24,
|
||
|
cls.BIT,
|
||
|
cls.YEAR,
|
||
|
]
|
||
|
|
||
|
@classmethod
|
||
|
def get_timestamp_types(cls) -> List[int]:
|
||
|
"""Get the list of all timestamp types"""
|
||
|
return [
|
||
|
cls.DATETIME,
|
||
|
cls.TIMESTAMP,
|
||
|
]
|
||
|
|
||
|
|
||
|
class FieldFlag(_Flags):
|
||
|
"""MySQL Field Flags
|
||
|
|
||
|
Field flags as found in MySQL sources mysql-src/include/mysql_com.h
|
||
|
"""
|
||
|
|
||
|
_prefix: str = ""
|
||
|
NOT_NULL: int = 1 << 0
|
||
|
PRI_KEY: int = 1 << 1
|
||
|
UNIQUE_KEY: int = 1 << 2
|
||
|
MULTIPLE_KEY: int = 1 << 3
|
||
|
BLOB: int = 1 << 4
|
||
|
UNSIGNED: int = 1 << 5
|
||
|
ZEROFILL: int = 1 << 6
|
||
|
BINARY: int = 1 << 7
|
||
|
|
||
|
ENUM: int = 1 << 8
|
||
|
AUTO_INCREMENT: int = 1 << 9
|
||
|
TIMESTAMP: int = 1 << 10
|
||
|
SET: int = 1 << 11
|
||
|
|
||
|
NO_DEFAULT_VALUE: int = 1 << 12
|
||
|
ON_UPDATE_NOW: int = 1 << 13
|
||
|
NUM: int = 1 << 14
|
||
|
PART_KEY: int = 1 << 15
|
||
|
GROUP: int = 1 << 14 # SAME AS NUM !!!!!!!????
|
||
|
UNIQUE: int = 1 << 16
|
||
|
BINCMP: int = 1 << 17
|
||
|
|
||
|
GET_FIXED_FIELDS: int = 1 << 18
|
||
|
FIELD_IN_PART_FUNC: int = 1 << 19
|
||
|
FIELD_IN_ADD_INDEX: int = 1 << 20
|
||
|
FIELD_IS_RENAMED: int = 1 << 21
|
||
|
|
||
|
desc: Dict[str, Tuple[int, str]] = {
|
||
|
"NOT_NULL": (1 << 0, "Field can't be NULL"),
|
||
|
"PRI_KEY": (1 << 1, "Field is part of a primary key"),
|
||
|
"UNIQUE_KEY": (1 << 2, "Field is part of a unique key"),
|
||
|
"MULTIPLE_KEY": (1 << 3, "Field is part of a key"),
|
||
|
"BLOB": (1 << 4, "Field is a blob"),
|
||
|
"UNSIGNED": (1 << 5, "Field is unsigned"),
|
||
|
"ZEROFILL": (1 << 6, "Field is zerofill"),
|
||
|
"BINARY": (1 << 7, "Field is binary "),
|
||
|
"ENUM": (1 << 8, "field is an enum"),
|
||
|
"AUTO_INCREMENT": (1 << 9, "field is a autoincrement field"),
|
||
|
"TIMESTAMP": (1 << 10, "Field is a timestamp"),
|
||
|
"SET": (1 << 11, "field is a set"),
|
||
|
"NO_DEFAULT_VALUE": (1 << 12, "Field doesn't have default value"),
|
||
|
"ON_UPDATE_NOW": (1 << 13, "Field is set to NOW on UPDATE"),
|
||
|
"NUM": (1 << 14, "Field is num (for clients)"),
|
||
|
"PART_KEY": (1 << 15, "Intern; Part of some key"),
|
||
|
"GROUP": (1 << 14, "Intern: Group field"), # Same as NUM
|
||
|
"UNIQUE": (1 << 16, "Intern: Used by sql_yacc"),
|
||
|
"BINCMP": (1 << 17, "Intern: Used by sql_yacc"),
|
||
|
"GET_FIXED_FIELDS": (1 << 18, "Used to get fields in item tree"),
|
||
|
"FIELD_IN_PART_FUNC": (1 << 19, "Field part of partition func"),
|
||
|
"FIELD_IN_ADD_INDEX": (1 << 20, "Intern: Field used in ADD INDEX"),
|
||
|
"FIELD_IS_RENAMED": (1 << 21, "Intern: Field is being renamed"),
|
||
|
}
|
||
|
|
||
|
|
||
|
class ServerCmdMeta(ABCMeta):
|
||
|
"""ClientFlag Metaclass."""
|
||
|
|
||
|
def __getattribute__(cls, name: str) -> Any:
|
||
|
deprecated_options = (
|
||
|
"FIELD_LIST",
|
||
|
"REFRESH",
|
||
|
"SHUTDOWN",
|
||
|
"PROCESS_INFO",
|
||
|
"PROCESS_KILL",
|
||
|
)
|
||
|
if name in deprecated_options:
|
||
|
warnings.warn(
|
||
|
f"The option 'ServerCmd.{name}' is deprecated and will be removed in "
|
||
|
"a future release.",
|
||
|
category=DeprecationWarning,
|
||
|
)
|
||
|
return super().__getattribute__(name)
|
||
|
|
||
|
|
||
|
class ServerCmd(_Constants, metaclass=ServerCmdMeta):
|
||
|
"""MySQL Server Commands"""
|
||
|
|
||
|
_prefix: str = "COM_"
|
||
|
SLEEP: int = 0
|
||
|
QUIT: int = 1
|
||
|
INIT_DB: int = 2
|
||
|
QUERY: int = 3
|
||
|
FIELD_LIST: int = 4
|
||
|
CREATE_DB: int = 5
|
||
|
DROP_DB: int = 6
|
||
|
REFRESH: int = 7
|
||
|
SHUTDOWN: int = 8
|
||
|
STATISTICS: int = 9
|
||
|
PROCESS_INFO: int = 10
|
||
|
CONNECT: int = 11
|
||
|
PROCESS_KILL: int = 12
|
||
|
DEBUG: int = 13
|
||
|
PING: int = 14
|
||
|
TIME: int = 15
|
||
|
DELAYED_INSERT: int = 16
|
||
|
CHANGE_USER: int = 17
|
||
|
BINLOG_DUMP: int = 18
|
||
|
TABLE_DUMP: int = 19
|
||
|
CONNECT_OUT: int = 20
|
||
|
REGISTER_REPLICA: int = 21
|
||
|
STMT_PREPARE: int = 22
|
||
|
STMT_EXECUTE: int = 23
|
||
|
STMT_SEND_LONG_DATA: int = 24
|
||
|
STMT_CLOSE: int = 25
|
||
|
STMT_RESET: int = 26
|
||
|
SET_OPTION: int = 27
|
||
|
STMT_FETCH: int = 28
|
||
|
DAEMON: int = 29
|
||
|
BINLOG_DUMP_GTID: int = 30
|
||
|
RESET_CONNECTION: int = 31
|
||
|
|
||
|
desc: Dict[str, Tuple[int, str]] = {
|
||
|
"SLEEP": (0, "SLEEP"),
|
||
|
"QUIT": (1, "QUIT"),
|
||
|
"INIT_DB": (2, "INIT_DB"),
|
||
|
"QUERY": (3, "QUERY"),
|
||
|
"FIELD_LIST": (4, "FIELD_LIST"),
|
||
|
"CREATE_DB": (5, "CREATE_DB"),
|
||
|
"DROP_DB": (6, "DROP_DB"),
|
||
|
"REFRESH": (7, "REFRESH"),
|
||
|
"SHUTDOWN": (8, "SHUTDOWN"),
|
||
|
"STATISTICS": (9, "STATISTICS"),
|
||
|
"PROCESS_INFO": (10, "PROCESS_INFO"),
|
||
|
"CONNECT": (11, "CONNECT"),
|
||
|
"PROCESS_KILL": (12, "PROCESS_KILL"),
|
||
|
"DEBUG": (13, "DEBUG"),
|
||
|
"PING": (14, "PING"),
|
||
|
"TIME": (15, "TIME"),
|
||
|
"DELAYED_INSERT": (16, "DELAYED_INSERT"),
|
||
|
"CHANGE_USER": (17, "CHANGE_USER"),
|
||
|
"BINLOG_DUMP": (18, "BINLOG_DUMP"),
|
||
|
"TABLE_DUMP": (19, "TABLE_DUMP"),
|
||
|
"CONNECT_OUT": (20, "CONNECT_OUT"),
|
||
|
"REGISTER_REPLICA": (21, "REGISTER_REPLICA"),
|
||
|
"STMT_PREPARE": (22, "STMT_PREPARE"),
|
||
|
"STMT_EXECUTE": (23, "STMT_EXECUTE"),
|
||
|
"STMT_SEND_LONG_DATA": (24, "STMT_SEND_LONG_DATA"),
|
||
|
"STMT_CLOSE": (25, "STMT_CLOSE"),
|
||
|
"STMT_RESET": (26, "STMT_RESET"),
|
||
|
"SET_OPTION": (27, "SET_OPTION"),
|
||
|
"STMT_FETCH": (28, "STMT_FETCH"),
|
||
|
"DAEMON": (29, "DAEMON"),
|
||
|
"BINLOG_DUMP_GTID": (30, "BINLOG_DUMP_GTID"),
|
||
|
"RESET_CONNECTION": (31, "RESET_CONNECTION"),
|
||
|
}
|
||
|
|
||
|
|
||
|
class ClientFlag(_Flags):
|
||
|
"""MySQL Client Flags.
|
||
|
|
||
|
Client options as found in the MySQL sources mysql-src/include/mysql_com.h.
|
||
|
|
||
|
This class provides constants defining MySQL client flags that can be used
|
||
|
when the connection is established to configure the session. The `ClientFlag`
|
||
|
class is available when importing mysql.connector.
|
||
|
|
||
|
The `ClientFlag` class shouldn't be instantiated.
|
||
|
|
||
|
Examples:
|
||
|
```
|
||
|
>>> import mysql.connector
|
||
|
>>> mysql.connector.ClientFlag.FOUND_ROWS
|
||
|
2
|
||
|
```
|
||
|
"""
|
||
|
|
||
|
LONG_PASSWD: int = 1 << 0
|
||
|
FOUND_ROWS: int = 1 << 1
|
||
|
LONG_FLAG: int = 1 << 2
|
||
|
CONNECT_WITH_DB: int = 1 << 3
|
||
|
NO_SCHEMA: int = 1 << 4
|
||
|
COMPRESS: int = 1 << 5
|
||
|
ODBC: int = 1 << 6
|
||
|
LOCAL_FILES: int = 1 << 7
|
||
|
IGNORE_SPACE: int = 1 << 8
|
||
|
PROTOCOL_41: int = 1 << 9
|
||
|
INTERACTIVE: int = 1 << 10
|
||
|
SSL: int = 1 << 11
|
||
|
IGNORE_SIGPIPE: int = 1 << 12
|
||
|
TRANSACTIONS: int = 1 << 13
|
||
|
RESERVED: int = 1 << 14
|
||
|
SECURE_CONNECTION: int = 1 << 15
|
||
|
MULTI_STATEMENTS: int = 1 << 16
|
||
|
MULTI_RESULTS: int = 1 << 17
|
||
|
PS_MULTI_RESULTS: int = 1 << 18
|
||
|
PLUGIN_AUTH: int = 1 << 19
|
||
|
CONNECT_ARGS: int = 1 << 20
|
||
|
PLUGIN_AUTH_LENENC_CLIENT_DATA: int = 1 << 21
|
||
|
CAN_HANDLE_EXPIRED_PASSWORDS: int = 1 << 22
|
||
|
SESION_TRACK: int = 1 << 23 # deprecated
|
||
|
SESSION_TRACK: int = 1 << 23
|
||
|
DEPRECATE_EOF: int = 1 << 24
|
||
|
CLIENT_QUERY_ATTRIBUTES: int = 1 << 27
|
||
|
SSL_VERIFY_SERVER_CERT: int = 1 << 30
|
||
|
REMEMBER_OPTIONS: int = 1 << 31
|
||
|
MULTI_FACTOR_AUTHENTICATION: int = 1 << 28
|
||
|
|
||
|
desc: Dict[str, Tuple[int, str]] = {
|
||
|
"LONG_PASSWD": (1 << 0, "New more secure passwords"),
|
||
|
"FOUND_ROWS": (1 << 1, "Found instead of affected rows"),
|
||
|
"LONG_FLAG": (1 << 2, "Get all column flags"),
|
||
|
"CONNECT_WITH_DB": (1 << 3, "One can specify db on connect"),
|
||
|
"NO_SCHEMA": (1 << 4, "Don't allow database.table.column"),
|
||
|
"COMPRESS": (1 << 5, "Can use compression protocol"),
|
||
|
"ODBC": (1 << 6, "ODBC client"),
|
||
|
"LOCAL_FILES": (1 << 7, "Can use LOAD DATA LOCAL"),
|
||
|
"IGNORE_SPACE": (1 << 8, "Ignore spaces before ''"),
|
||
|
"PROTOCOL_41": (1 << 9, "New 4.1 protocol"),
|
||
|
"INTERACTIVE": (1 << 10, "This is an interactive client"),
|
||
|
"SSL": (1 << 11, "Switch to SSL after handshake"),
|
||
|
"IGNORE_SIGPIPE": (1 << 12, "IGNORE sigpipes"),
|
||
|
"TRANSACTIONS": (1 << 13, "Client knows about transactions"),
|
||
|
"RESERVED": (1 << 14, "Old flag for 4.1 protocol"),
|
||
|
"SECURE_CONNECTION": (1 << 15, "New 4.1 authentication"),
|
||
|
"MULTI_STATEMENTS": (1 << 16, "Enable/disable multi-stmt support"),
|
||
|
"MULTI_RESULTS": (1 << 17, "Enable/disable multi-results"),
|
||
|
"PS_MULTI_RESULTS": (1 << 18, "Multi-results in PS-protocol"),
|
||
|
"PLUGIN_AUTH": (1 << 19, "Client supports plugin authentication"),
|
||
|
"CONNECT_ARGS": (1 << 20, "Client supports connection attributes"),
|
||
|
"PLUGIN_AUTH_LENENC_CLIENT_DATA": (
|
||
|
1 << 21,
|
||
|
"Enable authentication response packet to be larger than 255 bytes",
|
||
|
),
|
||
|
"CAN_HANDLE_EXPIRED_PASSWORDS": (
|
||
|
1 << 22,
|
||
|
"Don't close the connection for a connection with expired password",
|
||
|
),
|
||
|
"SESION_TRACK": ( # deprecated
|
||
|
1 << 23,
|
||
|
"Capable of handling server state change information",
|
||
|
),
|
||
|
"SESSION_TRACK": (
|
||
|
1 << 23,
|
||
|
"Capable of handling server state change information",
|
||
|
),
|
||
|
"DEPRECATE_EOF": (1 << 24, "Client no longer needs EOF packet"),
|
||
|
"CLIENT_QUERY_ATTRIBUTES": (
|
||
|
1 << 27,
|
||
|
"Support optional extension for query parameters",
|
||
|
),
|
||
|
"SSL_VERIFY_SERVER_CERT": (1 << 30, ""),
|
||
|
"REMEMBER_OPTIONS": (1 << 31, ""),
|
||
|
}
|
||
|
|
||
|
default: List[int] = [
|
||
|
LONG_PASSWD,
|
||
|
LONG_FLAG,
|
||
|
CONNECT_WITH_DB,
|
||
|
PROTOCOL_41,
|
||
|
TRANSACTIONS,
|
||
|
SECURE_CONNECTION,
|
||
|
MULTI_STATEMENTS,
|
||
|
MULTI_RESULTS,
|
||
|
CONNECT_ARGS,
|
||
|
]
|
||
|
|
||
|
@classmethod
|
||
|
def get_default(cls) -> int:
|
||
|
"""Get the default client options set
|
||
|
|
||
|
Returns a flag with all the default client options set"""
|
||
|
flags = 0
|
||
|
for option in cls.default:
|
||
|
flags |= option
|
||
|
return flags
|
||
|
|
||
|
|
||
|
class ServerFlag(_Flags):
|
||
|
"""MySQL Server Flags
|
||
|
|
||
|
Server flags as found in the MySQL sources mysql-src/include/mysql_com.h
|
||
|
"""
|
||
|
|
||
|
_prefix: str = "SERVER_"
|
||
|
STATUS_IN_TRANS: int = 1 << 0
|
||
|
STATUS_AUTOCOMMIT: int = 1 << 1
|
||
|
MORE_RESULTS_EXISTS: int = 1 << 3
|
||
|
QUERY_NO_GOOD_INDEX_USED: int = 1 << 4
|
||
|
QUERY_NO_INDEX_USED: int = 1 << 5
|
||
|
STATUS_CURSOR_EXISTS: int = 1 << 6
|
||
|
STATUS_LAST_ROW_SENT: int = 1 << 7
|
||
|
STATUS_DB_DROPPED: int = 1 << 8
|
||
|
STATUS_NO_BACKSLASH_ESCAPES: int = 1 << 9
|
||
|
SERVER_STATUS_METADATA_CHANGED: int = 1 << 10
|
||
|
SERVER_QUERY_WAS_SLOW: int = 1 << 11
|
||
|
SERVER_PS_OUT_PARAMS: int = 1 << 12
|
||
|
SERVER_STATUS_IN_TRANS_READONLY: int = 1 << 13
|
||
|
SERVER_SESSION_STATE_CHANGED: int = 1 << 14
|
||
|
|
||
|
desc: Dict[str, Tuple[int, str]] = {
|
||
|
"SERVER_STATUS_IN_TRANS": (1 << 0, "Transaction has started"),
|
||
|
"SERVER_STATUS_AUTOCOMMIT": (1 << 1, "Server in auto_commit mode"),
|
||
|
"SERVER_MORE_RESULTS_EXISTS": (
|
||
|
1 << 3,
|
||
|
"Multi query - next query exists",
|
||
|
),
|
||
|
"SERVER_QUERY_NO_GOOD_INDEX_USED": (1 << 4, ""),
|
||
|
"SERVER_QUERY_NO_INDEX_USED": (1 << 5, ""),
|
||
|
"SERVER_STATUS_CURSOR_EXISTS": (
|
||
|
1 << 6,
|
||
|
"Set when server opened a read-only non-scrollable cursor for a query.",
|
||
|
),
|
||
|
"SERVER_STATUS_LAST_ROW_SENT": (
|
||
|
1 << 7,
|
||
|
"Set when a read-only cursor is exhausted",
|
||
|
),
|
||
|
"SERVER_STATUS_DB_DROPPED": (1 << 8, "A database was dropped"),
|
||
|
"SERVER_STATUS_NO_BACKSLASH_ESCAPES": (1 << 9, ""),
|
||
|
"SERVER_STATUS_METADATA_CHANGED": (
|
||
|
1024,
|
||
|
"Set if after a prepared statement "
|
||
|
"reprepare we discovered that the "
|
||
|
"new statement returns a different "
|
||
|
"number of result set columns.",
|
||
|
),
|
||
|
"SERVER_QUERY_WAS_SLOW": (2048, ""),
|
||
|
"SERVER_PS_OUT_PARAMS": (
|
||
|
4096,
|
||
|
"To mark ResultSet containing output parameter values.",
|
||
|
),
|
||
|
"SERVER_STATUS_IN_TRANS_READONLY": (
|
||
|
8192,
|
||
|
"Set if multi-statement transaction is a read-only transaction.",
|
||
|
),
|
||
|
"SERVER_SESSION_STATE_CHANGED": (
|
||
|
1 << 14,
|
||
|
"Session state has changed on the "
|
||
|
"server because of the execution of "
|
||
|
"the last statement",
|
||
|
),
|
||
|
}
|
||
|
|
||
|
|
||
|
class RefreshOptionMeta(ABCMeta):
|
||
|
"""RefreshOption Metaclass."""
|
||
|
|
||
|
@property
|
||
|
def SLAVE(self) -> int: # pylint: disable=bad-mcs-method-argument,invalid-name
|
||
|
"""Return the deprecated alias of RefreshOption.REPLICA.
|
||
|
|
||
|
Raises a warning about this attribute deprecation.
|
||
|
"""
|
||
|
return _obsolete_option(
|
||
|
"RefreshOption.SLAVE",
|
||
|
"RefreshOption.REPLICA",
|
||
|
RefreshOption.REPLICA,
|
||
|
)
|
||
|
|
||
|
|
||
|
class RefreshOption(_Constants, metaclass=RefreshOptionMeta):
|
||
|
"""MySQL Refresh command options.
|
||
|
|
||
|
Options used when sending the COM_REFRESH server command.
|
||
|
"""
|
||
|
|
||
|
_prefix: str = "REFRESH_"
|
||
|
GRANT: int = 1 << 0
|
||
|
LOG: int = 1 << 1
|
||
|
TABLES: int = 1 << 2
|
||
|
HOST: int = 1 << 3
|
||
|
STATUS: int = 1 << 4
|
||
|
REPLICA: int = 1 << 6
|
||
|
|
||
|
desc: Dict[str, Tuple[int, str]] = {
|
||
|
"GRANT": (1 << 0, "Refresh grant tables"),
|
||
|
"LOG": (1 << 1, "Start on new log file"),
|
||
|
"TABLES": (1 << 2, "close all tables"),
|
||
|
"HOST": (1 << 3, "Flush host cache"),
|
||
|
"STATUS": (1 << 4, "Flush status variables"),
|
||
|
"REPLICA": (1 << 6, "Reset source info and restart replica thread"),
|
||
|
"SLAVE": (1 << 6, "Deprecated option; use REPLICA instead."),
|
||
|
}
|
||
|
|
||
|
|
||
|
class ShutdownType(_Constants):
|
||
|
"""MySQL Shutdown types
|
||
|
|
||
|
Shutdown types used by the COM_SHUTDOWN server command.
|
||
|
"""
|
||
|
|
||
|
_prefix: str = ""
|
||
|
SHUTDOWN_DEFAULT: int = 0
|
||
|
SHUTDOWN_WAIT_CONNECTIONS: int = 1
|
||
|
SHUTDOWN_WAIT_TRANSACTIONS: int = 2
|
||
|
SHUTDOWN_WAIT_UPDATES: int = 8
|
||
|
SHUTDOWN_WAIT_ALL_BUFFERS: int = 16
|
||
|
SHUTDOWN_WAIT_CRITICAL_BUFFERS: int = 17
|
||
|
KILL_QUERY: int = 254
|
||
|
KILL_CONNECTION: int = 255
|
||
|
|
||
|
desc: Dict[str, Tuple[int, str]] = {
|
||
|
"SHUTDOWN_DEFAULT": (
|
||
|
SHUTDOWN_DEFAULT,
|
||
|
"defaults to SHUTDOWN_WAIT_ALL_BUFFERS",
|
||
|
),
|
||
|
"SHUTDOWN_WAIT_CONNECTIONS": (
|
||
|
SHUTDOWN_WAIT_CONNECTIONS,
|
||
|
"wait for existing connections to finish",
|
||
|
),
|
||
|
"SHUTDOWN_WAIT_TRANSACTIONS": (
|
||
|
SHUTDOWN_WAIT_TRANSACTIONS,
|
||
|
"wait for existing trans to finish",
|
||
|
),
|
||
|
"SHUTDOWN_WAIT_UPDATES": (
|
||
|
SHUTDOWN_WAIT_UPDATES,
|
||
|
"wait for existing updates to finish",
|
||
|
),
|
||
|
"SHUTDOWN_WAIT_ALL_BUFFERS": (
|
||
|
SHUTDOWN_WAIT_ALL_BUFFERS,
|
||
|
"flush InnoDB and other storage engine buffers",
|
||
|
),
|
||
|
"SHUTDOWN_WAIT_CRITICAL_BUFFERS": (
|
||
|
SHUTDOWN_WAIT_CRITICAL_BUFFERS,
|
||
|
"don't flush InnoDB buffers, flush other storage engines' buffers",
|
||
|
),
|
||
|
"KILL_QUERY": (KILL_QUERY, "(no description)"),
|
||
|
"KILL_CONNECTION": (KILL_CONNECTION, "(no description)"),
|
||
|
}
|
||
|
|
||
|
|
||
|
class CharacterSet:
|
||
|
"""MySQL supported character sets and collations
|
||
|
|
||
|
List of character sets with their collations supported by MySQL. This
|
||
|
maps to the character set we get from the server within the handshake
|
||
|
packet.
|
||
|
|
||
|
The list is hardcode so we avoid a database query when getting the
|
||
|
name of the used character set or collation.
|
||
|
"""
|
||
|
|
||
|
# Multi-byte character sets which use 5c (backslash) in characters
|
||
|
slash_charsets: Tuple[int, ...] = (1, 13, 28, 84, 87, 88)
|
||
|
|
||
|
def __init__(self) -> None:
|
||
|
# Use LTS character set as default
|
||
|
self._desc: List[Optional[Tuple[str, str, bool]]] = MYSQL_CHARACTER_SETS_57
|
||
|
self._mysql_version: Tuple[int, ...] = (5, 7)
|
||
|
|
||
|
def set_mysql_version(self, version: Tuple[int, ...]) -> None:
|
||
|
"""Set the MySQL major version and change the charset mapping if is 5.7.
|
||
|
|
||
|
Args:
|
||
|
version (tuple): MySQL version tuple.
|
||
|
"""
|
||
|
self._mysql_version = version[:2]
|
||
|
if self._mysql_version >= (8, 0):
|
||
|
self._desc = MYSQL_CHARACTER_SETS
|
||
|
|
||
|
def get_info(self, setid: int) -> Tuple[str, str]:
|
||
|
"""Retrieves character set information as tuple using an ID
|
||
|
|
||
|
Retrieves character set and collation information based on the
|
||
|
given MySQL ID.
|
||
|
|
||
|
Raises ProgrammingError when character set is not supported.
|
||
|
|
||
|
Returns a tuple.
|
||
|
"""
|
||
|
try:
|
||
|
return self._desc[setid][0:2]
|
||
|
except IndexError:
|
||
|
raise ProgrammingError(f"Character set '{setid}' unsupported") from None
|
||
|
|
||
|
def get_desc(self, name: int) -> str:
|
||
|
"""Retrieves character set information as string using an ID
|
||
|
|
||
|
Retrieves character set and collation information based on the
|
||
|
given MySQL ID.
|
||
|
|
||
|
Returns a tuple.
|
||
|
"""
|
||
|
charset, collation = self.get_info(name)
|
||
|
return f"{charset}/{collation}"
|
||
|
|
||
|
def get_default_collation(self, charset: Union[int, str]) -> Tuple[str, str, int]:
|
||
|
"""Retrieves the default collation for given character set
|
||
|
|
||
|
Raises ProgrammingError when character set is not supported.
|
||
|
|
||
|
Returns list (collation, charset, index)
|
||
|
"""
|
||
|
if isinstance(charset, int):
|
||
|
try:
|
||
|
info = self._desc[charset]
|
||
|
return info[1], info[0], charset
|
||
|
except (IndexError, KeyError) as err:
|
||
|
raise ProgrammingError(
|
||
|
f"Character set ID '{charset}' unsupported"
|
||
|
) from err
|
||
|
|
||
|
for cid, info in enumerate(self._desc):
|
||
|
if info is None:
|
||
|
continue
|
||
|
if info[0] == charset and info[2] is True:
|
||
|
return info[1], info[0], cid
|
||
|
|
||
|
raise ProgrammingError(f"Character set '{charset}' unsupported")
|
||
|
|
||
|
def get_charset_info(
|
||
|
self, charset: Optional[Union[int, str]] = None, collation: Optional[str] = None
|
||
|
) -> Tuple[int, str, str]:
|
||
|
"""Get character set information using charset name and/or collation
|
||
|
|
||
|
Retrieves character set and collation information given character
|
||
|
set name and/or a collation name.
|
||
|
If charset is an integer, it will look up the character set based
|
||
|
on the MySQL's ID.
|
||
|
For example:
|
||
|
get_charset_info('utf8',None)
|
||
|
get_charset_info(collation='utf8_general_ci')
|
||
|
get_charset_info(47)
|
||
|
|
||
|
Raises ProgrammingError when character set is not supported.
|
||
|
|
||
|
Returns a tuple with (id, characterset name, collation)
|
||
|
"""
|
||
|
info: Optional[Union[Tuple[str, str, bool], Tuple[str, str, int]]] = None
|
||
|
if isinstance(charset, int):
|
||
|
try:
|
||
|
info = self._desc[charset]
|
||
|
return (charset, info[0], info[1])
|
||
|
except IndexError as err:
|
||
|
raise ProgrammingError(f"Character set ID {charset} unknown") from err
|
||
|
|
||
|
if charset in ("utf8", "utf-8") and self._mysql_version >= (8, 0):
|
||
|
charset = "utf8mb4"
|
||
|
if charset is not None and collation is None:
|
||
|
info = self.get_default_collation(charset)
|
||
|
return (info[2], info[1], info[0])
|
||
|
if charset is None and collation is not None:
|
||
|
for cid, info in enumerate(self._desc):
|
||
|
if info is None:
|
||
|
continue
|
||
|
if collation == info[1]:
|
||
|
return (cid, info[0], info[1])
|
||
|
raise ProgrammingError(f"Collation '{collation}' unknown")
|
||
|
for cid, info in enumerate(self._desc):
|
||
|
if info is None:
|
||
|
continue
|
||
|
if info[0] == charset and info[1] == collation:
|
||
|
return (cid, info[0], info[1])
|
||
|
_ = self.get_default_collation(charset)
|
||
|
raise ProgrammingError(f"Collation '{collation}' unknown")
|
||
|
|
||
|
def get_supported(self) -> Tuple[str, ...]:
|
||
|
"""Retrieves a list with names of all supproted character sets
|
||
|
|
||
|
Returns a tuple.
|
||
|
"""
|
||
|
res = []
|
||
|
for info in self._desc:
|
||
|
if info and info[0] not in res:
|
||
|
res.append(info[0])
|
||
|
return tuple(res)
|
||
|
|
||
|
|
||
|
class SQLMode(_Constants):
|
||
|
"""MySQL SQL Modes
|
||
|
|
||
|
The numeric values of SQL Modes are not interesting, only the names
|
||
|
are used when setting the SQL_MODE system variable using the MySQL
|
||
|
SET command.
|
||
|
|
||
|
The `SQLMode` class shouldn't be instantiated.
|
||
|
|
||
|
See http://dev.mysql.com/doc/refman/5.6/en/server-sql-mode.html
|
||
|
"""
|
||
|
|
||
|
_prefix: str = "MODE_"
|
||
|
REAL_AS_FLOAT: str = "REAL_AS_FLOAT"
|
||
|
PIPES_AS_CONCAT: str = "PIPES_AS_CONCAT"
|
||
|
ANSI_QUOTES: str = "ANSI_QUOTES"
|
||
|
IGNORE_SPACE: str = "IGNORE_SPACE"
|
||
|
NOT_USED: str = "NOT_USED"
|
||
|
ONLY_FULL_GROUP_BY: str = "ONLY_FULL_GROUP_BY"
|
||
|
NO_UNSIGNED_SUBTRACTION: str = "NO_UNSIGNED_SUBTRACTION"
|
||
|
NO_DIR_IN_CREATE: str = "NO_DIR_IN_CREATE"
|
||
|
POSTGRESQL: str = "POSTGRESQL"
|
||
|
ORACLE: str = "ORACLE"
|
||
|
MSSQL: str = "MSSQL"
|
||
|
DB2: str = "DB2"
|
||
|
MAXDB: str = "MAXDB"
|
||
|
NO_KEY_OPTIONS: str = "NO_KEY_OPTIONS"
|
||
|
NO_TABLE_OPTIONS: str = "NO_TABLE_OPTIONS"
|
||
|
NO_FIELD_OPTIONS: str = "NO_FIELD_OPTIONS"
|
||
|
MYSQL323: str = "MYSQL323"
|
||
|
MYSQL40: str = "MYSQL40"
|
||
|
ANSI: str = "ANSI"
|
||
|
NO_AUTO_VALUE_ON_ZERO: str = "NO_AUTO_VALUE_ON_ZERO"
|
||
|
NO_BACKSLASH_ESCAPES: str = "NO_BACKSLASH_ESCAPES"
|
||
|
STRICT_TRANS_TABLES: str = "STRICT_TRANS_TABLES"
|
||
|
STRICT_ALL_TABLES: str = "STRICT_ALL_TABLES"
|
||
|
NO_ZERO_IN_DATE: str = "NO_ZERO_IN_DATE"
|
||
|
NO_ZERO_DATE: str = "NO_ZERO_DATE"
|
||
|
INVALID_DATES: str = "INVALID_DATES"
|
||
|
ERROR_FOR_DIVISION_BY_ZERO: str = "ERROR_FOR_DIVISION_BY_ZERO"
|
||
|
TRADITIONAL: str = "TRADITIONAL"
|
||
|
NO_AUTO_CREATE_USER: str = "NO_AUTO_CREATE_USER"
|
||
|
HIGH_NOT_PRECEDENCE: str = "HIGH_NOT_PRECEDENCE"
|
||
|
NO_ENGINE_SUBSTITUTION: str = "NO_ENGINE_SUBSTITUTION"
|
||
|
PAD_CHAR_TO_FULL_LENGTH: str = "PAD_CHAR_TO_FULL_LENGTH"
|
||
|
|
||
|
@classmethod
|
||
|
def get_desc(cls, name: str) -> Optional[str]:
|
||
|
raise NotImplementedError
|
||
|
|
||
|
@classmethod
|
||
|
def get_info(cls, setid: int) -> Optional[str]:
|
||
|
raise NotImplementedError
|
||
|
|
||
|
@classmethod
|
||
|
def get_full_info(cls) -> Tuple[str, ...]:
|
||
|
"""Returns a sequence of all available SQL Modes
|
||
|
|
||
|
This class method returns a tuple containing all SQL Mode names. The
|
||
|
names will be alphabetically sorted.
|
||
|
|
||
|
Returns a tuple.
|
||
|
"""
|
||
|
res = []
|
||
|
for key in vars(cls).keys():
|
||
|
if not key.startswith("_") and not hasattr(getattr(cls, key), "__call__"):
|
||
|
res.append(key)
|
||
|
return tuple(sorted(res))
|