mysql-connector-python-2.0.4/0000755001577100000120000000000012506713751015446 5ustar pb2userwheelmysql-connector-python-2.0.4/lib/0000755001577100000120000000000012506713745016217 5ustar pb2userwheelmysql-connector-python-2.0.4/lib/__init__.py0000644001577100000120000000000012506707765020323 0ustar pb2userwheelmysql-connector-python-2.0.4/lib/mysql/0000755001577100000120000000000012506713745017364 5ustar pb2userwheelmysql-connector-python-2.0.4/lib/mysql/__init__.py0000644001577100000120000000000012506707765021470 0ustar pb2userwheelmysql-connector-python-2.0.4/lib/mysql/connector/0000755001577100000120000000000012506713751021353 5ustar pb2userwheelmysql-connector-python-2.0.4/lib/mysql/connector/connection.py0000644001577100000120000016712612506707765024111 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Implementing communication with MySQL servers. """ from io import IOBase import os import re import time from . import errors from .authentication import get_auth_plugin from .catch23 import PY2, isstr from .constants import ( ClientFlag, ServerCmd, CharacterSet, ServerFlag, flag_is_set, ShutdownType, NET_BUFFER_LENGTH ) from .conversion import MySQLConverterBase, MySQLConverter from .cursor import ( CursorBase, MySQLCursor, MySQLCursorRaw, MySQLCursorBuffered, MySQLCursorBufferedRaw, MySQLCursorPrepared, MySQLCursorDict, MySQLCursorBufferedDict, MySQLCursorNamedTuple, MySQLCursorBufferedNamedTuple) from .network import MySQLUnixSocket, MySQLTCPSocket from .protocol import MySQLProtocol from .utils import int4store DEFAULT_CONFIGURATION = { 'database': None, 'user': '', 'password': '', 'host': '127.0.0.1', 'port': 3306, 'unix_socket': None, 'use_unicode': True, 'charset': 'utf8', 'collation': None, 'converter_class': MySQLConverter, '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, 'passwd': None, 'db': None, 'connect_timeout': None, 'dsn': None, 'force_ipv6': False, 'auth_plugin': None, 'allow_local_infile': True, } class MySQLConnection(object): """Connection to a MySQL Server""" def __init__(self, *args, **kwargs): self._protocol = None self._socket = None self._handshake = None self._server_version = None self.converter = None self._converter_class = MySQLConverter self._client_flags = ClientFlag.get_default() self._charset_id = 33 self._sql_mode = None self._time_zone = None self._autocommit = False self._user = '' self._password = '' self._database = '' self._host = '127.0.0.1' self._port = 3306 self._unix_socket = None self._client_host = '' self._client_port = 0 self._ssl = {} self._force_ipv6 = False self._use_unicode = True self._get_warnings = False self._raise_on_warnings = False self._connection_timeout = None self._buffered = False self._unread_result = False self._have_next_result = False self._raw = False self._in_transaction = False self._prepared_statements = None self._ssl_active = False self._auth_plugin = None self._pool_config_version = None self._compress = False if len(kwargs) > 0: self.connect(**kwargs) def _get_self(self): """Return self for weakref.proxy This method is used when the original object is needed when using weakref.proxy. """ return self def _do_handshake(self): """Get the handshake from the MySQL server""" packet = self._socket.recv() if packet[4] == 255: raise errors.get_exception(packet) try: handshake = self._protocol.parse_handshake(packet) except Exception as err: raise errors.InterfaceError( 'Failed parsing handshake; {0}'.format(err)) if PY2: regex_ver = re.compile(r"^(\d{1,2})\.(\d{1,2})\.(\d{1,3})(.*)") else: # pylint: disable=W1401 regex_ver = re.compile(br"^(\d{1,2})\.(\d{1,2})\.(\d{1,3})(.*)") # pylint: enable=W1401 match = regex_ver.match(handshake['server_version_original']) if not match: raise errors.InterfaceError("Failed parsing MySQL version") version = tuple([int(v) for v in match.groups()[0:3]]) if b'fabric' in match.group(4).lower(): if version < (1, 4): raise errors.InterfaceError( "MySQL Fabric '{0}'' is not supported".format( handshake['server_version_original'])) elif version < (4, 1): raise errors.InterfaceError( "MySQL Version '{0}' is not supported.".format( handshake['server_version_original'])) if handshake['capabilities'] & ClientFlag.PLUGIN_AUTH: self.set_client_flags([ClientFlag.PLUGIN_AUTH]) self._handshake = handshake self._server_version = version def _do_auth(self, username=None, password=None, database=None, client_flags=0, charset=33, ssl_options=None): """Authenticate with the MySQL server Authentication happens in two parts. We first send a response to the handshake. The MySQL server will then send either an AuthSwitchRequest or an error packet. Raises NotSupportedError when we get the old, insecure password reply back. Raises any error coming from MySQL. """ self._ssl_active = False if client_flags & ClientFlag.SSL and ssl_options: packet = self._protocol.make_auth_ssl(charset=charset, client_flags=client_flags) self._socket.send(packet) self._socket.switch_to_ssl(**ssl_options) self._ssl_active = True packet = self._protocol.make_auth( handshake=self._handshake, username=username, password=password, database=database, charset=charset, client_flags=client_flags, ssl_enabled=self._ssl_active, auth_plugin=self._auth_plugin) self._socket.send(packet) self._auth_switch_request(username, password) if not (client_flags & ClientFlag.CONNECT_WITH_DB) and database: self.cmd_init_db(database) return True def _auth_switch_request(self, username=None, password=None): """Handle second part of authentication Raises NotSupportedError when we get the old, insecure password reply back. Raises any error coming from MySQL. """ packet = self._socket.recv() if packet[4] == 254 and len(packet) == 5: raise errors.NotSupportedError( "Authentication with old (insecure) passwords " "is not supported. For more information, lookup " "Password Hashing in the latest MySQL manual") elif packet[4] == 254: # AuthSwitchRequest (new_auth_plugin, auth_data) = self._protocol.parse_auth_switch_request(packet) auth = get_auth_plugin(new_auth_plugin)( auth_data, password=password, ssl_enabled=self._ssl_active) response = auth.auth_response() if response == b'\x00': self._socket.send(b'') else: self._socket.send(response) packet = self._socket.recv() if packet[4] != 1: return self._handle_ok(packet) else: auth_data = self._protocol.parse_auth_more_data(packet) elif packet[4] == 255: raise errors.get_exception(packet) def config(self, **kwargs): """Configure the MySQL Connection This method allows you to configure the MySQLConnection instance. Raises on errors. """ config = kwargs.copy() if 'dsn' in config: raise errors.NotSupportedError("Data source name is not supported") # Configure how we handle MySQL warnings try: self.get_warnings = config['get_warnings'] del config['get_warnings'] except KeyError: pass # Leave what was set or default try: self.raise_on_warnings = config['raise_on_warnings'] del config['raise_on_warnings'] except KeyError: pass # Leave what was set or default # Configure client flags try: default = ClientFlag.get_default() self.set_client_flags(config['client_flags'] or default) del config['client_flags'] except KeyError: pass # Missing client_flags-argument is OK try: if config['compress']: self._compress = True self.set_client_flags([ClientFlag.COMPRESS]) except KeyError: pass # Missing compress argument is OK try: if not config['allow_local_infile']: self.set_client_flags([-ClientFlag.LOCAL_FILES]) except KeyError: pass # Missing allow_local_infile argument is OK # Configure character set and collation if 'charset' in config or 'collation' in config: try: charset = config['charset'] del config['charset'] except KeyError: charset = None try: collation = config['collation'] del config['collation'] except KeyError: collation = None self._charset_id = CharacterSet.get_charset_info(charset, collation)[0] # Set converter class try: self.set_converter_class(config['converter_class']) except KeyError: pass # Using default converter class except TypeError: raise AttributeError("Converter class should be a subclass " "of conversion.MySQLConverterBase.") # Compatible configuration with other drivers compat_map = [ # (,) ('db', 'database'), ('passwd', 'password'), ('connect_timeout', 'connection_timeout'), ] for compat, translate in compat_map: try: if translate not in config: config[translate] = config[compat] del config[compat] except KeyError: pass # Missing compat argument is OK # Configure login information if 'user' in config or 'password' in config: try: user = config['user'] del config['user'] except KeyError: user = self._user try: password = config['password'] del config['password'] except KeyError: password = self._password self.set_login(user, password) # Check network locations try: self._port = int(config['port']) del config['port'] except KeyError: pass # Missing port argument is OK except ValueError: raise errors.InterfaceError( "TCP/IP port number should be an integer") # Other configuration set_ssl_flag = False for key, value in config.items(): try: DEFAULT_CONFIGURATION[key] except KeyError: raise AttributeError("Unsupported argument '{0}'".format(key)) # SSL Configuration if key.startswith('ssl_'): set_ssl_flag = True self._ssl.update({key.replace('ssl_', ''): value}) else: attribute = '_' + key try: setattr(self, attribute, value.strip()) except AttributeError: setattr(self, attribute, value) if set_ssl_flag: if 'verify_cert' not in self._ssl: self._ssl['verify_cert'] = \ DEFAULT_CONFIGURATION['ssl_verify_cert'] # Make sure both ssl_key/ssl_cert are set, or neither (XOR) if 'ca' not in self._ssl or self._ssl['ca'] is None: raise AttributeError( "Missing ssl_ca argument.") if bool('key' in self._ssl) != bool('cert' in self._ssl): raise AttributeError( "ssl_key and ssl_cert need to be both " "specified, or neither." ) # Make sure key/cert are set to None elif not set(('key', 'cert')) <= set(self._ssl): self._ssl['key'] = None self._ssl['cert'] = None elif (self._ssl['key'] is None) != (self._ssl['cert'] is None): raise AttributeError( "ssl_key and ssl_cert need to be both " "set, or neither." ) self.set_client_flags([ClientFlag.SSL]) def _get_connection(self, prtcls=None): """Get connection based on configuration This method will return the appropriated connection object using the connection parameters. Returns subclass of MySQLBaseSocket. """ conn = None if self.unix_socket and os.name != 'nt': conn = MySQLUnixSocket(unix_socket=self.unix_socket) else: conn = MySQLTCPSocket(host=self.server_host, port=self.server_port, force_ipv6=self._force_ipv6) conn.set_connection_timeout(self._connection_timeout) return conn def _open_connection(self): """Open the connection to the MySQL server This method sets up and opens the connection to the MySQL server. Raises on errors. """ self._socket = self._get_connection() self._socket.open_connection() self._do_handshake() self._do_auth(self._user, self._password, self._database, self._client_flags, self._charset_id, self._ssl) self.set_converter_class(self._converter_class) if self._client_flags & ClientFlag.COMPRESS: self._socket.recv = self._socket.recv_compressed self._socket.send = self._socket.send_compressed def _post_connection(self): """Executes commands after connection has been established This method executes commands after the connection has been established. Some setting like autocommit, character set, and SQL mode are set using this method. """ self.set_charset_collation(self._charset_id) self.autocommit = self._autocommit if self._time_zone: self.time_zone = self._time_zone if self._sql_mode: self.sql_mode = self._sql_mode def connect(self, **kwargs): """Connect to the MySQL server This method sets up the connection to the MySQL server. If no arguments are given, it will use the already configured or default values. """ if len(kwargs) > 0: self.config(**kwargs) self._protocol = MySQLProtocol() self.disconnect() self._open_connection() self._post_connection() def shutdown(self): """Shut down connection to MySQL Server. """ if not self._socket: return try: self._socket.shutdown() except (AttributeError, errors.Error): pass # Getting an exception would mean we are disconnected. def disconnect(self): """Disconnect from the MySQL server """ if not self._socket: return try: self.cmd_quit() self._socket.close_connection() except (AttributeError, errors.Error): pass # Getting an exception would mean we are disconnected. close = disconnect def _send_cmd(self, command, argument=None, packet_number=0, packet=None, expect_response=True): """Send a command to the MySQL server This method sends a command with an optional argument. If packet is not None, it will be sent and the argument will be ignored. The packet_number is optional and should usually not be used. Some commands might not result in the MySQL server returning a response. If a command does not return anything, you should set expect_response to False. The _send_cmd method will then return None instead of a MySQL packet. Returns a MySQL packet or None. """ if self.unread_result: raise errors.InternalError("Unread result found.") try: self._socket.send( self._protocol.make_command(command, packet or argument), packet_number) except AttributeError: raise errors.OperationalError("MySQL Connection not available.") if not expect_response: return None return self._socket.recv() def _send_data(self, data_file, send_empty_packet=False): """Send data to the MySQL server This method accepts a file-like object and sends its data as is to the MySQL server. If the send_empty_packet is True, it will send an extra empty package (for example when using LOAD LOCAL DATA INFILE). Returns a MySQL packet. """ if self.unread_result: raise errors.InternalError("Unread result found.") if not hasattr(data_file, 'read'): raise ValueError("expecting a file-like object") try: buf = data_file.read(NET_BUFFER_LENGTH - 16) while buf: self._socket.send(buf) buf = data_file.read(NET_BUFFER_LENGTH - 16) except AttributeError: raise errors.OperationalError("MySQL Connection not available.") if send_empty_packet: try: self._socket.send(b'') except AttributeError: raise errors.OperationalError( "MySQL Connection not available.") return self._socket.recv() def _handle_server_status(self, flags): """Handle the server flags found in MySQL packets This method handles the server flags send by MySQL OK and EOF packets. It, for example, checks whether there exists more result sets or whether there is an ongoing transaction. """ self._have_next_result = flag_is_set(ServerFlag.MORE_RESULTS_EXISTS, flags) self._in_transaction = flag_is_set(ServerFlag.STATUS_IN_TRANS, flags) @property def in_transaction(self): """MySQL session has started a transaction """ return self._in_transaction def _handle_ok(self, packet): """Handle a MySQL OK packet This method handles a MySQL OK packet. When the packet is found to be an Error packet, an error will be raised. If the packet is neither an OK or an Error packet, errors.InterfaceError will be raised. Returns a dict() """ if packet[4] == 0: ok_pkt = self._protocol.parse_ok(packet) self._handle_server_status(ok_pkt['server_status']) return ok_pkt elif packet[4] == 255: raise errors.get_exception(packet) raise errors.InterfaceError('Expected OK packet') def _handle_eof(self, packet): """Handle a MySQL EOF packet This method handles a MySQL EOF packet. When the packet is found to be an Error packet, an error will be raised. If the packet is neither and OK or an Error packet, errors.InterfaceError will be raised. Returns a dict() """ if packet[4] == 254: eof = self._protocol.parse_eof(packet) self._handle_server_status(eof['status_flag']) return eof elif packet[4] == 255: raise errors.get_exception(packet) raise errors.InterfaceError('Expected EOF packet') def _handle_load_data_infile(self, filename): """Handle a LOAD DATA INFILE LOCAL request""" try: data_file = open(filename, 'rb') except IOError: # Send a empty packet to cancel the operation try: self._socket.send(b'') except AttributeError: raise errors.OperationalError( "MySQL Connection not available.") raise errors.InterfaceError( "File '{0}' could not be read".format(filename)) return self._handle_ok(self._send_data(data_file, send_empty_packet=True)) def _handle_result(self, packet): """Handle a MySQL Result This method handles a MySQL result, for example, after sending the query command. OK and EOF packets will be handled and returned. If the packet is an Error packet, an errors.Error-exception will be raised. The dictionary returned of: - columns: column information - eof: the EOF-packet information Returns a dict() """ if not packet or len(packet) < 4: raise errors.InterfaceError('Empty response') elif packet[4] == 0: return self._handle_ok(packet) elif packet[4] == 251: if PY2: filename = str(packet[5:]) else: filename = packet[5:].decode() return self._handle_load_data_infile(filename) elif packet[4] == 254: return self._handle_eof(packet) elif packet[4] == 255: raise errors.get_exception(packet) # We have a text result set column_count = self._protocol.parse_column_count(packet) if not column_count or not isinstance(column_count, int): raise errors.InterfaceError('Illegal result set.') columns = [None,] * column_count for i in range(0, column_count): columns[i] = self._protocol.parse_column(self._socket.recv()) eof = self._handle_eof(self._socket.recv()) self.unread_result = True return {'columns': columns, 'eof': eof} def get_rows(self, count=None, binary=False, columns=None): """Get all rows returned by the MySQL server This method gets all rows returned by the MySQL server after sending, for example, the query command. The result is a tuple consisting of a list of rows and the EOF packet. Returns a tuple() """ if not self.unread_result: raise errors.InternalError("No result set available.") if binary: rows = self._protocol.read_binary_result( self._socket, columns, count) else: rows = self._protocol.read_text_result(self._socket, count) if rows[-1] is not None: self._handle_server_status(rows[-1]['status_flag']) self.unread_result = False return rows def get_row(self, binary=False, columns=None): """Get the next rows returned by the MySQL server This method gets one row from the result set after sending, for example, the query command. The result is a tuple consisting of the row and the EOF packet. If no row was available in the result set, the row data will be None. Returns a tuple. """ (rows, eof) = self.get_rows(count=1, binary=binary, columns=columns) if len(rows): return (rows[0], eof) return (None, eof) def cmd_init_db(self, database): """Change the current database This method changes the current (default) database by sending the INIT_DB command. The result is a dictionary containing the OK packet information. Returns a dict() """ return self._handle_ok( self._send_cmd(ServerCmd.INIT_DB, database.encode('utf-8'))) def cmd_query(self, query): """Send a query to the MySQL server This method send the query to the MySQL server and returns the result. If there was a text result, a tuple will be returned consisting of the number of columns and a list containing information about these columns. When the query doesn't return a text result, the OK or EOF packet information as dictionary will be returned. In case the result was an error, exception errors.Error will be raised. Returns a tuple() """ if not isinstance(query, bytes): query = query.encode('utf-8') result = self._handle_result(self._send_cmd(ServerCmd.QUERY, query)) if self._have_next_result: raise errors.InterfaceError( 'Use cmd_query_iter for statements with multiple queries.') return result def cmd_query_iter(self, statements): """Send one or more statements to the MySQL server Similar to the cmd_query method, but instead returns a generator object to iterate through results. It sends the statements to the MySQL server and through the iterator you can get the results. statement = 'SELECT 1; INSERT INTO t1 VALUES (); SELECT 2' for result in cnx.cmd_query(statement, iterate=True): if 'columns' in result: columns = result['columns'] rows = cnx.get_rows() else: # do something useful with INSERT result Returns a generator. """ if not isinstance(statements, bytearray): if isstr(statements): statements = bytearray(statements.encode('utf-8')) else: statements = bytearray(statements) # Handle the first query result yield self._handle_result(self._send_cmd(ServerCmd.QUERY, statements)) # Handle next results, if any while self._have_next_result: if self.unread_result: raise errors.InternalError("Unread result found.") yield self._handle_result(self._socket.recv()) def cmd_refresh(self, options): """Send the Refresh command to the MySQL server This method sends the Refresh command to the MySQL server. The options argument should be a bitwise value using constants.RefreshOption. Usage example: RefreshOption = mysql.connector.RefreshOption refresh = RefreshOption.LOG | RefreshOption.THREADS cnx.cmd_refresh(refresh) The result is a dictionary with the OK packet information. Returns a dict() """ return self._handle_ok( self._send_cmd(ServerCmd.REFRESH, int4store(options))) def cmd_quit(self): """Close the current connection with the server This method sends the QUIT command to the MySQL server, closing the current connection. Since the no response can be returned to the client, cmd_quit() will return the packet it send. Returns a str() """ if self.unread_result: raise errors.InternalError("Unread result found.") packet = self._protocol.make_command(ServerCmd.QUIT) self._socket.send(packet, 0) return packet def cmd_shutdown(self, shutdown_type=None): """Shut down the MySQL Server This method sends the SHUTDOWN command to the MySQL server and is only possible if the current user has SUPER privileges. The result is a dictionary containing the OK packet information. Note: Most applications and scripts do not the SUPER privilege. Returns a dict() """ if shutdown_type: if not ShutdownType.get_info(shutdown_type): raise errors.InterfaceError("Invalid shutdown type") atype = shutdown_type else: atype = ShutdownType.SHUTDOWN_DEFAULT return self._handle_eof(self._send_cmd(ServerCmd.SHUTDOWN, atype)) def cmd_statistics(self): """Send the statistics command to the MySQL Server This method sends the STATISTICS command to the MySQL server. The result is a dictionary with various statistical information. Returns a dict() """ if self.unread_result: raise errors.InternalError("Unread result found.") packet = self._protocol.make_command(ServerCmd.STATISTICS) self._socket.send(packet, 0) return self._protocol.parse_statistics(self._socket.recv()) def cmd_process_info(self): """Get the process list of the MySQL Server This method is a placeholder to notify that the PROCESS_INFO command is not supported by raising the NotSupportedError. The command "SHOW PROCESSLIST" should be send using the cmd_query()-method or using the INFORMATION_SCHEMA database. Raises NotSupportedError exception """ raise errors.NotSupportedError( "Not implemented. Use SHOW PROCESSLIST or INFORMATION_SCHEMA") def cmd_process_kill(self, mysql_pid): """Kill a MySQL process This method send the PROCESS_KILL command to the server along with the process ID. The result is a dictionary with the OK packet information. Returns a dict() """ return self._handle_ok( self._send_cmd(ServerCmd.PROCESS_KILL, int4store(mysql_pid))) def cmd_debug(self): """Send the DEBUG command This method sends the DEBUG command to the MySQL server, which requires the MySQL user to have SUPER privilege. The output will go to the MySQL server error log and the result of this method is a dictionary with EOF packet information. Returns a dict() """ return self._handle_eof(self._send_cmd(ServerCmd.DEBUG)) def cmd_ping(self): """Send the PING command This method sends the PING command to the MySQL server. It is used to check if the the connection is still valid. The result of this method is dictionary with OK packet information. Returns a dict() """ return self._handle_ok(self._send_cmd(ServerCmd.PING)) def cmd_change_user(self, username='', password='', database='', charset=33): """Change the current logged in user This method allows to change the current logged in user information. The result is a dictionary with OK packet information. Returns a dict() """ if self.unread_result: raise errors.InternalError("Unread result found.") if self._compress: raise errors.NotSupportedError("Change user is not supported with " "compression.") packet = self._protocol.make_change_user( handshake=self._handshake, username=username, password=password, database=database, charset=charset, client_flags=self._client_flags, ssl_enabled=self._ssl_active, auth_plugin=self._auth_plugin) self._socket.send(packet, 0) ok_packet = self._auth_switch_request(username, password) try: if not (self._client_flags & ClientFlag.CONNECT_WITH_DB) \ and database: self.cmd_init_db(database) except: raise self._charset_id = charset self._post_connection() return ok_packet def is_connected(self): """Reports whether the connection to MySQL Server is available This method checks whether the connection to MySQL is available. It is similar to ping(), but unlike the ping()-method, either True or False is returned and no exception is raised. Returns True or False. """ try: self.cmd_ping() except: return False # This method does not raise return True def reset_session(self, user_variables=None, session_variables=None): """Clears the current active session This method resets the session state, if the MySQL server is 5.7.3 or later active session will be reset without re-authenticating. For other server versions session will be reset by re-authenticating. It is possible to provide a sequence of variables and their values to be set after clearing the session. This is possible for both user defined variables and session variables. This method takes two arguments user_variables and session_variables which are dictionaries. Raises OperationalError if not connected, InternalError if there are unread results and InterfaceError on errors. """ if not self.is_connected(): raise errors.OperationalError("MySQL Connection not available.") try: self.cmd_reset_connection() except errors.NotSupportedError: if self._compress: raise errors.NotSupportedError( "Reset session is not supported with compression for " "MySQL server version 5.7.2 or earlier.") else: self.cmd_change_user(self._user, self._password, self._database, self._charset_id) cur = self.cursor() if user_variables: for key, value in user_variables.items(): cur.execute("SET @`{0}` = %s".format(key), (value,)) if session_variables: for key, value in session_variables.items(): cur.execute("SET SESSION `{0}` = %s".format(key), (value,)) def reconnect(self, attempts=1, delay=0): """Attempt to reconnect to the MySQL server The argument attempts should be the number of times a reconnect is tried. The delay argument is the number of seconds to wait between each retry. You may want to set the number of attempts higher and use delay when you expect the MySQL server to be down for maintenance or when you expect the network to be temporary unavailable. Raises InterfaceError on errors. """ counter = 0 while counter != attempts: counter = counter + 1 try: self.disconnect() self.connect() if self.is_connected(): break except Exception as err: # pylint: disable=W0703 if counter == attempts: msg = "Can not reconnect to MySQL after {0} "\ "attempt(s): {1}".format(attempts, str(err)) raise errors.InterfaceError(msg) if delay > 0: time.sleep(delay) def ping(self, reconnect=False, attempts=1, delay=0): """Check availability to the MySQL server When reconnect is set to True, one or more attempts are made to try to reconnect to the MySQL server using the reconnect()-method. delay is the number of seconds to wait between each retry. When the connection is not available, an InterfaceError is raised. Use the is_connected()-method if you just want to check the connection without raising an error. Raises InterfaceError on errors. """ try: self.cmd_ping() except: if reconnect: self.reconnect(attempts=attempts, delay=delay) else: raise errors.InterfaceError("Connection to MySQL is" " not available.") def set_converter_class(self, convclass): """ Set the converter class to be used. This should be a class overloading methods and members of conversion.MySQLConverter. """ if issubclass(convclass, MySQLConverterBase): charset_name = CharacterSet.get_info(self._charset_id)[0] self._converter_class = convclass self.converter = convclass(charset_name, self._use_unicode) else: raise TypeError("Converter class should be a subclass " "of conversion.MySQLConverterBase.") def get_server_version(self): """Get the MySQL version This method returns the MySQL server version as a tuple. If not previously connected, it will return None. Returns a tuple or None. """ return self._server_version def get_server_info(self): """Get the original MySQL version information This method returns the original MySQL server as text. If not previously connected, it will return None. Returns a string or None. """ try: return self._handshake['server_version_original'] except (TypeError, KeyError): return None @property def connection_id(self): """MySQL connection ID""" try: return self._handshake['server_threadid'] except KeyError: return None def set_login(self, username=None, password=None): """Set login information for MySQL Set the username and/or password for the user connecting to the MySQL Server. """ if username is not None: self._user = username.strip() else: self._user = '' if password is not None: self._password = password.strip() else: self._password = '' def set_unicode(self, value=True): """Toggle unicode mode Set whether we return string fields as unicode or not. Default is True. """ self._use_unicode = value if self.converter: self.converter.set_unicode(value) def set_charset_collation(self, charset=None, collation=None): """Sets the character set and collation for the current connection This method sets the character set and collation to be used for the current connection. The charset argument can be either the name of a character set as a string, or the numerical equivalent as defined in constants.CharacterSet. When the collation is not given, the default will be looked up and used. For example, the following will set the collation for the latin1 character set to latin1_general_ci: set_charset('latin1','latin1_general_ci') """ if charset: if isinstance(charset, int): self._charset_id = charset (self._charset_id, charset_name, collation_name) = \ CharacterSet.get_charset_info(charset) elif isinstance(charset, str): (self._charset_id, charset_name, collation_name) = \ CharacterSet.get_charset_info(charset, collation) else: raise ValueError( "charset should be either integer, string or None") elif collation: (self._charset_id, charset_name, collation_name) = \ CharacterSet.get_charset_info(collation=collation) self._execute_query("SET NAMES '{0}' COLLATE '{1}'".format( charset_name, collation_name)) self.converter.set_charset(charset_name) @property def charset(self): """Returns the character set for current connection This property returns the character set name of the current connection. The server is queried when the connection is active. If not connected, the configured character set name is returned. Returns a string. """ return CharacterSet.get_info(self._charset_id)[0] @property def python_charset(self): """Returns the Python character set for current connection This property returns the character set name of the current connection. Note that, unlike property charset, this checks if the previously set character set is supported by Python and if not, it returns the equivalent character set that Python supports. Returns a string. """ encoding = CharacterSet.get_info(self._charset_id)[0] if encoding in ('utf8mb4', 'binary'): return 'utf8' else: return encoding @property def collation(self): """Returns the collation for current connection This property returns the collation name of the current connection. The server is queried when the connection is active. If not connected, the configured collation name is returned. Returns a string. """ return CharacterSet.get_charset_info(self._charset_id)[2] def set_client_flags(self, flags): """Set the client flags The flags-argument can be either an int or a list (or tuple) of ClientFlag-values. If it is an integer, it will set client_flags to flags as is. If flags is a list (or tuple), each flag will be set or unset when it's negative. set_client_flags([ClientFlag.FOUND_ROWS,-ClientFlag.LONG_FLAG]) Raises ProgrammingError when the flags argument is not a set or an integer bigger than 0. Returns self.client_flags """ if isinstance(flags, int) and flags > 0: self._client_flags = flags elif isinstance(flags, (tuple, list)): for flag in flags: if flag < 0: self._client_flags &= ~abs(flag) else: self._client_flags |= flag else: raise errors.ProgrammingError( "set_client_flags expect integer (>0) or set") return self._client_flags def isset_client_flag(self, flag): """Check if a client flag is set""" if (self._client_flags & flag) > 0: return True return False @property def user(self): """User used while connecting to MySQL""" return self._user @property def server_host(self): """MySQL server IP address or name""" return self._host @property def server_port(self): "MySQL server TCP/IP port" return self._port @property def unix_socket(self): "MySQL Unix socket file location" return self._unix_socket def _set_unread_result(self, toggle): """Set whether there is an unread result This method is used by cursors to let other cursors know there is still a result set that needs to be retrieved. Raises ValueError on errors. """ if not isinstance(toggle, bool): raise ValueError("Expected a boolean type") self._unread_result = toggle def _get_unread_result(self): """Get whether there is an unread result This method is used by cursors to check whether another cursor still needs to retrieve its result set. Returns True, or False when there is no unread result. """ return self._unread_result unread_result = property(_get_unread_result, _set_unread_result, doc="Unread result for this MySQL connection") def set_database(self, value): """Set the current database""" self.cmd_query("USE %s" % value) def get_database(self): """Get the current database""" return self._info_query("SELECT DATABASE()")[0] database = property(get_database, set_database, doc="Current database") def set_time_zone(self, value): """Set the time zone""" self.cmd_query("SET @@session.time_zone = '{0}'".format(value)) self._time_zone = value def get_time_zone(self): """Get the current time zone""" return self._info_query("SELECT @@session.time_zone")[0] time_zone = property(get_time_zone, set_time_zone, doc="time_zone value for current MySQL session") def set_sql_mode(self, value): """Set the SQL mode This method sets the SQL Mode for the current connection. The value argument can be either a string with comma separate mode names, or a sequence of mode names. It is good practice to use the constants class SQLMode: from mysql.connector.constants import SQLMode cnx.sql_mode = [SQLMode.NO_ZERO_DATE, SQLMode.REAL_AS_FLOAT] """ if isinstance(value, (list, tuple)): value = ','.join(value) self.cmd_query("SET @@session.sql_mode = '{0}'".format(value)) self._sql_mode = value def get_sql_mode(self): """Get the SQL mode""" return self._info_query("SELECT @@session.sql_mode")[0] sql_mode = property(get_sql_mode, set_sql_mode, doc="sql_mode value for current MySQL session") def set_autocommit(self, value): """Toggle autocommit""" switch = 'ON' if value else 'OFF' self._execute_query("SET @@session.autocommit = {0}".format(switch)) self._autocommit = value def get_autocommit(self): """Get whether autocommit is on or off""" value = self._info_query("SELECT @@session.autocommit")[0] return True if value == 1 else False autocommit = property(get_autocommit, set_autocommit, doc="autocommit value for current MySQL session") def _set_getwarnings(self, toggle): """Set whether warnings should be automatically retrieved The toggle-argument must be a boolean. When True, cursors for this connection will retrieve information about warnings (if any). Raises ValueError on error. """ if not isinstance(toggle, bool): raise ValueError("Expected a boolean type") self._get_warnings = toggle def _get_getwarnings(self): """Get whether this connection retrieves warnings automatically This method returns whether this connection retrieves warnings automatically. Returns True, or False when warnings are not retrieved. """ return self._get_warnings get_warnings = property( _get_getwarnings, _set_getwarnings, doc="Toggle and check whether to retrieve warnings automatically") def _set_raise_on_warnings(self, toggle): """Set whether warnings raise an error The toggle-argument must be a boolean. When True, cursors for this connection will raise an error when MySQL reports warnings. Raising on warnings implies retrieving warnings automatically. In other words: warnings will be set to True. If set to False, warnings will be also set to False. Raises ValueError on error. """ if not isinstance(toggle, bool): raise ValueError("Expected a boolean type") self._raise_on_warnings = toggle self._get_warnings = toggle def _get_raise_on_warnings(self): """Get whether this connection raises an error on warnings This method returns whether this connection will raise errors when MySQL reports warnings. Returns True or False. """ return self._raise_on_warnings raise_on_warnings = property( _get_raise_on_warnings, _set_raise_on_warnings, doc="Toggle whether to raise on warnings "\ "(implies retrieving warnings).") def cursor(self, buffered=None, raw=None, prepared=None, cursor_class=None, dictionary=None, named_tuple=None): """Instantiates and returns a cursor By default, MySQLCursor is returned. Depending on the options while connecting, a buffered and/or raw cursor is instantiated instead. Also depending upon the cursor options, rows can be returned as dictionary or named tuple. Dictionary and namedtuple based cursors are available with buffered output but not raw. It is possible to also give a custom cursor through the cursor_class parameter, but it needs to be a subclass of mysql.connector.cursor.CursorBase. Raises ProgrammingError when cursor_class is not a subclass of CursorBase. Raises ValueError when cursor is not available. Returns a cursor-object """ if self._unread_result is True: raise errors.InternalError("Unread result found.") if not self.is_connected(): raise errors.OperationalError("MySQL Connection not available.") if cursor_class is not None: if not issubclass(cursor_class, CursorBase): raise errors.ProgrammingError( "Cursor class needs be to subclass of cursor.CursorBase") return (cursor_class)(self) buffered = buffered or self._buffered raw = raw or self._raw cursor_type = 0 if buffered is True: cursor_type |= 1 if raw is True: cursor_type |= 2 if dictionary is True: cursor_type |= 4 if named_tuple is True: cursor_type |= 8 if prepared is True: cursor_type |= 16 types = { 0: MySQLCursor, # 0 1: MySQLCursorBuffered, 2: MySQLCursorRaw, 3: MySQLCursorBufferedRaw, 4: MySQLCursorDict, 5: MySQLCursorBufferedDict, 8: MySQLCursorNamedTuple, 9: MySQLCursorBufferedNamedTuple, 16: MySQLCursorPrepared } try: return (types[cursor_type])(self) except KeyError: args = ('buffered', 'raw', 'dictionary', 'named_tuple', 'prepared') raise ValueError('Cursor not available with given criteria: ' + ', '.join([args[i] for i in range(5) if cursor_type & (1 << i) != 0])) def start_transaction(self, consistent_snapshot=False, isolation_level=None, readonly=None): """Start a transaction This method explicitly starts a transaction sending the START TRANSACTION statement to the MySQL server. You can optionally set whether there should be a consistent snapshot, which isolation level you need or which access mode i.e. READ ONLY or READ WRITE. For example, to start a transaction with isolation level SERIALIZABLE, you would do the following: >>> cnx = mysql.connector.connect(..) >>> cnx.start_transaction(isolation_level='SERIALIZABLE') Raises ProgrammingError when a transaction is already in progress and when ValueError when isolation_level specifies an Unknown level. """ if self.in_transaction: raise errors.ProgrammingError("Transaction already in progress") if isolation_level: level = isolation_level.strip().replace('-', ' ').upper() levels = ['READ UNCOMMITTED', 'READ COMMITTED', 'REPEATABLE READ', 'SERIALIZABLE'] if level not in levels: raise ValueError( 'Unknown isolation level "{0}"'.format(isolation_level)) self._execute_query( "SET TRANSACTION ISOLATION LEVEL {0}".format(level)) if readonly is not None: if self._server_version < (5, 6, 5): raise ValueError( "MySQL server version {0} does not support " "this feature".format(self._server_version)) if readonly: access_mode = 'READ ONLY' else: access_mode = 'READ WRITE' self._execute_query( "SET TRANSACTION {0}".format(access_mode)) query = "START TRANSACTION" if consistent_snapshot: query += " WITH CONSISTENT SNAPSHOT" self._execute_query(query) def commit(self): """Commit current transaction""" self._execute_query("COMMIT") def rollback(self): """Rollback current transaction""" if self._unread_result: self.get_rows() self._execute_query("ROLLBACK") def _execute_query(self, query): """Execute a query This method simply calls cmd_query() after checking for unread result. If there are still unread result, an errors.InterfaceError is raised. Otherwise whatever cmd_query() returns is returned. Returns a dict() """ if self._unread_result is True: raise errors.InternalError("Unread result found.") self.cmd_query(query) def _info_query(self, query): """Send a query which only returns 1 row""" cursor = self.cursor(buffered=True) cursor.execute(query) return cursor.fetchone() def _handle_binary_ok(self, packet): """Handle a MySQL Binary Protocol OK packet This method handles a MySQL Binary Protocol OK packet. When the packet is found to be an Error packet, an error will be raised. If the packet is neither an OK or an Error packet, errors.InterfaceError will be raised. Returns a dict() """ if packet[4] == 0: return self._protocol.parse_binary_prepare_ok(packet) elif packet[4] == 255: raise errors.get_exception(packet) raise errors.InterfaceError('Expected Binary OK packet') def _handle_binary_result(self, packet): """Handle a MySQL Result This method handles a MySQL result, for example, after sending the query command. OK and EOF packets will be handled and returned. If the packet is an Error packet, an errors.Error-exception will be raised. The tuple returned by this method consist of: - the number of columns in the result, - a list of tuples with information about the columns, - the EOF packet information as a dictionary. Returns tuple() or dict() """ if not packet or len(packet) < 4: raise errors.InterfaceError('Empty response') elif packet[4] == 0: return self._handle_ok(packet) elif packet[4] == 254: return self._handle_eof(packet) elif packet[4] == 255: raise errors.get_exception(packet) # We have a binary result set column_count = self._protocol.parse_column_count(packet) if not column_count or not isinstance(column_count, int): raise errors.InterfaceError('Illegal result set.') columns = [None] * column_count for i in range(0, column_count): columns[i] = self._protocol.parse_column(self._socket.recv()) eof = self._handle_eof(self._socket.recv()) return (column_count, columns, eof) def cmd_stmt_prepare(self, statement): """Prepare a MySQL statement This method will send the PREPARE command to MySQL together with the given statement. Returns a dict() """ packet = self._send_cmd(ServerCmd.STMT_PREPARE, statement) result = self._handle_binary_ok(packet) result['columns'] = [] result['parameters'] = [] if result['num_params'] > 0: for _ in range(0, result['num_params']): result['parameters'].append( self._protocol.parse_column(self._socket.recv())) self._handle_eof(self._socket.recv()) if result['num_columns'] > 0: for _ in range(0, result['num_columns']): result['columns'].append( self._protocol.parse_column(self._socket.recv())) self._handle_eof(self._socket.recv()) return result def cmd_stmt_execute(self, statement_id, data=(), parameters=(), flags=0): """Execute a prepared MySQL statement""" parameters = list(parameters) long_data_used = {} if data: for param_id, _ in enumerate(parameters): if isinstance(data[param_id], IOBase): binary = True try: binary = 'b' not in data[param_id].mode except AttributeError: pass self.cmd_stmt_send_long_data(statement_id, param_id, data[param_id]) long_data_used[param_id] = (binary,) execute_packet = self._protocol.make_stmt_execute( statement_id, data, tuple(parameters), flags, long_data_used, self.charset) packet = self._send_cmd(ServerCmd.STMT_EXECUTE, packet=execute_packet) result = self._handle_binary_result(packet) return result def cmd_stmt_close(self, statement_id): """Deallocate a prepared MySQL statement This method deallocates the prepared statement using the statement_id. Note that the MySQL server does not return anything. """ self._send_cmd(ServerCmd.STMT_CLOSE, int4store(statement_id), expect_response=False) def cmd_stmt_send_long_data(self, statement_id, param_id, data): """Send data for a column This methods send data for a column (for example BLOB) for statement identified by statement_id. The param_id indicate which parameter the data belongs too. The data argument should be a file-like object. Since MySQL does not send anything back, no error is raised. When the MySQL server is not reachable, an OperationalError is raised. cmd_stmt_send_long_data should be called before cmd_stmt_execute. The total bytes send is returned. Returns int. """ chunk_size = 8192 total_sent = 0 # pylint: disable=W0212 prepare_packet = self._protocol._prepare_stmt_send_long_data # pylint: enable=W0212 try: buf = data.read(chunk_size) while buf: packet = prepare_packet(statement_id, param_id, buf) self._send_cmd(ServerCmd.STMT_SEND_LONG_DATA, packet=packet, expect_response=False) total_sent += len(buf) buf = data.read(chunk_size) except AttributeError: raise errors.OperationalError("MySQL Connection not available.") return total_sent def cmd_stmt_reset(self, statement_id): """Reset data for prepared statement sent as long data The result is a dictionary with OK packet information. Returns a dict() """ self._handle_ok(self._send_cmd(ServerCmd.STMT_RESET, int4store(statement_id))) def cmd_reset_connection(self): """Resets the session state without re-authenticating Works only for MySQL server 5.7.3 or later. The result is a dictionary with OK packet information. Returns a dict() """ if self._server_version < (5, 7, 3): raise errors.NotSupportedError("MySQL version 5.7.2 and " "earlier does not support " "COM_RESET_CONNECTION.") self._handle_ok(self._send_cmd(ServerCmd.RESET_CONNECTION)) self._post_connection() mysql-connector-python-2.0.4/lib/mysql/connector/utils.py0000644001577100000120000002171112506707765023077 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Utilities """ from __future__ import print_function __MYSQL_DEBUG__ = False import struct from .catch23 import struct_unpack def intread(buf): """Unpacks the given buffer to an integer""" try: if isinstance(buf, int): return buf length = len(buf) if length == 1: return buf[0] elif length <= 4: tmp = buf + b'\x00'*(4-length) return struct_unpack(' 255: raise ValueError('int1store requires 0 <= i <= 255') else: return bytearray(struct.pack(' 65535: raise ValueError('int2store requires 0 <= i <= 65535') else: return bytearray(struct.pack(' 16777215: raise ValueError('int3store requires 0 <= i <= 16777215') else: return bytearray(struct.pack(' 4294967295: raise ValueError('int4store requires 0 <= i <= 4294967295') else: return bytearray(struct.pack(' 18446744073709551616: raise ValueError('int8store requires 0 <= i <= 2^64') else: return bytearray(struct.pack(' 18446744073709551616: raise ValueError('intstore requires 0 <= i <= 2^64') if i <= 255: formed_string = int1store elif i <= 65535: formed_string = int2store elif i <= 16777215: formed_string = int3store elif i <= 4294967295: formed_string = int4store else: formed_string = int8store return formed_string(i) def lc_int(i): """ Takes an unsigned integer and packs it as bytes, with the information of how much bytes the encoded int takes. """ if i < 0 or i > 18446744073709551616: raise ValueError('Requires 0 <= i <= 2^64') if i < 251: return bytearray(struct.pack(' +----------+------------------------- | length | a string goes here +----------+------------------------- If the string is bigger than 250, then it looks like this: <- 1b -><- 2/3/8 -> +------+-----------+------------------------- | type | length | a string goes here +------+-----------+------------------------- if type == \xfc: length is code in next 2 bytes elif type == \xfd: length is code in next 3 bytes elif type == \xfe: length is code in next 8 bytes NULL has a special value. If the buffer starts with \xfb then it's a NULL and we return None as value. Returns a tuple (trucated buffer, bytes). """ if buf[0] == 251: # \xfb # NULL value return (buf[1:], None) length = lsize = 0 fst = buf[0] if fst <= 250: # \xFA length = fst return (buf[1 + length:], buf[1:length + 1]) elif fst == 252: lsize = 2 elif fst == 253: lsize = 3 if fst == 254: lsize = 8 length = intread(buf[1:lsize + 1]) return (buf[lsize + length + 1:], buf[lsize + 1:length + lsize + 1]) def read_lc_string_list(buf): """Reads all length encoded strings from the given buffer Returns a list of bytes """ byteslst = [] sizes = {252: 2, 253: 3, 254: 8} buf_len = len(buf) pos = 0 while pos < buf_len: first = buf[pos] if first == 255: # Special case when MySQL error 1317 is returned by MySQL. # We simply return None. return None if first == 251: # NULL value byteslst.append(None) pos += 1 else: if first <= 250: length = first byteslst.append(buf[(pos + 1):length + (pos + 1)]) pos += 1 + length else: lsize = 0 try: lsize = sizes[first] except KeyError: return None length = intread(buf[(pos + 1):lsize + (pos + 1)]) byteslst.append( buf[pos + 1 + lsize:length + lsize + (pos + 1)]) pos += 1 + lsize + length return tuple(byteslst) def read_string(buf, end=None, size=None): """ Reads a string up until a character or for a given size. Returns a tuple (trucated buffer, string). """ if end is None and size is None: raise ValueError('read_string() needs either end or size') if end is not None: try: idx = buf.index(end) except ValueError: raise ValueError("end byte not present in buffer") return (buf[idx + 1:], buf[0:idx]) elif size is not None: return read_bytes(buf, size) raise ValueError('read_string() needs either end or size (weird)') def read_int(buf, size): """Read an integer from buffer Returns a tuple (truncated buffer, int) """ try: res = intread(buf[0:size]) except: raise return (buf[size:], res) def read_lc_int(buf): """ Takes a buffer and reads an length code string from the start. Returns a tuple with buffer less the integer and the integer read. """ if not buf: raise ValueError("Empty buffer.") lcbyte = buf[0] if lcbyte == 251: return (buf[1:], None) elif lcbyte < 251: return (buf[1:], int(lcbyte)) elif lcbyte == 252: return (buf[3:], struct_unpack(' 0: digest = _digest_buffer(abuffer[0:limit]) else: digest = _digest_buffer(abuffer) print(prefix + ': ' + digest) else: print(_digest_buffer(abuffer)) mysql-connector-python-2.0.4/lib/mysql/connector/pooling.py0000644001577100000120000003023612506707765023410 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Implementing pooling of connections to MySQL servers. """ import re from uuid import uuid4 # pylint: disable=F0401 try: import queue except ImportError: # Python v2 import Queue as queue # pylint: enable=F0401 import threading from . import errors from .connection import MySQLConnection CONNECTION_POOL_LOCK = threading.RLock() CNX_POOL_ARGS = ('pool_name', 'pool_size', 'pool_reset_session') CNX_POOL_MAXSIZE = 32 CNX_POOL_MAXNAMESIZE = 64 CNX_POOL_NAMEREGEX = re.compile(r'[^a-zA-Z0-9._:\-*$#]') def generate_pool_name(**kwargs): """Generate a pool name This function takes keyword arguments, usually the connection arguments for MySQLConnection, and tries to generate a name for a pool. Raises PoolError when no name can be generated. Returns a string. """ parts = [] for key in ('host', 'port', 'user', 'database'): try: parts.append(str(kwargs[key])) except KeyError: pass if not parts: raise errors.PoolError( "Failed generating pool name; specify pool_name") return '_'.join(parts) class PooledMySQLConnection(object): """Class holding a MySQL Connection in a pool PooledMySQLConnection is used by MySQLConnectionPool to return an instance holding a MySQL connection. It works like a MySQLConnection except for methods like close() and config(). The close()-method will add the connection back to the pool rather than disconnecting from the MySQL server. Configuring the connection have to be done through the MySQLConnectionPool method set_config(). Using config() on pooled connection will raise a PoolError. """ def __init__(self, pool, cnx): """Initialize The pool argument must be an instance of MySQLConnectionPoll. cnx if an instance of MySQLConnection. """ if not isinstance(pool, MySQLConnectionPool): raise AttributeError( "pool should be a MySQLConnectionPool") if not isinstance(cnx, MySQLConnection): raise AttributeError( "cnx should be a MySQLConnection") self._cnx_pool = pool self._cnx = cnx def __getattr__(self, attr): """Calls attributes of the MySQLConnection instance""" return getattr(self._cnx, attr) def close(self): """Do not close, but add connection back to pool The close() method does not close the connection with the MySQL server. The connection is added back to the pool so it can be reused. When the pool is configured to reset the session, the session state will be cleared by re-authenticating the user. """ cnx = self._cnx if self._cnx_pool.reset_session: cnx.reset_session() self._cnx_pool.add_connection(cnx) self._cnx = None def config(self, **kwargs): """Configuration is done through the pool""" raise errors.PoolError( "Configuration for pooled connections should " "be done through the pool itself." ) @property def pool_name(self): """Return the name of the connection pool""" return self._cnx_pool.pool_name class MySQLConnectionPool(object): """Class defining a pool of MySQL connections""" def __init__(self, pool_size=5, pool_name=None, pool_reset_session=True, **kwargs): """Initialize Initialize a MySQL connection pool with a maximum number of connections set to pool_size. The rest of the keywords arguments, kwargs, are configuration arguments for MySQLConnection instances. """ self._pool_size = None self._pool_name = None self._reset_session = pool_reset_session self._set_pool_size(pool_size) self._set_pool_name(pool_name or generate_pool_name(**kwargs)) self._cnx_config = {} self._cnx_queue = queue.Queue(self._pool_size) self._config_version = uuid4() if kwargs: self.set_config(**kwargs) cnt = 0 while cnt < self._pool_size: self.add_connection() cnt += 1 @property def pool_name(self): """Return the name of the connection pool""" return self._pool_name @property def pool_size(self): """Return number of connections managed by the pool""" return self._pool_size @property def reset_session(self): """Return whether to reset session""" return self._reset_session def set_config(self, **kwargs): """Set the connection configuration for MySQLConnection instances This method sets the configuration used for creating MySQLConnection instances. See MySQLConnection for valid connection arguments. Raises PoolError when a connection argument is not valid, missing or not supported by MySQLConnection. """ if not kwargs: return with CONNECTION_POOL_LOCK: try: test_cnx = MySQLConnection() test_cnx.config(**kwargs) self._cnx_config = kwargs self._config_version = uuid4() except AttributeError as err: raise errors.PoolError( "Connection configuration not valid: {0}".format(err)) def _set_pool_size(self, pool_size): """Set the size of the pool This method sets the size of the pool but it will not resize the pool. Raises an AttributeError when the pool_size is not valid. Invalid size is 0, negative or higher than pooling.CNX_POOL_MAXSIZE. """ if pool_size <= 0 or pool_size > CNX_POOL_MAXSIZE: raise AttributeError( "Pool size should be higher than 0 and " "lower or equal to {0}".format(CNX_POOL_MAXSIZE)) self._pool_size = pool_size def _set_pool_name(self, pool_name): r"""Set the name of the pool This method checks the validity and sets the name of the pool. Raises an AttributeError when pool_name contains illegal characters ([^a-zA-Z0-9._\-*$#]) or is longer than pooling.CNX_POOL_MAXNAMESIZE. """ if CNX_POOL_NAMEREGEX.search(pool_name): raise AttributeError( "Pool name '{0}' contains illegal characters".format(pool_name)) if len(pool_name) > CNX_POOL_MAXNAMESIZE: raise AttributeError( "Pool name '{0}' is too long".format(pool_name)) self._pool_name = pool_name def _queue_connection(self, cnx): """Put connection back in the queue This method is putting a connection back in the queue. It will not acquire a lock as the methods using _queue_connection() will have it set. Raises PoolError on errors. """ if not isinstance(cnx, MySQLConnection): raise errors.PoolError( "Connection instance not subclass of MySQLConnection.") try: self._cnx_queue.put(cnx, block=False) except queue.Full: errors.PoolError("Failed adding connection; queue is full") def add_connection(self, cnx=None): """Add a connection to the pool This method instantiates a MySQLConnection using the configuration passed when initializing the MySQLConnectionPool instance or using the set_config() method. If cnx is a MySQLConnection instance, it will be added to the queue. Raises PoolError when no configuration is set, when no more connection can be added (maximum reached) or when the connection can not be instantiated. """ with CONNECTION_POOL_LOCK: if not self._cnx_config: raise errors.PoolError( "Connection configuration not available") if self._cnx_queue.full(): raise errors.PoolError( "Failed adding connection; queue is full") if not cnx: cnx = MySQLConnection(**self._cnx_config) try: if (self._reset_session and self._cnx_config['compress'] and cnx.get_server_version() < (5, 7, 3)): raise errors.NotSupportedError("Pool reset session is " "not supported with " "compression for MySQL " "server version 5.7.2 " "or earlier.") except KeyError: pass # pylint: disable=W0201,W0212 cnx._pool_config_version = self._config_version # pylint: enable=W0201,W0212 else: if not isinstance(cnx, MySQLConnection): raise errors.PoolError( "Connection instance not subclass of MySQLConnection.") self._queue_connection(cnx) def get_connection(self): """Get a connection from the pool This method returns an PooledMySQLConnection instance which has a reference to the pool that created it, and the next available MySQL connection. When the MySQL connection is not connect, a reconnect is attempted. Raises PoolError on errors. Returns a PooledMySQLConnection instance. """ with CONNECTION_POOL_LOCK: try: cnx = self._cnx_queue.get(block=False) except queue.Empty: raise errors.PoolError( "Failed getting connection; pool exhausted") # pylint: disable=W0201,W0212 if not cnx.is_connected() \ or self._config_version != cnx._pool_config_version: cnx.config(**self._cnx_config) try: cnx.reconnect() except errors.InterfaceError: # Failed to reconnect, give connection back to pool self._queue_connection(cnx) raise cnx._pool_config_version = self._config_version # pylint: enable=W0201,W0212 return PooledMySQLConnection(self, cnx) def _remove_connections(self): """Close all connections This method closes all connections. It returns the number of connections it closed. Used mostly for tests. Returns int. """ with CONNECTION_POOL_LOCK: cnt = 0 cnxq = self._cnx_queue while cnxq.qsize(): try: cnx = cnxq.get(block=False) cnx.disconnect() cnt += 1 except queue.Empty: return cnt except errors.PoolError: raise except errors.Error: # Any other error when closing means connection is closed pass return cnt mysql-connector-python-2.0.4/lib/mysql/connector/version.py0000644001577100000120000000303012506707765023416 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """MySQL Connector/Python version information The file version.py gets installed and is available after installation as mysql.connector.version. """ VERSION = (2, 0, 4, '', 0) if VERSION[3] and VERSION[4]: VERSION_TEXT = '{0}.{1}.{2}{3}{4}'.format(*VERSION) else: VERSION_TEXT = '{0}.{1}.{2}'.format(*VERSION[0:3]) LICENSE = 'GPLv2 with FOSS License Exception' EDITION = '' # Added in package names, after the version mysql-connector-python-2.0.4/lib/mysql/connector/protocol.py0000644001577100000120000006420012506707765023600 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Implements the MySQL Client/Server protocol """ import struct import datetime from decimal import Decimal from .constants import ( FieldFlag, ServerCmd, FieldType, ClientFlag) from . import errors, utils from .authentication import get_auth_plugin from .catch23 import PY2, struct_unpack class MySQLProtocol(object): """Implements MySQL client/server protocol Create and parses MySQL packets. """ def _connect_with_db(self, client_flags, database): """Prepare database string for handshake response""" if client_flags & ClientFlag.CONNECT_WITH_DB and database: return database.encode('utf8') + b'\x00' return b'\x00' def _auth_response(self, client_flags, username, password, database, auth_plugin, auth_data, ssl_enabled): """Prepare the authentication response""" if not password: return b'\x00' try: auth = get_auth_plugin(auth_plugin)( auth_data, username=username, password=password, database=database, ssl_enabled=ssl_enabled) plugin_auth_response = auth.auth_response() except (TypeError, errors.InterfaceError) as exc: raise errors.ProgrammingError( "Failed authentication: {0}".format(str(exc))) if client_flags & ClientFlag.SECURE_CONNECTION: resplen = len(plugin_auth_response) auth_response = struct.pack('= 7: mcs = 0 if length == 11: mcs = struct_unpack('I', packet[8:length + 1])[0] value = datetime.datetime( year=struct_unpack('H', packet[1:3])[0], month=packet[3], day=packet[4], hour=packet[5], minute=packet[6], second=packet[7], microsecond=mcs) return (packet[length + 1:], value) def _parse_binary_time(self, packet, field): """Parse a time value from a binary packet""" length = packet[0] data = packet[1:length + 1] mcs = 0 if length > 8: mcs = struct_unpack('I', data[8:])[0] days = struct_unpack('I', data[1:5])[0] if data[0] == 1: days *= -1 tmp = datetime.timedelta(days=days, seconds=data[7], microseconds=mcs, minutes=data[6], hours=data[5]) return (packet[length + 1:], tmp) def _parse_binary_values(self, fields, packet): """Parse values from a binary result packet""" null_bitmap_length = (len(fields) + 7 + 2) // 8 null_bitmap = [int(i) for i in packet[0:null_bitmap_length]] packet = packet[null_bitmap_length:] values = [] for pos, field in enumerate(fields): if null_bitmap[int((pos+2)/8)] & (1 << (pos + 2) % 8): values.append(None) continue elif field[1] in (FieldType.TINY, FieldType.SHORT, FieldType.INT24, FieldType.LONG, FieldType.LONGLONG): (packet, value) = self._parse_binary_integer(packet, field) values.append(value) elif field[1] in (FieldType.DOUBLE, FieldType.FLOAT): (packet, value) = self._parse_binary_float(packet, field) values.append(value) elif field[1] in (FieldType.DATETIME, FieldType.DATE, FieldType.TIMESTAMP): (packet, value) = self._parse_binary_timestamp(packet, field) values.append(value) elif field[1] == FieldType.TIME: (packet, value) = self._parse_binary_time(packet, field) values.append(value) else: (packet, value) = utils.read_lc_string(packet) values.append(value) return tuple(values) def read_binary_result(self, sock, columns, count=1): """Read MySQL binary protocol result Reads all or given number of binary resultset rows from the socket. """ rows = [] eof = None values = None i = 0 while True: if eof is not None: break if i == count: break packet = sock.recv() if packet[4] == 254: eof = self.parse_eof(packet) values = None elif packet[4] == 0: eof = None values = self._parse_binary_values(columns, packet[5:]) if eof is None and values is not None: rows.append(values) i += 1 return (rows, eof) def parse_binary_prepare_ok(self, packet): """Parse a MySQL Binary Protocol OK packet""" if not packet[4] == 0: raise errors.InterfaceError("Failed parsing Binary OK packet") ok_pkt = {} try: (packet, ok_pkt['statement_id']) = utils.read_int(packet[5:], 4) (packet, ok_pkt['num_columns']) = utils.read_int(packet, 2) (packet, ok_pkt['num_params']) = utils.read_int(packet, 2) packet = packet[1:] # Filler 1 * \x00 (packet, ok_pkt['warning_count']) = utils.read_int(packet, 2) except ValueError: raise errors.InterfaceError("Failed parsing Binary OK packet") return ok_pkt def _prepare_binary_integer(self, value): """Prepare an integer for the MySQL binary protocol""" field_type = None flags = 0 if value < 0: if value >= -128: format_ = 'b' field_type = FieldType.TINY elif value >= -32768: format_ = 'h' field_type = FieldType.SHORT elif value >= -2147483648: format_ = 'i' field_type = FieldType.LONG else: format_ = 'q' field_type = FieldType.LONGLONG else: flags = 128 if value <= 255: format_ = 'B' field_type = FieldType.TINY elif value <= 65535: format_ = 'H' field_type = FieldType.SHORT elif value <= 4294967295: format_ = 'I' field_type = FieldType.LONG else: field_type = FieldType.LONGLONG format_ = 'Q' return (struct.pack(format_, value), field_type, flags) def _prepare_binary_timestamp(self, value): """Prepare a timestamp object for the MySQL binary protocol This method prepares a timestamp of type datetime.datetime or datetime.date for sending over the MySQL binary protocol. A tuple is returned with the prepared value and field type as elements. Raises ValueError when the argument value is of invalid type. Returns a tuple. """ if isinstance(value, datetime.datetime): field_type = FieldType.DATETIME elif isinstance(value, datetime.date): field_type = FieldType.DATE else: raise ValueError( "Argument must a datetime.datetime or datetime.date") packed = (utils.int2store(value.year) + utils.int1store(value.month) + utils.int1store(value.day)) if isinstance(value, datetime.datetime): packed = (packed + utils.int1store(value.hour) + utils.int1store(value.minute) + utils.int1store(value.second)) if value.microsecond > 0: packed += utils.int4store(value.microsecond) packed = utils.int1store(len(packed)) + packed return (packed, field_type) def _prepare_binary_time(self, value): """Prepare a time object for the MySQL binary protocol This method prepares a time object of type datetime.timedelta or datetime.time for sending over the MySQL binary protocol. A tuple is returned with the prepared value and field type as elements. Raises ValueError when the argument value is of invalid type. Returns a tuple. """ if not isinstance(value, (datetime.timedelta, datetime.time)): raise ValueError( "Argument must a datetime.timedelta or datetime.time") field_type = FieldType.TIME negative = 0 mcs = None packed = b'' if isinstance(value, datetime.timedelta): if value.days < 0: negative = 1 (hours, remainder) = divmod(value.seconds, 3600) (mins, secs) = divmod(remainder, 60) packed += (utils.int4store(abs(value.days)) + utils.int1store(hours) + utils.int1store(mins) + utils.int1store(secs)) mcs = value.microseconds else: packed += (utils.int4store(0) + utils.int1store(value.hour) + utils.int1store(value.minute) + utils.int1store(value.second)) mcs = value.microsecond if mcs: packed += utils.int4store(mcs) packed = utils.int1store(negative) + packed packed = utils.int1store(len(packed)) + packed return (packed, field_type) def _prepare_stmt_send_long_data(self, statement, param, data): """Prepare long data for prepared statements Returns a string. """ packet = ( utils.int4store(statement) + utils.int2store(param) + data) return packet def make_stmt_execute(self, statement_id, data=(), parameters=(), flags=0, long_data_used=None, charset='utf8'): """Make a MySQL packet with the Statement Execute command""" iteration_count = 1 null_bitmap = [0] * ((len(data) + 7) // 8) values = [] types = [] packed = b'' if long_data_used is None: long_data_used = {} if parameters and data: if len(data) != len(parameters): raise errors.InterfaceError( "Failed executing prepared statement: data values does not" " match number of parameters") for pos, _ in enumerate(parameters): value = data[pos] flags = 0 if value is None: null_bitmap[(pos // 8)] |= 1 << (pos % 8) types.append(utils.int1store(FieldType.NULL) + utils.int1store(flags)) continue elif pos in long_data_used: if long_data_used[pos][0]: # We suppose binary data field_type = FieldType.BLOB else: # We suppose text data field_type = FieldType.STRING elif isinstance(value, int): (packed, field_type, flags) = self._prepare_binary_integer(value) values.append(packed) elif isinstance(value, str): if PY2: values.append(utils.lc_int(len(value)) + value) else: value = value.encode(charset) values.append( utils.lc_int(len(value)) + value) field_type = FieldType.VARCHAR elif isinstance(value, bytes): values.append(utils.lc_int(len(value)) + value) field_type = FieldType.BLOB elif PY2 and \ isinstance(value, unicode): # pylint: disable=E0602 value = value.encode(charset) values.append(utils.lc_int(len(value)) + value) field_type = FieldType.VARCHAR elif isinstance(value, Decimal): values.append( utils.lc_int(len(str(value).encode( charset))) + str(value).encode(charset)) field_type = FieldType.DECIMAL elif isinstance(value, float): values.append(struct.pack('d', value)) field_type = FieldType.DOUBLE elif isinstance(value, (datetime.datetime, datetime.date)): (packed, field_type) = self._prepare_binary_timestamp( value) values.append(packed) elif isinstance(value, (datetime.timedelta, datetime.time)): (packed, field_type) = self._prepare_binary_time(value) values.append(packed) else: raise errors.ProgrammingError( "MySQL binary protocol can not handle " "'{classname}' objects".format( classname=value.__class__.__name__)) types.append(utils.int1store(field_type) + utils.int1store(flags)) packet = ( utils.int4store(statement_id) + utils.int1store(flags) + utils.int4store(iteration_count) + b''.join([struct.pack('B', bit) for bit in null_bitmap]) + utils.int1store(1) ) for a_type in types: packet += a_type for a_value in values: packet += a_value return packet def parse_auth_switch_request(self, packet): """Parse a MySQL AuthSwitchRequest-packet""" if not packet[4] == 254: raise errors.InterfaceError( "Failed parsing AuthSwitchRequest packet") (packet, plugin_name) = utils.read_string(packet[5:], end=b'\x00') if packet[-1] == 0: packet = packet[:-1] return plugin_name.decode('utf8'), packet def parse_auth_more_data(self, packet): """Parse a MySQL AuthMoreData-packet""" if not packet[4] == 1: raise errors.InterfaceError( "Failed parsing AuthMoreData packet") return packet[5:] mysql-connector-python-2.0.4/lib/mysql/connector/charsets.py0000644001577100000120000003004612506707765023554 0ustar pb2userwheel# -*- coding: utf-8 -*- # MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 # This file was auto-generated. _GENERATED_ON = '2014-05-23' _MYSQL_VERSION = (5, 7, 4) """This module contains the MySQL Server Character Sets""" MYSQL_CHARACTER_SETS = [ # (character set name, collation, default) None, ("big5", "big5_chinese_ci", True), # 1 ("latin2", "latin2_czech_cs", False), # 2 ("dec8", "dec8_swedish_ci", True), # 3 ("cp850", "cp850_general_ci", True), # 4 ("latin1", "latin1_german1_ci", False), # 5 ("hp8", "hp8_english_ci", True), # 6 ("koi8r", "koi8r_general_ci", True), # 7 ("latin1", "latin1_swedish_ci", True), # 8 ("latin2", "latin2_general_ci", True), # 9 ("swe7", "swe7_swedish_ci", True), # 10 ("ascii", "ascii_general_ci", True), # 11 ("ujis", "ujis_japanese_ci", True), # 12 ("sjis", "sjis_japanese_ci", True), # 13 ("cp1251", "cp1251_bulgarian_ci", False), # 14 ("latin1", "latin1_danish_ci", False), # 15 ("hebrew", "hebrew_general_ci", True), # 16 None, ("tis620", "tis620_thai_ci", True), # 18 ("euckr", "euckr_korean_ci", True), # 19 ("latin7", "latin7_estonian_cs", False), # 20 ("latin2", "latin2_hungarian_ci", False), # 21 ("koi8u", "koi8u_general_ci", True), # 22 ("cp1251", "cp1251_ukrainian_ci", False), # 23 ("gb2312", "gb2312_chinese_ci", True), # 24 ("greek", "greek_general_ci", True), # 25 ("cp1250", "cp1250_general_ci", True), # 26 ("latin2", "latin2_croatian_ci", False), # 27 ("gbk", "gbk_chinese_ci", True), # 28 ("cp1257", "cp1257_lithuanian_ci", False), # 29 ("latin5", "latin5_turkish_ci", True), # 30 ("latin1", "latin1_german2_ci", False), # 31 ("armscii8", "armscii8_general_ci", True), # 32 ("utf8", "utf8_general_ci", True), # 33 ("cp1250", "cp1250_czech_cs", False), # 34 ("ucs2", "ucs2_general_ci", True), # 35 ("cp866", "cp866_general_ci", True), # 36 ("keybcs2", "keybcs2_general_ci", True), # 37 ("macce", "macce_general_ci", True), # 38 ("macroman", "macroman_general_ci", True), # 39 ("cp852", "cp852_general_ci", True), # 40 ("latin7", "latin7_general_ci", True), # 41 ("latin7", "latin7_general_cs", False), # 42 ("macce", "macce_bin", False), # 43 ("cp1250", "cp1250_croatian_ci", False), # 44 ("utf8mb4", "utf8mb4_general_ci", True), # 45 ("utf8mb4", "utf8mb4_bin", False), # 46 ("latin1", "latin1_bin", False), # 47 ("latin1", "latin1_general_ci", False), # 48 ("latin1", "latin1_general_cs", False), # 49 ("cp1251", "cp1251_bin", False), # 50 ("cp1251", "cp1251_general_ci", True), # 51 ("cp1251", "cp1251_general_cs", False), # 52 ("macroman", "macroman_bin", False), # 53 ("utf16", "utf16_general_ci", True), # 54 ("utf16", "utf16_bin", False), # 55 ("utf16le", "utf16le_general_ci", True), # 56 ("cp1256", "cp1256_general_ci", True), # 57 ("cp1257", "cp1257_bin", False), # 58 ("cp1257", "cp1257_general_ci", True), # 59 ("utf32", "utf32_general_ci", True), # 60 ("utf32", "utf32_bin", False), # 61 ("utf16le", "utf16le_bin", False), # 62 ("binary", "binary", True), # 63 ("armscii8", "armscii8_bin", False), # 64 ("ascii", "ascii_bin", False), # 65 ("cp1250", "cp1250_bin", False), # 66 ("cp1256", "cp1256_bin", False), # 67 ("cp866", "cp866_bin", False), # 68 ("dec8", "dec8_bin", False), # 69 ("greek", "greek_bin", False), # 70 ("hebrew", "hebrew_bin", False), # 71 ("hp8", "hp8_bin", False), # 72 ("keybcs2", "keybcs2_bin", False), # 73 ("koi8r", "koi8r_bin", False), # 74 ("koi8u", "koi8u_bin", False), # 75 None, ("latin2", "latin2_bin", False), # 77 ("latin5", "latin5_bin", False), # 78 ("latin7", "latin7_bin", False), # 79 ("cp850", "cp850_bin", False), # 80 ("cp852", "cp852_bin", False), # 81 ("swe7", "swe7_bin", False), # 82 ("utf8", "utf8_bin", False), # 83 ("big5", "big5_bin", False), # 84 ("euckr", "euckr_bin", False), # 85 ("gb2312", "gb2312_bin", False), # 86 ("gbk", "gbk_bin", False), # 87 ("sjis", "sjis_bin", False), # 88 ("tis620", "tis620_bin", False), # 89 ("ucs2", "ucs2_bin", False), # 90 ("ujis", "ujis_bin", False), # 91 ("geostd8", "geostd8_general_ci", True), # 92 ("geostd8", "geostd8_bin", False), # 93 ("latin1", "latin1_spanish_ci", False), # 94 ("cp932", "cp932_japanese_ci", True), # 95 ("cp932", "cp932_bin", False), # 96 ("eucjpms", "eucjpms_japanese_ci", True), # 97 ("eucjpms", "eucjpms_bin", False), # 98 ("cp1250", "cp1250_polish_ci", False), # 99 None, ("utf16", "utf16_unicode_ci", False), # 101 ("utf16", "utf16_icelandic_ci", False), # 102 ("utf16", "utf16_latvian_ci", False), # 103 ("utf16", "utf16_romanian_ci", False), # 104 ("utf16", "utf16_slovenian_ci", False), # 105 ("utf16", "utf16_polish_ci", False), # 106 ("utf16", "utf16_estonian_ci", False), # 107 ("utf16", "utf16_spanish_ci", False), # 108 ("utf16", "utf16_swedish_ci", False), # 109 ("utf16", "utf16_turkish_ci", False), # 110 ("utf16", "utf16_czech_ci", False), # 111 ("utf16", "utf16_danish_ci", False), # 112 ("utf16", "utf16_lithuanian_ci", False), # 113 ("utf16", "utf16_slovak_ci", False), # 114 ("utf16", "utf16_spanish2_ci", False), # 115 ("utf16", "utf16_roman_ci", False), # 116 ("utf16", "utf16_persian_ci", False), # 117 ("utf16", "utf16_esperanto_ci", False), # 118 ("utf16", "utf16_hungarian_ci", False), # 119 ("utf16", "utf16_sinhala_ci", False), # 120 ("utf16", "utf16_german2_ci", False), # 121 ("utf16", "utf16_croatian_ci", False), # 122 ("utf16", "utf16_unicode_520_ci", False), # 123 ("utf16", "utf16_vietnamese_ci", False), # 124 None, None, None, ("ucs2", "ucs2_unicode_ci", False), # 128 ("ucs2", "ucs2_icelandic_ci", False), # 129 ("ucs2", "ucs2_latvian_ci", False), # 130 ("ucs2", "ucs2_romanian_ci", False), # 131 ("ucs2", "ucs2_slovenian_ci", False), # 132 ("ucs2", "ucs2_polish_ci", False), # 133 ("ucs2", "ucs2_estonian_ci", False), # 134 ("ucs2", "ucs2_spanish_ci", False), # 135 ("ucs2", "ucs2_swedish_ci", False), # 136 ("ucs2", "ucs2_turkish_ci", False), # 137 ("ucs2", "ucs2_czech_ci", False), # 138 ("ucs2", "ucs2_danish_ci", False), # 139 ("ucs2", "ucs2_lithuanian_ci", False), # 140 ("ucs2", "ucs2_slovak_ci", False), # 141 ("ucs2", "ucs2_spanish2_ci", False), # 142 ("ucs2", "ucs2_roman_ci", False), # 143 ("ucs2", "ucs2_persian_ci", False), # 144 ("ucs2", "ucs2_esperanto_ci", False), # 145 ("ucs2", "ucs2_hungarian_ci", False), # 146 ("ucs2", "ucs2_sinhala_ci", False), # 147 ("ucs2", "ucs2_german2_ci", False), # 148 ("ucs2", "ucs2_croatian_ci", False), # 149 ("ucs2", "ucs2_unicode_520_ci", False), # 150 ("ucs2", "ucs2_vietnamese_ci", False), # 151 None, None, None, None, None, None, None, ("ucs2", "ucs2_general_mysql500_ci", False), # 159 ("utf32", "utf32_unicode_ci", False), # 160 ("utf32", "utf32_icelandic_ci", False), # 161 ("utf32", "utf32_latvian_ci", False), # 162 ("utf32", "utf32_romanian_ci", False), # 163 ("utf32", "utf32_slovenian_ci", False), # 164 ("utf32", "utf32_polish_ci", False), # 165 ("utf32", "utf32_estonian_ci", False), # 166 ("utf32", "utf32_spanish_ci", False), # 167 ("utf32", "utf32_swedish_ci", False), # 168 ("utf32", "utf32_turkish_ci", False), # 169 ("utf32", "utf32_czech_ci", False), # 170 ("utf32", "utf32_danish_ci", False), # 171 ("utf32", "utf32_lithuanian_ci", False), # 172 ("utf32", "utf32_slovak_ci", False), # 173 ("utf32", "utf32_spanish2_ci", False), # 174 ("utf32", "utf32_roman_ci", False), # 175 ("utf32", "utf32_persian_ci", False), # 176 ("utf32", "utf32_esperanto_ci", False), # 177 ("utf32", "utf32_hungarian_ci", False), # 178 ("utf32", "utf32_sinhala_ci", False), # 179 ("utf32", "utf32_german2_ci", False), # 180 ("utf32", "utf32_croatian_ci", False), # 181 ("utf32", "utf32_unicode_520_ci", False), # 182 ("utf32", "utf32_vietnamese_ci", False), # 183 None, None, None, None, None, None, None, None, ("utf8", "utf8_unicode_ci", False), # 192 ("utf8", "utf8_icelandic_ci", False), # 193 ("utf8", "utf8_latvian_ci", False), # 194 ("utf8", "utf8_romanian_ci", False), # 195 ("utf8", "utf8_slovenian_ci", False), # 196 ("utf8", "utf8_polish_ci", False), # 197 ("utf8", "utf8_estonian_ci", False), # 198 ("utf8", "utf8_spanish_ci", False), # 199 ("utf8", "utf8_swedish_ci", False), # 200 ("utf8", "utf8_turkish_ci", False), # 201 ("utf8", "utf8_czech_ci", False), # 202 ("utf8", "utf8_danish_ci", False), # 203 ("utf8", "utf8_lithuanian_ci", False), # 204 ("utf8", "utf8_slovak_ci", False), # 205 ("utf8", "utf8_spanish2_ci", False), # 206 ("utf8", "utf8_roman_ci", False), # 207 ("utf8", "utf8_persian_ci", False), # 208 ("utf8", "utf8_esperanto_ci", False), # 209 ("utf8", "utf8_hungarian_ci", False), # 210 ("utf8", "utf8_sinhala_ci", False), # 211 ("utf8", "utf8_german2_ci", False), # 212 ("utf8", "utf8_croatian_ci", False), # 213 ("utf8", "utf8_unicode_520_ci", False), # 214 ("utf8", "utf8_vietnamese_ci", False), # 215 None, None, None, None, None, None, None, ("utf8", "utf8_general_mysql500_ci", False), # 223 ("utf8mb4", "utf8mb4_unicode_ci", False), # 224 ("utf8mb4", "utf8mb4_icelandic_ci", False), # 225 ("utf8mb4", "utf8mb4_latvian_ci", False), # 226 ("utf8mb4", "utf8mb4_romanian_ci", False), # 227 ("utf8mb4", "utf8mb4_slovenian_ci", False), # 228 ("utf8mb4", "utf8mb4_polish_ci", False), # 229 ("utf8mb4", "utf8mb4_estonian_ci", False), # 230 ("utf8mb4", "utf8mb4_spanish_ci", False), # 231 ("utf8mb4", "utf8mb4_swedish_ci", False), # 232 ("utf8mb4", "utf8mb4_turkish_ci", False), # 233 ("utf8mb4", "utf8mb4_czech_ci", False), # 234 ("utf8mb4", "utf8mb4_danish_ci", False), # 235 ("utf8mb4", "utf8mb4_lithuanian_ci", False), # 236 ("utf8mb4", "utf8mb4_slovak_ci", False), # 237 ("utf8mb4", "utf8mb4_spanish2_ci", False), # 238 ("utf8mb4", "utf8mb4_roman_ci", False), # 239 ("utf8mb4", "utf8mb4_persian_ci", False), # 240 ("utf8mb4", "utf8mb4_esperanto_ci", False), # 241 ("utf8mb4", "utf8mb4_hungarian_ci", False), # 242 ("utf8mb4", "utf8mb4_sinhala_ci", False), # 243 ("utf8mb4", "utf8mb4_german2_ci", False), # 244 ("utf8mb4", "utf8mb4_croatian_ci", False), # 245 ("utf8mb4", "utf8mb4_unicode_520_ci", False), # 246 ("utf8mb4", "utf8mb4_vietnamese_ci", False), # 247 ("gb18030", "gb18030_chinese_ci", True), # 248 ("gb18030", "gb18030_bin", False), # 249 ("gb18030", "gb18030_unicode_520_ci", False), # 250 ] mysql-connector-python-2.0.4/lib/mysql/connector/catch23.py0000644001577100000120000000526512506707765023174 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Python v2 to v3 migration module""" from decimal import Decimal import struct import sys from .custom_types import HexLiteral # pylint: disable=E0602,E1103 PY2 = sys.version_info[0] == 2 if PY2: NUMERIC_TYPES = (int, float, Decimal, HexLiteral, long) UNICODE_TYPES = (unicode,) # This should not contain str else: NUMERIC_TYPES = (int, float, Decimal, HexLiteral) UNICODE_TYPES = (str,) def init_bytearray(payload=b'', encoding='utf-8'): """Initializes a bytearray from the payload""" if isinstance(payload, bytearray): return payload if PY2: return bytearray(payload) if isinstance(payload, int): return bytearray(payload) elif not isinstance(payload, bytes): try: return bytearray(payload.encode(encoding=encoding)) except AttributeError: raise ValueError("payload must be a str or bytes") return bytearray(payload) def isstr(obj): """Returns whether a variable is a string""" if PY2: return isinstance(obj, basestring) else: return isinstance(obj, str) def isunicode(obj): """Returns whether a variable is a of unicode type""" if PY2: return isinstance(obj, unicode) else: return isinstance(obj, str) if PY2: def struct_unpack(fmt, buf): """Wrapper around struct.unpack handling buffer as bytes and strings""" if isinstance(buf, (bytearray, bytes)): return struct.unpack_from(fmt, buffer(buf)) return struct.unpack_from(fmt, buf) else: struct_unpack = struct.unpack # pylint: disable=C0103 mysql-connector-python-2.0.4/lib/mysql/connector/custom_types.py0000644001577100000120000000320112506707765024467 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Custom Python types used by MySQL Connector/Python""" import sys class HexLiteral(str): """Class holding MySQL hex literals""" def __new__(cls, str_, charset='utf8'): if sys.version_info[0] == 2: hexed = ["%02x" % ord(i) for i in str_.encode(charset)] else: hexed = ["%02x" % i for i in str_.encode(charset)] obj = str.__new__(cls, ''.join(hexed)) obj.charset = charset obj.original = str_ return obj def __str__(self): return '0x' + self mysql-connector-python-2.0.4/lib/mysql/connector/constants.py0000644001577100000120000005255212506707765023762 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """ from .errors import ProgrammingError from .charsets import MYSQL_CHARACTER_SETS MAX_PACKET_LENGTH = 16777215 NET_BUFFER_LENGTH = 8192 def flag_is_set(flag, flags): """Checks if the flag is set Returns boolean""" if (flags & flag) > 0: return True return False class _Constants(object): """ Base class for constants """ prefix = '' desc = {} def __new__(cls): raise TypeError("Can not instanciate from %s" % cls.__name__) @classmethod def get_desc(cls, name): """Get description of given constant""" try: return cls.desc[name][1] except: return None @classmethod def get_info(cls, num): """Get information about given constant""" for name, info in cls.desc.items(): if info[0] == num: return name return None @classmethod def get_full_info(cls): """get full information about given constant""" res = () try: res = ["%s : %s" % (k, v[1]) for k, v in cls.desc.items()] except Exception as err: # pylint: disable=W0703 res = ('No information found in constant class.%s' % err) return res class _Flags(_Constants): """Base class for classes describing flags """ @classmethod def get_bit_info(cls, value): """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 """ prefix = 'FIELD_TYPE_' DECIMAL = 0x00 TINY = 0x01 SHORT = 0x02 LONG = 0x03 FLOAT = 0x04 DOUBLE = 0x05 NULL = 0x06 TIMESTAMP = 0x07 LONGLONG = 0x08 INT24 = 0x09 DATE = 0x0a TIME = 0x0b DATETIME = 0x0c YEAR = 0x0d NEWDATE = 0x0e VARCHAR = 0x0f BIT = 0x10 NEWDECIMAL = 0xf6 ENUM = 0xf7 SET = 0xf8 TINY_BLOB = 0xf9 MEDIUM_BLOB = 0xfa LONG_BLOB = 0xfb BLOB = 0xfc VAR_STRING = 0xfd STRING = 0xfe GEOMETRY = 0xff desc = { 'DECIMAL': (0x00, 'DECIMAL'), 'TINY': (0x01, 'TINY'), 'SHORT': (0x02, 'SHORT'), 'LONG': (0x03, 'LONG'), 'FLOAT': (0x04, 'FLOAT'), 'DOUBLE': (0x05, 'DOUBLE'), 'NULL': (0x06, 'NULL'), 'TIMESTAMP': (0x07, 'TIMESTAMP'), 'LONGLONG': (0x08, 'LONGLONG'), 'INT24': (0x09, 'INT24'), 'DATE': (0x0a, 'DATE'), 'TIME': (0x0b, 'TIME'), 'DATETIME': (0x0c, 'DATETIME'), 'YEAR': (0x0d, 'YEAR'), 'NEWDATE': (0x0e, 'NEWDATE'), 'VARCHAR': (0x0f, 'VARCHAR'), 'BIT': (0x10, 'BIT'), 'NEWDECIMAL': (0xf6, 'NEWDECIMAL'), 'ENUM': (0xf7, 'ENUM'), 'SET': (0xf8, 'SET'), 'TINY_BLOB': (0xf9, 'TINY_BLOB'), 'MEDIUM_BLOB': (0xfa, 'MEDIUM_BLOB'), 'LONG_BLOB': (0xfb, 'LONG_BLOB'), 'BLOB': (0xfc, 'BLOB'), 'VAR_STRING': (0xfd, 'VAR_STRING'), 'STRING': (0xfe, 'STRING'), 'GEOMETRY': (0xff, 'GEOMETRY'), } @classmethod def get_string_types(cls): """Get the list of all string types""" return [ cls.VARCHAR, cls.ENUM, cls.VAR_STRING, cls.STRING, ] @classmethod def get_binary_types(cls): """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): """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): """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 = '' NOT_NULL = 1 << 0 PRI_KEY = 1 << 1 UNIQUE_KEY = 1 << 2 MULTIPLE_KEY = 1 << 3 BLOB = 1 << 4 UNSIGNED = 1 << 5 ZEROFILL = 1 << 6 BINARY = 1 << 7 ENUM = 1 << 8 AUTO_INCREMENT = 1 << 9 TIMESTAMP = 1 << 10 SET = 1 << 11 NO_DEFAULT_VALUE = 1 << 12 ON_UPDATE_NOW = 1 << 13 NUM = 1 << 14 PART_KEY = 1 << 15 GROUP = 1 << 14 # SAME AS NUM !!!!!!!???? UNIQUE = 1 << 16 BINCMP = 1 << 17 GET_FIXED_FIELDS = 1 << 18 FIELD_IN_PART_FUNC = 1 << 19 FIELD_IN_ADD_INDEX = 1 << 20 FIELD_IS_RENAMED = 1 << 21 desc = { '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 ServerCmd(_Constants): """MySQL Server Commands """ _prefix = 'COM_' SLEEP = 0 QUIT = 1 INIT_DB = 2 QUERY = 3 FIELD_LIST = 4 CREATE_DB = 5 DROP_DB = 6 REFRESH = 7 SHUTDOWN = 8 STATISTICS = 9 PROCESS_INFO = 10 CONNECT = 11 PROCESS_KILL = 12 DEBUG = 13 PING = 14 TIME = 15 DELAYED_INSERT = 16 CHANGE_USER = 17 BINLOG_DUMP = 18 TABLE_DUMP = 19 CONNECT_OUT = 20 REGISTER_SLAVE = 21 STMT_PREPARE = 22 STMT_EXECUTE = 23 STMT_SEND_LONG_DATA = 24 STMT_CLOSE = 25 STMT_RESET = 26 SET_OPTION = 27 STMT_FETCH = 28 DAEMON = 29 BINLOG_DUMP_GTID = 30 RESET_CONNECTION = 31 desc = { '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_SLAVE': (21, 'REGISTER_SLAVE'), '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 """ LONG_PASSWD = 1 << 0 FOUND_ROWS = 1 << 1 LONG_FLAG = 1 << 2 CONNECT_WITH_DB = 1 << 3 NO_SCHEMA = 1 << 4 COMPRESS = 1 << 5 ODBC = 1 << 6 LOCAL_FILES = 1 << 7 IGNORE_SPACE = 1 << 8 PROTOCOL_41 = 1 << 9 INTERACTIVE = 1 << 10 SSL = 1 << 11 IGNORE_SIGPIPE = 1 << 12 TRANSACTIONS = 1 << 13 RESERVED = 1 << 14 SECURE_CONNECTION = 1 << 15 MULTI_STATEMENTS = 1 << 16 MULTI_RESULTS = 1 << 17 PS_MULTI_RESULTS = 1 << 18 PLUGIN_AUTH = 1 << 19 CONNECT_ARGS = 1 << 20 PLUGIN_AUTH_LENENC_CLIENT_DATA = 1 << 21 CAN_HANDLE_EXPIRED_PASSWORDS = 1 << 22 SSL_VERIFY_SERVER_CERT = 1 << 30 REMEMBER_OPTIONS = 1 << 31 desc = { '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'), 'SSL_VERIFY_SERVER_CERT': (1 << 30, ''), 'REMEMBER_OPTIONS': (1 << 31, ''), } default = [ LONG_PASSWD, LONG_FLAG, CONNECT_WITH_DB, PROTOCOL_41, TRANSACTIONS, SECURE_CONNECTION, MULTI_STATEMENTS, MULTI_RESULTS, LOCAL_FILES, ] @classmethod def get_default(cls): """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 = 'SERVER_' STATUS_IN_TRANS = 1 << 0 STATUS_AUTOCOMMIT = 1 << 1 MORE_RESULTS_EXISTS = 1 << 3 QUERY_NO_GOOD_INDEX_USED = 1 << 4 QUERY_NO_INDEX_USED = 1 << 5 STATUS_CURSOR_EXISTS = 1 << 6 STATUS_LAST_ROW_SENT = 1 << 7 STATUS_DB_DROPPED = 1 << 8 STATUS_NO_BACKSLASH_ESCAPES = 1 << 9 desc = { '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, ''), 'SERVER_STATUS_LAST_ROW_SENT': (1 << 7, ''), 'SERVER_STATUS_DB_DROPPED': (1 << 8, 'A database was dropped'), 'SERVER_STATUS_NO_BACKSLASH_ESCAPES': (1 << 9, ''), } class RefreshOption(_Constants): """MySQL Refresh command options Options used when sending the COM_REFRESH server command. """ _prefix = 'REFRESH_' GRANT = 1 << 0 LOG = 1 << 1 TABLES = 1 << 2 HOST = 1 << 3 STATUS = 1 << 4 THREADS = 1 << 5 SLAVE = 1 << 6 desc = { 'GRANT': (1 << 0, 'Refresh grant tables'), 'LOG': (1 << 1, 'Start on new log file'), 'TABLES': (1 << 2, 'close all tables'), 'HOSTS': (1 << 3, 'Flush host cache'), 'STATUS': (1 << 4, 'Flush status variables'), 'THREADS': (1 << 5, 'Flush thread cache'), 'SLAVE': (1 << 6, 'Reset master info and restart slave thread'), } class ShutdownType(_Constants): """MySQL Shutdown types Shutdown types used by the COM_SHUTDOWN server command. """ _prefix = '' SHUTDOWN_DEFAULT = b'\x00' SHUTDOWN_WAIT_CONNECTIONS = b'\x01' SHUTDOWN_WAIT_TRANSACTIONS = b'\x02' SHUTDOWN_WAIT_UPDATES = b'\x08' SHUTDOWN_WAIT_ALL_BUFFERS = b'\x10' SHUTDOWN_WAIT_CRITICAL_BUFFERS = b'\x11' KILL_QUERY = b'\xfe' KILL_CONNECTION = b'\xff' desc = { '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(_Constants): """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. """ desc = MYSQL_CHARACTER_SETS # Multi-byte character sets which use 5c (backslash) in characters slash_charsets = (1, 13, 28, 84, 87, 88) @classmethod def get_info(cls, setid): """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 cls.desc[setid][0:2] except IndexError: raise ProgrammingError( "Character set '{0}' unsupported".format(setid)) @classmethod def get_desc(cls, setid): """Retrieves character set information as string using an ID Retrieves character set and collation information based on the given MySQL ID. Returns a tuple. """ try: return "%s/%s" % cls.get_info(setid) except: raise @classmethod def get_default_collation(cls, charset): """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 = cls.desc[charset] return info[1], info[0], charset except: ProgrammingError("Character set ID '%s' unsupported." % ( charset)) for cid, info in enumerate(cls.desc): if info is None: continue if info[0] == charset and info[2] is True: return info[1], info[0], cid raise ProgrammingError("Character set '%s' unsupported." % (charset)) @classmethod def get_charset_info(cls, charset=None, collation=None): """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) """ if isinstance(charset, int): try: info = cls.desc[charset] return (charset, info[0], info[1]) except IndexError: ProgrammingError("Character set ID {0} unknown.".format( charset)) if charset is not None and collation is None: info = cls.get_default_collation(charset) return (info[2], info[1], info[0]) elif charset is None and collation is not None: for cid, info in enumerate(cls.desc): if info is None: continue if collation == info[1]: return (cid, info[0], info[1]) raise ProgrammingError("Collation '{0}' unknown.".format(collation)) else: for cid, info in enumerate(cls.desc): if info is None: continue if info[0] == charset and info[1] == collation: return (cid, info[0], info[1]) raise ProgrammingError("Character set '{0}' unknown.".format( charset)) @classmethod def get_supported(cls): """Retrieves a list with names of all supproted character sets Returns a tuple. """ res = [] for info in cls.desc: if info and info[0] not in res: res.append(info[0]) return tuple(res) class SQLMode(_Constants): # pylint: disable=R0921 """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. See http://dev.mysql.com/doc/refman/5.6/en/server-sql-mode.html """ _prefix = 'MODE_' REAL_AS_FLOAT = 'REAL_AS_FLOAT' PIPES_AS_CONCAT = 'PIPES_AS_CONCAT' ANSI_QUOTES = 'ANSI_QUOTES' IGNORE_SPACE = 'IGNORE_SPACE' NOT_USED = 'NOT_USED' ONLY_FULL_GROUP_BY = 'ONLY_FULL_GROUP_BY' NO_UNSIGNED_SUBTRACTION = 'NO_UNSIGNED_SUBTRACTION' NO_DIR_IN_CREATE = 'NO_DIR_IN_CREATE' POSTGRESQL = 'POSTGRESQL' ORACLE = 'ORACLE' MSSQL = 'MSSQL' DB2 = 'DB2' MAXDB = 'MAXDB' NO_KEY_OPTIONS = 'NO_KEY_OPTIONS' NO_TABLE_OPTIONS = 'NO_TABLE_OPTIONS' NO_FIELD_OPTIONS = 'NO_FIELD_OPTIONS' MYSQL323 = 'MYSQL323' MYSQL40 = 'MYSQL40' ANSI = 'ANSI' NO_AUTO_VALUE_ON_ZERO = 'NO_AUTO_VALUE_ON_ZERO' NO_BACKSLASH_ESCAPES = 'NO_BACKSLASH_ESCAPES' STRICT_TRANS_TABLES = 'STRICT_TRANS_TABLES' STRICT_ALL_TABLES = 'STRICT_ALL_TABLES' NO_ZERO_IN_DATE = 'NO_ZERO_IN_DATE' NO_ZERO_DATE = 'NO_ZERO_DATE' INVALID_DATES = 'INVALID_DATES' ERROR_FOR_DIVISION_BY_ZERO = 'ERROR_FOR_DIVISION_BY_ZERO' TRADITIONAL = 'TRADITIONAL' NO_AUTO_CREATE_USER = 'NO_AUTO_CREATE_USER' HIGH_NOT_PRECEDENCE = 'HIGH_NOT_PRECEDENCE' NO_ENGINE_SUBSTITUTION = 'NO_ENGINE_SUBSTITUTION' PAD_CHAR_TO_FULL_LENGTH = 'PAD_CHAR_TO_FULL_LENGTH' @classmethod def get_desc(cls, name): raise NotImplementedError @classmethod def get_info(cls, number): raise NotImplementedError @classmethod def get_full_info(cls): """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)) mysql-connector-python-2.0.4/lib/mysql/connector/django/0000755001577100000120000000000012506713751022615 5ustar pb2userwheelmysql-connector-python-2.0.4/lib/mysql/connector/django/base.py0000644001577100000120000006414512506707765024123 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. """Django database Backend using MySQL Connector/Python This Django database backend is heavily based on the MySQL backend coming with Django. Changes include: * Support for microseconds (MySQL 5.6.3 and later) * Using INFORMATION_SCHEMA where possible * Using new defaults for, for example SQL_AUTO_IS_NULL Requires and comes with MySQL Connector/Python v1.1 and later: http://dev.mysql.com/downloads/connector/python/ """ from __future__ import unicode_literals import sys import django from django.utils.functional import cached_property try: import mysql.connector from mysql.connector.conversion import MySQLConverter except ImportError as err: from django.core.exceptions import ImproperlyConfigured raise ImproperlyConfigured( "Error loading mysql.connector module: {0}".format(err)) try: version = mysql.connector.__version_info__[0:3] except AttributeError: from mysql.connector.version import VERSION version = VERSION[0:3] if version < (1, 1): from django.core.exceptions import ImproperlyConfigured raise ImproperlyConfigured( "MySQL Connector/Python v1.1.0 or newer " "is required; you have %s" % mysql.connector.__version__) from django.db import utils if django.VERSION < (1, 7): from django.db.backends import util else: from django.db.backends import utils as backend_utils from django.db.backends import (BaseDatabaseFeatures, BaseDatabaseOperations, BaseDatabaseWrapper) from django.db.backends.signals import connection_created from django.utils import (six, timezone, dateparse) from django.conf import settings from mysql.connector.django.client import DatabaseClient from mysql.connector.django.creation import DatabaseCreation from mysql.connector.django.introspection import DatabaseIntrospection from mysql.connector.django.validation import DatabaseValidation if django.VERSION >= (1, 7): from mysql.connector.django.schema import DatabaseSchemaEditor try: import pytz HAVE_PYTZ = True except ImportError: HAVE_PYTZ = False DatabaseError = mysql.connector.DatabaseError IntegrityError = mysql.connector.IntegrityError NotSupportedError = mysql.connector.NotSupportedError class DjangoMySQLConverter(MySQLConverter): """Custom converter for Django""" def _TIME_to_python(self, value, dsc=None): """Return MySQL TIME data type as datetime.time() Returns datetime.time() """ return dateparse.parse_time(value.decode('utf-8')) def _DATETIME_to_python(self, value, dsc=None): """Connector/Python always returns naive datetime.datetime Connector/Python always returns naive timestamps since MySQL has no time zone support. Since Django needs non-naive, we need to add the UTC time zone. Returns datetime.datetime() """ if not value: return None dt = MySQLConverter._DATETIME_to_python(self, value) if dt is None: return None if settings.USE_TZ and timezone.is_naive(dt): dt = dt.replace(tzinfo=timezone.utc) return dt def _safetext_to_mysql(self, value): return self._str_to_mysql(value) def _safebytes_to_mysql(self, value): return self._bytes_to_mysql(value) class CursorWrapper(object): """Wrapper around MySQL Connector/Python's cursor class. The cursor class is defined by the options passed to MySQL Connector/Python. If buffered option is True in those options, MySQLCursorBuffered will be used. """ def __init__(self, cursor): self.cursor = cursor def _execute_wrapper(self, method, query, args): """Wrapper around execute() and executemany()""" try: return method(query, args) except (mysql.connector.ProgrammingError) as err: six.reraise(utils.ProgrammingError, utils.ProgrammingError(err.msg), sys.exc_info()[2]) except (mysql.connector.IntegrityError) as err: six.reraise(utils.IntegrityError, utils.IntegrityError(err.msg), sys.exc_info()[2]) except mysql.connector.OperationalError as err: six.reraise(utils.DatabaseError, utils.DatabaseError(err.msg), sys.exc_info()[2]) except mysql.connector.DatabaseError as err: six.reraise(utils.DatabaseError, utils.DatabaseError(err.msg), sys.exc_info()[2]) def execute(self, query, args=None): """Executes the given operation This wrapper method around the execute()-method of the cursor is mainly needed to re-raise using different exceptions. """ return self._execute_wrapper(self.cursor.execute, query, args) def executemany(self, query, args): """Executes the given operation This wrapper method around the executemany()-method of the cursor is mainly needed to re-raise using different exceptions. """ return self._execute_wrapper(self.cursor.executemany, query, args) def __getattr__(self, attr): """Return attribute of wrapped cursor""" return getattr(self.cursor, attr) def __iter__(self): """Returns iterator over wrapped cursor""" return iter(self.cursor) def __enter__(self): return self def __exit__(self, exc_type, exc_value, exc_traceback): self.close() class DatabaseFeatures(BaseDatabaseFeatures): """Features specific to MySQL Microsecond precision is supported since MySQL 5.6.3 and turned on by default. """ empty_fetchmany_value = [] update_can_self_select = False allows_group_by_pk = True related_fields_match_type = True allow_sliced_subqueries = False has_bulk_insert = True has_select_for_update = True has_select_for_update_nowait = False supports_forward_references = False supports_long_model_names = False supports_binary_field = six.PY2 supports_microsecond_precision = False # toggled in __init__() supports_regex_backreferencing = False supports_date_lookup_using_string = False can_introspect_binary_field = False can_introspect_boolean_field = False supports_timezones = False requires_explicit_null_ordering_when_grouping = True allows_auto_pk_0 = False allows_primary_key_0 = False uses_savepoints = True atomic_transactions = False supports_column_check_constraints = False def __init__(self, connection): super(DatabaseFeatures, self).__init__(connection) self.supports_microsecond_precision = self._microseconds_precision() def _microseconds_precision(self): if self.connection.mysql_version >= (5, 6, 3): return True return False @cached_property def _mysql_storage_engine(self): """Get default storage engine of MySQL This method creates a table without ENGINE table option and inspects which engine was used. Used by Django tests. """ tblname = 'INTROSPECT_TEST' droptable = 'DROP TABLE IF EXISTS {table}'.format(table=tblname) with self.connection.cursor() as cursor: cursor.execute(droptable) cursor.execute('CREATE TABLE {table} (X INT)'.format(table=tblname)) if self.connection.mysql_version >= (5, 0, 0): cursor.execute( "SELECT ENGINE FROM INFORMATION_SCHEMA.TABLES " "WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s", (self.connection.settings_dict['NAME'], tblname)) engine = cursor.fetchone()[0] else: # Very old MySQL servers.. cursor.execute("SHOW TABLE STATUS WHERE Name='{table}'".format( table=tblname)) engine = cursor.fetchone()[1] cursor.execute(droptable) self._cached_storage_engine = engine return engine @cached_property def can_introspect_foreign_keys(self): """Confirm support for introspected foreign keys Only the InnoDB storage engine supports Foreigen Key (not taking into account MySQL Cluster here). """ return self._mysql_storage_engine == 'InnoDB' @cached_property def has_zoneinfo_database(self): """Tests if the time zone definitions are installed MySQL accepts full time zones names (eg. Africa/Nairobi) but rejects abbreviations (eg. EAT). When pytz isn't installed and the current time zone is LocalTimezone (the only sensible value in this context), the current time zone name will be an abbreviation. As a consequence, MySQL cannot perform time zone conversions reliably. """ # Django 1.6 if not HAVE_PYTZ: return False with self.connection.cursor() as cursor: cursor.execute("SELECT 1 FROM mysql.time_zone LIMIT 1") return cursor.fetchall() != [] class DatabaseOperations(BaseDatabaseOperations): compiler_module = "mysql.connector.django.compiler" # MySQL stores positive fields as UNSIGNED ints. if django.VERSION >= (1, 7): integer_field_ranges = dict(BaseDatabaseOperations.integer_field_ranges, PositiveSmallIntegerField=(0, 4294967295), PositiveIntegerField=( 0, 18446744073709551615),) def date_extract_sql(self, lookup_type, field_name): # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html if lookup_type == 'week_day': # DAYOFWEEK() returns an integer, 1-7, Sunday=1. # Note: WEEKDAY() returns 0-6, Monday=0. return "DAYOFWEEK({0})".format(field_name) else: return "EXTRACT({0} FROM {1})".format( lookup_type.upper(), field_name) def date_trunc_sql(self, lookup_type, field_name): """Returns SQL simulating DATE_TRUNC This function uses MySQL functions DATE_FORMAT and CAST to simulate DATE_TRUNC. The field_name is returned when lookup_type is not supported. """ fields = ['year', 'month', 'day', 'hour', 'minute', 'second'] format = ('%Y-', '%m', '-%d', ' %H:', '%i', ':%S') format_def = ('0000-', '01', '-01', ' 00:', '00', ':00') try: i = fields.index(lookup_type) + 1 except ValueError: # Wrong lookup type, just return the value from MySQL as-is sql = field_name else: format_str = ''.join([f for f in format[:i]] + [f for f in format_def[i:]]) sql = "CAST(DATE_FORMAT({0}, '{1}') AS DATETIME)".format( field_name, format_str) return sql def datetime_extract_sql(self, lookup_type, field_name, tzname): # Django 1.6 if settings.USE_TZ: field_name = "CONVERT_TZ({0}, 'UTC', %s)".format(field_name) params = [tzname] else: params = [] # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html if lookup_type == 'week_day': # DAYOFWEEK() returns an integer, 1-7, Sunday=1. # Note: WEEKDAY() returns 0-6, Monday=0. sql = "DAYOFWEEK({0})".format(field_name) else: sql = "EXTRACT({0} FROM {1})".format(lookup_type.upper(), field_name) return sql, params def datetime_trunc_sql(self, lookup_type, field_name, tzname): # Django 1.6 if settings.USE_TZ: field_name = "CONVERT_TZ({0}, 'UTC', %s)".format(field_name) params = [tzname] else: params = [] fields = ['year', 'month', 'day', 'hour', 'minute', 'second'] format_ = ('%Y-', '%m', '-%d', ' %H:', '%i', ':%S') format_def = ('0000-', '01', '-01', ' 00:', '00', ':00') try: i = fields.index(lookup_type) + 1 except ValueError: sql = field_name else: format_str = ''.join([f for f in format_[:i]] + [f for f in format_def[i:]]) sql = "CAST(DATE_FORMAT({0}, '{1}') AS DATETIME)".format( field_name, format_str) return sql, params def date_interval_sql(self, sql, connector, timedelta): """Returns SQL for calculating date/time intervals """ fmt = ( "({sql} {connector} INTERVAL '{days} " "0:0:{secs}:{msecs}' DAY_MICROSECOND)" ) return fmt.format( sql=sql, connector=connector, days=timedelta.days, secs=timedelta.seconds, msecs=timedelta.microseconds ) def drop_foreignkey_sql(self): return "DROP FOREIGN KEY" def force_no_ordering(self): """ "ORDER BY NULL" prevents MySQL from implicitly ordering by grouped columns. If no ordering would otherwise be applied, we don't want any implicit sorting going on. """ return ["NULL"] def fulltext_search_sql(self, field_name): return 'MATCH ({0}) AGAINST (%s IN BOOLEAN MODE)'.format(field_name) def last_executed_query(self, cursor, sql, params): return cursor.statement def no_limit_value(self): # 2**64 - 1, as recommended by the MySQL documentation return 18446744073709551615 def quote_name(self, name): if name.startswith("`") and name.endswith("`"): return name # Quoting once is enough. return "`{0}`".format(name) def random_function_sql(self): return 'RAND()' def sql_flush(self, style, tables, sequences, allow_cascade=False): if tables: sql = ['SET FOREIGN_KEY_CHECKS = 0;'] for table in tables: sql.append('{keyword} {table};'.format( keyword=style.SQL_KEYWORD('TRUNCATE'), table=style.SQL_FIELD(self.quote_name(table)))) sql.append('SET FOREIGN_KEY_CHECKS = 1;') sql.extend(self.sequence_reset_by_name_sql(style, sequences)) return sql else: return [] def sequence_reset_by_name_sql(self, style, sequences): # Truncate already resets the AUTO_INCREMENT field from # MySQL version 5.0.13 onwards. Refs #16961. res = [] if self.connection.mysql_version < (5, 0, 13): fmt = "{alter} {table} {{tablename}} {auto_inc} {field};".format( alter=style.SQL_KEYWORD('ALTER'), table=style.SQL_KEYWORD('TABLE'), auto_inc=style.SQL_KEYWORD('AUTO_INCREMENT'), field=style.SQL_FIELD('= 1') ) for sequence in sequences: tablename = style.SQL_TABLE(self.quote_name(sequence['table'])) res.append(fmt.format(tablename=tablename)) return res return res def validate_autopk_value(self, value): # MySQLism: zero in AUTO_INCREMENT field does not work. Refs #17653. if value == 0: raise ValueError('The database backend does not accept 0 as a ' 'value for AutoField.') return value def value_to_db_datetime(self, value): if value is None: return None # MySQL doesn't support tz-aware times if timezone.is_aware(value): if settings.USE_TZ: value = value.astimezone(timezone.utc).replace(tzinfo=None) else: raise ValueError( "MySQL backend does not support timezone-aware times." ) return self.connection.converter.to_mysql(value) def value_to_db_time(self, value): if value is None: return None # MySQL doesn't support tz-aware times if timezone.is_aware(value): raise ValueError("MySQL backend does not support timezone-aware " "times.") return self.connection.converter.to_mysql(value) def year_lookup_bounds(self, value): # Again, no microseconds first = '{0}-01-01 00:00:00' second = '{0}-12-31 23:59:59.999999' return [first.format(value), second.format(value)] def year_lookup_bounds_for_datetime_field(self, value): # Django 1.6 # Again, no microseconds first, second = super(DatabaseOperations, self).year_lookup_bounds_for_datetime_field(value) if self.connection.mysql_version >= (5, 6, 4): return [first.replace(microsecond=0), second] else: return [first.replace(microsecond=0), second.replace(microsecond=0)] def max_name_length(self): return 64 def bulk_insert_sql(self, fields, num_values): items_sql = "({0})".format(", ".join(["%s"] * len(fields))) return "VALUES " + ", ".join([items_sql] * num_values) def savepoint_create_sql(self, sid): return "SAVEPOINT {0}".format(sid) def savepoint_commit_sql(self, sid): return "RELEASE SAVEPOINT {0}".format(sid) def savepoint_rollback_sql(self, sid): return "ROLLBACK TO SAVEPOINT {0}".format(sid) def combine_expression(self, connector, sub_expressions): """ MySQL requires special cases for ^ operators in query expressions """ if connector == '^': return 'POW(%s)' % ','.join(sub_expressions) return super(DatabaseOperations, self).combine_expression( connector, sub_expressions) class DatabaseWrapper(BaseDatabaseWrapper): vendor = 'mysql' operators = { 'exact': '= %s', 'iexact': 'LIKE %s', 'contains': 'LIKE BINARY %s', 'icontains': 'LIKE %s', 'regex': 'REGEXP BINARY %s', 'iregex': 'REGEXP %s', 'gt': '> %s', 'gte': '>= %s', 'lt': '< %s', 'lte': '<= %s', 'startswith': 'LIKE BINARY %s', 'endswith': 'LIKE BINARY %s', 'istartswith': 'LIKE %s', 'iendswith': 'LIKE %s', } Database = mysql.connector def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.converter = DjangoMySQLConverter() self.ops = DatabaseOperations(self) self.features = DatabaseFeatures(self) self.client = DatabaseClient(self) self.creation = DatabaseCreation(self) self.introspection = DatabaseIntrospection(self) self.validation = DatabaseValidation(self) def _valid_connection(self): if self.connection: return self.connection.is_connected() return False def get_connection_params(self): # Django 1.6 kwargs = { 'charset': 'utf8', 'use_unicode': True, 'buffered': True, } settings_dict = self.settings_dict if settings_dict['USER']: kwargs['user'] = settings_dict['USER'] if settings_dict['NAME']: kwargs['database'] = settings_dict['NAME'] if settings_dict['PASSWORD']: kwargs['passwd'] = settings_dict['PASSWORD'] if settings_dict['HOST'].startswith('/'): kwargs['unix_socket'] = settings_dict['HOST'] elif settings_dict['HOST']: kwargs['host'] = settings_dict['HOST'] if settings_dict['PORT']: kwargs['port'] = int(settings_dict['PORT']) # Raise exceptions for database warnings if DEBUG is on kwargs['raise_on_warnings'] = settings.DEBUG kwargs['client_flags'] = [ # Need potentially affected rows on UPDATE mysql.connector.constants.ClientFlag.FOUND_ROWS, ] try: kwargs.update(settings_dict['OPTIONS']) except KeyError: # OPTIONS missing is OK pass return kwargs def get_new_connection(self, conn_params): # Django 1.6 cnx = mysql.connector.connect(**conn_params) cnx.set_converter_class(DjangoMySQLConverter) return cnx def init_connection_state(self): # Django 1.6 if self.mysql_version < (5, 5, 3): # See sysvar_sql_auto_is_null in MySQL Reference manual self.connection.cmd_query("SET SQL_AUTO_IS_NULL = 0") if 'AUTOCOMMIT' in self.settings_dict: try: # Django 1.6 self.set_autocommit(self.settings_dict['AUTOCOMMIT']) except AttributeError: self._set_autocommit(self.settings_dict['AUTOCOMMIT']) def create_cursor(self): # Django 1.6 cursor = self.connection.cursor() return CursorWrapper(cursor) def _connect(self): """Setup the connection with MySQL""" self.connection = self.get_new_connection(self.get_connection_params()) connection_created.send(sender=self.__class__, connection=self) self.init_connection_state() def _cursor(self): """Return a CursorWrapper object Returns a CursorWrapper """ try: # Django 1.6 return super(DatabaseWrapper, self)._cursor() except AttributeError: if not self.connection: self._connect() return self.create_cursor() def get_server_version(self): """Returns the MySQL server version of current connection Returns a tuple """ try: # Django 1.6 self.ensure_connection() except AttributeError: if not self.connection: self._connect() return self.connection.get_server_version() def disable_constraint_checking(self): """Disables foreign key checks Disables foreign key checks, primarily for use in adding rows with forward references. Always returns True, to indicate constraint checks need to be re-enabled. Returns True """ self.cursor().execute('SET @@session.foreign_key_checks = 0') return True def enable_constraint_checking(self): """Re-enable foreign key checks Re-enable foreign key checks after they have been disabled. """ # Override needs_rollback in case constraint_checks_disabled is # nested inside transaction.atomic. if django.VERSION >= (1, 6): self.needs_rollback, needs_rollback = False, self.needs_rollback try: self.cursor().execute('SET @@session.foreign_key_checks = 1') finally: if django.VERSION >= (1, 6): self.needs_rollback = needs_rollback def check_constraints(self, table_names=None): """Check rows in tables for invalid foreign key references Checks each table name in `table_names` for rows with invalid foreign key references. This method is intended to be used in conjunction with `disable_constraint_checking()` and `enable_constraint_checking()`, to determine if rows with invalid references were entered while constraint checks were off. Raises an IntegrityError on the first invalid foreign key reference encountered (if any) and provides detailed information about the invalid reference in the error message. Backends can override this method if they can more directly apply constraint checking (e.g. via "SET CONSTRAINTS ALL IMMEDIATE") """ ref_query = """ SELECT REFERRING.`{0}`, REFERRING.`{1}` FROM `{2}` as REFERRING LEFT JOIN `{3}` as REFERRED ON (REFERRING.`{4}` = REFERRED.`{5}`) WHERE REFERRING.`{6}` IS NOT NULL AND REFERRED.`{7}` IS NULL""" cursor = self.cursor() if table_names is None: table_names = self.introspection.table_names(cursor) for table_name in table_names: primary_key_column_name = \ self.introspection.get_primary_key_column(cursor, table_name) if not primary_key_column_name: continue key_columns = self.introspection.get_key_columns(cursor, table_name) for column_name, referenced_table_name, referenced_column_name \ in key_columns: cursor.execute(ref_query.format(primary_key_column_name, column_name, table_name, referenced_table_name, column_name, referenced_column_name, column_name, referenced_column_name)) for bad_row in cursor.fetchall(): msg = ("The row in table '{0}' with primary key '{1}' has " "an invalid foreign key: {2}.{3} contains a value " "'{4}' that does not have a corresponding value in " "{5}.{6}.".format(table_name, bad_row[0], table_name, column_name, bad_row[1], referenced_table_name, referenced_column_name)) raise utils.IntegrityError(msg) def _rollback(self): try: BaseDatabaseWrapper._rollback(self) except NotSupportedError: pass def _set_autocommit(self, autocommit): # Django 1.6 with self.wrap_database_errors: self.connection.autocommit = autocommit def schema_editor(self, *args, **kwargs): """Returns a new instance of this backend's SchemaEditor""" # Django 1.7 return DatabaseSchemaEditor(self, *args, **kwargs) def is_usable(self): # Django 1.6 return self.connection.is_connected() @cached_property def mysql_version(self): config = self.get_connection_params() temp_conn = mysql.connector.connect(**config) server_version = temp_conn.get_server_version() temp_conn.close() return server_version mysql-connector-python-2.0.4/lib/mysql/connector/django/client.py0000644001577100000120000000332512506707765024460 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. import os import sys from django.db.backends import BaseDatabaseClient class DatabaseClient(BaseDatabaseClient): executable_name = 'mysql' def runshell(self): settings_dict = self.connection.settings_dict args = [self.executable_name] db = settings_dict['OPTIONS'].get('database', settings_dict['NAME']) user = settings_dict['OPTIONS'].get('user', settings_dict['USER']) passwd = settings_dict['OPTIONS'].get('password', settings_dict['PASSWORD']) host = settings_dict['OPTIONS'].get('host', settings_dict['HOST']) port = settings_dict['OPTIONS'].get('port', settings_dict['PORT']) defaults_file = settings_dict['OPTIONS'].get('read_default_file') # --defaults-file should always be the first option if defaults_file: args.append("--defaults-file={0}".format(defaults_file)) # We force SQL_MODE to TRADITIONAL args.append("--init-command=SET @@session.SQL_MODE=TRADITIONAL") if user: args.append("--user={0}".format(user)) if passwd: args.append("--password={0}".format(passwd)) if host: if '/' in host: args.append("--socket={0}".format(host)) else: args.append("--host={0}".format(host)) if port: args.append("--port={0}".format(port)) if db: args.append("--database={0}".format(db)) if os.name == 'nt': sys.exit(os.system(' '.join(args))) else: os.execvp(self.executable_name, args) mysql-connector-python-2.0.4/lib/mysql/connector/django/validation.py0000644001577100000120000000456112506707765025337 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. import django from django.db.backends import BaseDatabaseValidation if django.VERSION < (1, 7): from django.db import models else: from django.core import checks from django.db import connection class DatabaseValidation(BaseDatabaseValidation): if django.VERSION < (1, 7): def validate_field(self, errors, opts, f): """ MySQL has the following field length restriction: No character (varchar) fields can have a length exceeding 255 characters if they have a unique index on them. """ varchar_fields = (models.CharField, models.CommaSeparatedIntegerField, models.SlugField) if isinstance(f, varchar_fields) and f.max_length > 255 and f.unique: msg = ('"%(name)s": %(cls)s cannot have a "max_length" greater ' 'than 255 when using "unique=True".') errors.add(opts, msg % {'name': f.name, 'cls': f.__class__.__name__}) else: def check_field(self, field, **kwargs): """ MySQL has the following field length restriction: No character (varchar) fields can have a length exceeding 255 characters if they have a unique index on them. """ # Django 1.7 errors = super(DatabaseValidation, self).check_field(field, **kwargs) # Ignore any related fields. if getattr(field, 'rel', None) is None: field_type = field.db_type(connection) if (field_type.startswith('varchar') # Look for CharFields... and field.unique # ... that are unique and (field.max_length is None or int(field.max_length) > 255)): errors.append( checks.Error( ('MySQL does not allow unique CharFields to have a ' 'max_length > 255.'), hint=None, obj=field, id='mysql.E001', ) ) return errors mysql-connector-python-2.0.4/lib/mysql/connector/django/compiler.py0000644001577100000120000000302312506707765025007 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. import django from django.db.models.sql import compiler from django.utils.six.moves import zip_longest class SQLCompiler(compiler.SQLCompiler): def resolve_columns(self, row, fields=()): values = [] index_extra_select = len(self.query.extra_select) bool_fields = ("BooleanField", "NullBooleanField") for value, field in zip_longest(row[index_extra_select:], fields): if (field and field.get_internal_type() in bool_fields and value in (0, 1)): value = bool(value) values.append(value) return row[:index_extra_select] + tuple(values) def as_subquery_condition(self, alias, columns, qn): # Django 1.6 qn2 = self.connection.ops.quote_name sql, params = self.as_sql() column_list = ', '.join( ['%s.%s' % (qn(alias), qn2(column)) for column in columns]) return '({0}) IN ({1})'.format(column_list, sql), params class SQLInsertCompiler(compiler.SQLInsertCompiler, SQLCompiler): pass class SQLDeleteCompiler(compiler.SQLDeleteCompiler, SQLCompiler): pass class SQLUpdateCompiler(compiler.SQLUpdateCompiler, SQLCompiler): pass class SQLAggregateCompiler(compiler.SQLAggregateCompiler, SQLCompiler): pass class SQLDateCompiler(compiler.SQLDateCompiler, SQLCompiler): pass if django.VERSION >= (1, 6): class SQLDateTimeCompiler(compiler.SQLDateTimeCompiler, SQLCompiler): # Django 1.6 pass mysql-connector-python-2.0.4/lib/mysql/connector/django/creation.py0000644001577100000120000001257412506707765025014 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. import django from django.db import models from django.db.backends.creation import BaseDatabaseCreation if django.VERSION < (1, 7): from django.db.backends.util import truncate_name else: from django.db.backends.utils import truncate_name class DatabaseCreation(BaseDatabaseCreation): """Maps Django Field object with MySQL data types """ def __init__(self, connection): super(DatabaseCreation, self).__init__(connection) self.data_types = { 'AutoField': 'integer AUTO_INCREMENT', 'BinaryField': 'longblob', 'BooleanField': 'bool', 'CharField': 'varchar(%(max_length)s)', 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', 'DateField': 'date', 'DateTimeField': 'datetime', # ms support set later 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'FileField': 'varchar(%(max_length)s)', 'FilePathField': 'varchar(%(max_length)s)', 'FloatField': 'double precision', 'IntegerField': 'integer', 'BigIntegerField': 'bigint', 'IPAddressField': 'char(15)', 'GenericIPAddressField': 'char(39)', 'NullBooleanField': 'bool', 'OneToOneField': 'integer', 'PositiveIntegerField': 'integer UNSIGNED', 'PositiveSmallIntegerField': 'smallint UNSIGNED', 'SlugField': 'varchar(%(max_length)s)', 'SmallIntegerField': 'smallint', 'TextField': 'longtext', 'TimeField': 'time', # ms support set later } # Support for microseconds if self.connection.mysql_version >= (5, 6, 4): self.data_types.update({ 'DateTimeField': 'datetime(6)', 'TimeField': 'time(6)', }) def sql_table_creation_suffix(self): suffix = [] if django.VERSION < (1, 7): if self.connection.settings_dict['TEST_CHARSET']: suffix.append('CHARACTER SET {0}'.format( self.connection.settings_dict['TEST_CHARSET'])) if self.connection.settings_dict['TEST_COLLATION']: suffix.append('COLLATE {0}'.format( self.connection.settings_dict['TEST_COLLATION'])) else: test_settings = self.connection.settings_dict['TEST'] if test_settings['CHARSET']: suffix.append('CHARACTER SET %s' % test_settings['CHARSET']) if test_settings['COLLATION']: suffix.append('COLLATE %s' % test_settings['COLLATION']) return ' '.join(suffix) if django.VERSION < (1, 6): def sql_for_inline_foreign_key_references(self, field, known_models, style): "All inline references are pending under MySQL" return [], True else: def sql_for_inline_foreign_key_references(self, model, field, known_models, style): "All inline references are pending under MySQL" return [], True def sql_for_inline_many_to_many_references(self, model, field, style): opts = model._meta qn = self.connection.ops.quote_name columndef = ' {column} {type} {options},' table_output = [ columndef.format( column=style.SQL_FIELD(qn(field.m2m_column_name())), type=style.SQL_COLTYPE(models.ForeignKey(model).db_type( connection=self.connection)), options=style.SQL_KEYWORD('NOT NULL') ), columndef.format( column=style.SQL_FIELD(qn(field.m2m_reverse_name())), type=style.SQL_COLTYPE(models.ForeignKey(field.rel.to).db_type( connection=self.connection)), options=style.SQL_KEYWORD('NOT NULL') ), ] deferred = [ (field.m2m_db_table(), field.m2m_column_name(), opts.db_table, opts.pk.column), (field.m2m_db_table(), field.m2m_reverse_name(), field.rel.to._meta.db_table, field.rel.to._meta.pk.column) ] return table_output, deferred def sql_destroy_indexes_for_fields(self, model, fields, style): # Django 1.6 if len(fields) == 1 and fields[0].db_tablespace: tablespace_sql = self.connection.ops.tablespace_sql( fields[0].db_tablespace) elif model._meta.db_tablespace: tablespace_sql = self.connection.ops.tablespace_sql( model._meta.db_tablespace) else: tablespace_sql = "" if tablespace_sql: tablespace_sql = " " + tablespace_sql field_names = [] qn = self.connection.ops.quote_name for f in fields: field_names.append(style.SQL_FIELD(qn(f.column))) index_name = "{0}_{1}".format(model._meta.db_table, self._digest([f.name for f in fields])) return [ style.SQL_KEYWORD("DROP INDEX") + " " + style.SQL_TABLE(qn(truncate_name(index_name, self.connection.ops.max_name_length()))) + " " + style.SQL_KEYWORD("ON") + " " + style.SQL_TABLE(qn(model._meta.db_table)) + ";", ] mysql-connector-python-2.0.4/lib/mysql/connector/django/schema.py0000644001577100000120000000452512506707765024445 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # New file added for Django 1.7 from django.db.backends.schema import BaseDatabaseSchemaEditor from django.db.models import NOT_PROVIDED class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): sql_rename_table = "RENAME TABLE %(old_table)s TO %(new_table)s" sql_alter_column_null = "MODIFY %(column)s %(type)s NULL" sql_alter_column_not_null = "MODIFY %(column)s %(type)s NOT NULL" sql_alter_column_type = "MODIFY %(column)s %(type)s" sql_rename_column = "ALTER TABLE %(table)s CHANGE %(old_column)s " \ "%(new_column)s %(type)s" sql_delete_unique = "ALTER TABLE %(table)s DROP INDEX %(name)s" sql_create_fk = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s FOREIGN " \ "KEY (%(column)s) REFERENCES %(to_table)s (%(to_column)s)" sql_delete_fk = "ALTER TABLE %(table)s DROP FOREIGN KEY %(name)s" sql_delete_index = "DROP INDEX %(name)s ON %(table)s" sql_delete_pk = "ALTER TABLE %(table)s DROP PRIMARY KEY" alter_string_set_null = 'MODIFY %(column)s %(type)s NULL;' alter_string_drop_null = 'MODIFY %(column)s %(type)s NOT NULL;' sql_create_pk = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s " \ "PRIMARY KEY (%(columns)s)" sql_delete_pk = "ALTER TABLE %(table)s DROP PRIMARY KEY" def quote_value(self, value): # Inner import to allow module to fail to load gracefully from mysql.connector.conversion import MySQLConverter return MySQLConverter.quote(MySQLConverter.escape(value)) def skip_default(self, field): """ MySQL doesn't accept default values for longtext and longblob and implicitly treats these columns as nullable. """ return field.db_type(self.connection) in ('longtext', 'longblob') def add_field(self, model, field): super(DatabaseSchemaEditor, self).add_field(model, field) # Simulate the effect of a one-off default. if self.skip_default(field) and field.default not in (None, NOT_PROVIDED): effective_default = self.effective_default(field) self.execute('UPDATE %(table)s SET %(column)s = %%s' % { 'table': self.quote_name(model._meta.db_table), 'column': self.quote_name(field.column), }, [effective_default]) mysql-connector-python-2.0.4/lib/mysql/connector/django/__init__.py0000644001577100000120000000000012506707765024724 0ustar pb2userwheelmysql-connector-python-2.0.4/lib/mysql/connector/django/introspection.py0000644001577100000120000002222512506707765026102 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. import re import django from django.db.backends import BaseDatabaseIntrospection if django.VERSION >= (1, 6): from django.db.backends import FieldInfo from django.utils.encoding import force_text if django.VERSION >= (1, 7): from django.utils.datastructures import OrderedSet from mysql.connector.constants import FieldType foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) " r"REFERENCES `([^`]*)` \(`([^`]*)`\)") class DatabaseIntrospection(BaseDatabaseIntrospection): data_types_reverse = { FieldType.BLOB: 'TextField', FieldType.DECIMAL: 'DecimalField', FieldType.NEWDECIMAL: 'DecimalField', FieldType.DATE: 'DateField', FieldType.DATETIME: 'DateTimeField', FieldType.DOUBLE: 'FloatField', FieldType.FLOAT: 'FloatField', FieldType.INT24: 'IntegerField', FieldType.LONG: 'IntegerField', FieldType.LONGLONG: 'BigIntegerField', FieldType.SHORT: 'IntegerField', FieldType.STRING: 'CharField', FieldType.TIME: 'TimeField', FieldType.TIMESTAMP: 'DateTimeField', FieldType.TINY: 'IntegerField', FieldType.TINY_BLOB: 'TextField', FieldType.MEDIUM_BLOB: 'TextField', FieldType.LONG_BLOB: 'TextField', FieldType.VAR_STRING: 'CharField', } def get_table_list(self, cursor): "Returns a list of table names in the current database." cursor.execute("SHOW TABLES") return [row[0] for row in cursor.fetchall()] def get_table_description(self, cursor, table_name): """ Returns a description of the table, with the DB-API cursor.description interface." """ # varchar length returned by cursor.description is an internal length, # not visible length (#5725), use information_schema database to fix # this cursor.execute( "SELECT column_name, character_maximum_length " "FROM INFORMATION_SCHEMA.COLUMNS " "WHERE table_name = %s AND table_schema = DATABASE() " "AND character_maximum_length IS NOT NULL", [table_name]) length_map = dict(cursor.fetchall()) # Also getting precision and scale from information_schema (see #5014) cursor.execute( "SELECT column_name, numeric_precision, numeric_scale FROM " "INFORMATION_SCHEMA.COLUMNS WHERE table_name = %s AND " "table_schema = DATABASE() AND data_type='decimal'", [table_name]) numeric_map = dict((line[0], tuple([int(n) for n in line[1:]])) for line in cursor.fetchall()) cursor.execute("SELECT * FROM {0} LIMIT 1".format( self.connection.ops.quote_name(table_name))) if django.VERSION >= (1, 6): return [FieldInfo(*((force_text(line[0]),) + line[1:3] + (length_map.get(line[0], line[3]),) + numeric_map.get(line[0], line[4:6]) + (line[6],))) for line in cursor.description] else: return [line[:3] + (length_map.get(line[0], line[3]),) + line[4:] for line in cursor.description] def _name_to_index(self, cursor, table_name): """ Returns a dictionary of {field_name: field_index} for the given table. Indexes are 0-based. """ return dict((d[0], i) for i, d in enumerate( self.get_table_description(cursor, table_name))) def get_relations(self, cursor, table_name): """ Returns a dictionary of {field_index: (field_index_other_table, other_table)} representing all relationships to the given table. Indexes are 0-based. """ my_field_dict = self._name_to_index(cursor, table_name) constraints = self.get_key_columns(cursor, table_name) relations = {} for my_fieldname, other_table, other_field in constraints: other_field_index = self._name_to_index(cursor, other_table)[other_field] my_field_index = my_field_dict[my_fieldname] relations[my_field_index] = (other_field_index, other_table) return relations def get_key_columns(self, cursor, table_name): """ Returns a list of (column_name, referenced_table_name, referenced_column_name) for all key columns in given table. """ key_columns = [] cursor.execute( "SELECT column_name, referenced_table_name, referenced_column_name " "FROM information_schema.key_column_usage " "WHERE table_name = %s " "AND table_schema = DATABASE() " "AND referenced_table_name IS NOT NULL " "AND referenced_column_name IS NOT NULL", [table_name]) key_columns.extend(cursor.fetchall()) return key_columns def get_indexes(self, cursor, table_name): cursor.execute("SHOW INDEX FROM {0}" "".format(self.connection.ops.quote_name(table_name))) # Do a two-pass search for indexes: on first pass check which indexes # are multicolumn, on second pass check which single-column indexes # are present. rows = list(cursor.fetchall()) multicol_indexes = set() for row in rows: if row[3] > 1: multicol_indexes.add(row[2]) indexes = {} for row in rows: if row[2] in multicol_indexes: continue if row[4] not in indexes: indexes[row[4]] = {'primary_key': False, 'unique': False} # It's possible to have the unique and PK constraints in # separate indexes. if row[2] == 'PRIMARY': indexes[row[4]]['primary_key'] = True if not row[1]: indexes[row[4]]['unique'] = True return indexes def get_primary_key_column(self, cursor, table_name): """ Returns the name of the primary key column for the given table """ # Django 1.6 for column in self.get_indexes(cursor, table_name).items(): if column[1]['primary_key']: return column[0] return None def get_constraints(self, cursor, table_name): """ Retrieves any constraints or keys (unique, pk, fk, check, index) across one or more columns. """ # Django 1.7 constraints = {} # Get the actual constraint names and columns name_query = ( "SELECT kc.`constraint_name`, kc.`column_name`, " "kc.`referenced_table_name`, kc.`referenced_column_name` " "FROM information_schema.key_column_usage AS kc " "WHERE " "kc.table_schema = %s AND " "kc.table_name = %s" ) cursor.execute(name_query, [self.connection.settings_dict['NAME'], table_name]) for constraint, column, ref_table, ref_column in cursor.fetchall(): if constraint not in constraints: constraints[constraint] = { 'columns': OrderedSet(), 'primary_key': False, 'unique': False, 'index': False, 'check': False, 'foreign_key': (ref_table, ref_column) if ref_column else None, } constraints[constraint]['columns'].add(column) # Now get the constraint types type_query = """ SELECT c.constraint_name, c.constraint_type FROM information_schema.table_constraints AS c WHERE c.table_schema = %s AND c.table_name = %s """ cursor.execute(type_query, [self.connection.settings_dict['NAME'], table_name]) for constraint, kind in cursor.fetchall(): if kind.lower() == "primary key": constraints[constraint]['primary_key'] = True constraints[constraint]['unique'] = True elif kind.lower() == "unique": constraints[constraint]['unique'] = True # Now add in the indexes cursor.execute("SHOW INDEX FROM %s" % self.connection.ops.quote_name( table_name)) for table, non_unique, index, colseq, column in [x[:5] for x in cursor.fetchall()]: if index not in constraints: constraints[index] = { 'columns': OrderedSet(), 'primary_key': False, 'unique': False, 'index': True, 'check': False, 'foreign_key': None, } constraints[index]['index'] = True constraints[index]['columns'].add(column) # Convert the sorted sets to lists for constraint in constraints.values(): constraint['columns'] = list(constraint['columns']) return constraints mysql-connector-python-2.0.4/lib/mysql/connector/__init__.py0000644001577100000120000001514212506707765023477 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """ MySQL Connector/Python - MySQL drive written in Python """ from . import version from .connection import MySQLConnection from .errors import ( # pylint: disable=W0622 Error, Warning, InterfaceError, DatabaseError, NotSupportedError, DataError, IntegrityError, ProgrammingError, OperationalError, InternalError, custom_error_exception, PoolError) from .constants import FieldFlag, FieldType, CharacterSet, \ RefreshOption, ClientFlag from .dbapi import ( Date, Time, Timestamp, Binary, DateFromTicks, DateFromTicks, TimestampFromTicks, TimeFromTicks, STRING, BINARY, NUMBER, DATETIME, ROWID, apilevel, threadsafety, paramstyle) from .optionfiles import read_option_files _CONNECTION_POOLS = {} def _get_pooled_connection(**kwargs): """Return a pooled MySQL connection""" # If no pool name specified, generate one from .pooling import ( MySQLConnectionPool, generate_pool_name, CONNECTION_POOL_LOCK) try: pool_name = kwargs['pool_name'] except KeyError: pool_name = generate_pool_name(**kwargs) # Setup the pool, ensuring only 1 thread can update at a time with CONNECTION_POOL_LOCK: if pool_name not in _CONNECTION_POOLS: _CONNECTION_POOLS[pool_name] = MySQLConnectionPool(**kwargs) elif isinstance(_CONNECTION_POOLS[pool_name], MySQLConnectionPool): # pool_size must be the same check_size = _CONNECTION_POOLS[pool_name].pool_size if ('pool_size' in kwargs and kwargs['pool_size'] != check_size): raise PoolError("Size can not be changed " "for active pools.") # Return pooled connection try: return _CONNECTION_POOLS[pool_name].get_connection() except AttributeError: raise InterfaceError( "Failed getting connection from pool '{0}'".format(pool_name)) def _get_failover_connection(**kwargs): """Return a MySQL connection and try to failover if needed An InterfaceError is raise when no MySQL is available. ValueError is raised when the failover server configuration contains an illegal connection argument. Supported arguments are user, password, host, port, unix_socket and database. ValueError is also raised when the failover argument was not provided. Returns MySQLConnection instance. """ config = kwargs.copy() try: failover = config['failover'] except KeyError: raise ValueError('failover argument not provided') del config['failover'] support_cnx_args = set( ['user', 'password', 'host', 'port', 'unix_socket', 'database', 'pool_name', 'pool_size']) # First check if we can add all use the configuration for server in failover: diff = set(server.keys()) - support_cnx_args if diff: raise ValueError( "Unsupported connection argument {0} in failover: {1}".format( 's' if len(diff) > 1 else '', ', '.join(diff))) for server in failover: new_config = config.copy() new_config.update(server) try: return connect(**new_config) except Error: # If we failed to connect, we try the next server pass raise InterfaceError("Could not failover: no MySQL server available") def connect(*args, **kwargs): """Create or get a MySQL connection object In its simpliest form, Connect() will open a connection to a MySQL server and return a MySQLConnection object. When any connection pooling arguments are given, for example pool_name or pool_size, a pool is created or a previously one is used to return a PooledMySQLConnection. Returns MySQLConnection or PooledMySQLConnection. """ # Option files if 'option_files' in kwargs: new_config = read_option_files(**kwargs) return connect(**new_config) if all(['fabric' in kwargs, 'failover' in kwargs]): raise InterfaceError("fabric and failover arguments can not be used") if 'fabric' in kwargs: if 'pool_name' in kwargs: raise AttributeError("'pool_name' argument is not supported with " " MySQL Fabric. Use 'pool_size' instead.") from .fabric import connect as fabric_connect return fabric_connect(*args, **kwargs) # Failover if 'failover' in kwargs: return _get_failover_connection(**kwargs) # Pooled connections try: from .pooling import CNX_POOL_ARGS if any([key in kwargs for key in CNX_POOL_ARGS]): return _get_pooled_connection(**kwargs) except NameError: # No pooling pass # Regular connection return MySQLConnection(*args, **kwargs) Connect = connect # pylint: disable=C0103 __version_info__ = version.VERSION __version__ = version.VERSION_TEXT __all__ = [ 'MySQLConnection', 'Connect', 'custom_error_exception', # Some useful constants 'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption', # Error handling 'Error', 'Warning', 'InterfaceError', 'DatabaseError', 'NotSupportedError', 'DataError', 'IntegrityError', 'ProgrammingError', 'OperationalError', 'InternalError', # DBAPI PEP 249 required exports 'connect', 'apilevel', 'threadsafety', 'paramstyle', 'Date', 'Time', 'Timestamp', 'Binary', 'DateFromTicks', 'DateFromTicks', 'TimestampFromTicks', 'TimeFromTicks', 'STRING', 'BINARY', 'NUMBER', 'DATETIME', 'ROWID', ] mysql-connector-python-2.0.4/lib/mysql/connector/conversion.py0000644001577100000120000004542312506707765024132 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Converting MySQL and Python types """ import datetime import time from decimal import Decimal from .constants import FieldType, FieldFlag, CharacterSet from .catch23 import PY2, NUMERIC_TYPES, struct_unpack from .custom_types import HexLiteral class MySQLConverterBase(object): """Base class for conversion classes All class dealing with converting to and from MySQL data types must be a subclass of this class. """ def __init__(self, charset='utf8', use_unicode=True): self.python_types = None self.mysql_types = None self.charset = None self.charset_id = 0 self.use_unicode = None self.set_charset(charset) self.set_unicode(use_unicode) def set_charset(self, charset): """Set character set""" if charset == 'utf8mb4': charset = 'utf8' if charset is not None: self.charset = charset else: # default to utf8 self.charset = 'utf8' self.charset_id = CharacterSet.get_charset_info(self.charset)[0] def set_unicode(self, value=True): """Set whether to use Unicode""" self.use_unicode = value def to_mysql(self, value): """Convert Python data type to MySQL""" type_name = value.__class__.__name__.lower() try: return getattr(self, "_{0}_to_mysql".format(type_name))(value) except AttributeError: return value def to_python(self, vtype, value): """Convert MySQL data type to Python""" return value def escape(self, buf): """Escape buffer for sending to MySQL""" return buf def quote(self, buf): """Quote buffer for sending to MySQL""" return str(buf) class MySQLConverter(MySQLConverterBase): """Default conversion class for MySQL Connector/Python. o escape method: for escaping values send to MySQL o quoting method: for quoting values send to MySQL in statements o conversion mapping: maps Python and MySQL data types to function for converting them. Whenever one needs to convert values differently, a converter_class argument can be given while instantiating a new connection like cnx.connect(converter_class=CustomMySQLConverterClass). """ def __init__(self, charset=None, use_unicode=True): MySQLConverterBase.__init__(self, charset, use_unicode) self._cache_field_types = {} def escape(self, value): """ Escapes special characters as they are expected to by when MySQL receives them. As found in MySQL source mysys/charset.c Returns the value if not a string, or the escaped string. """ if value is None: return value elif isinstance(value, NUMERIC_TYPES): return value if isinstance(value, (bytes, bytearray)): value = value.replace(b'\\', b'\\\\') value = value.replace(b'\n', b'\\n') value = value.replace(b'\r', b'\\r') value = value.replace(b'\047', b'\134\047') # single quotes value = value.replace(b'\042', b'\134\042') # double quotes value = value.replace(b'\032', b'\134\032') # for Win32 else: value = value.replace('\\', '\\\\') value = value.replace('\n', '\\n') value = value.replace('\r', '\\r') value = value.replace('\047', '\134\047') # single quotes value = value.replace('\042', '\134\042') # double quotes value = value.replace('\032', '\134\032') # for Win32 return value def quote(self, buf): """ Quote the parameters for commands. General rules: o numbers are returns as bytes using ascii codec o None is returned as bytearray(b'NULL') o Everything else is single quoted '' Returns a bytearray object. """ if isinstance(buf, NUMERIC_TYPES): if PY2: if isinstance(buf, float): return repr(buf) else: return str(buf) else: return str(buf).encode('ascii') elif isinstance(buf, type(None)): return bytearray(b"NULL") else: return bytearray(b"'" + buf + b"'") def to_mysql(self, value): """Convert Python data type to MySQL""" type_name = value.__class__.__name__.lower() try: return getattr(self, "_{0}_to_mysql".format(type_name))(value) except AttributeError: raise TypeError("Python '{0}' cannot be converted to a " "MySQL type".format(type_name)) def _int_to_mysql(self, value): """Convert value to int""" return int(value) def _long_to_mysql(self, value): """Convert value to int""" return int(value) def _float_to_mysql(self, value): """Convert value to float""" return float(value) def _str_to_mysql(self, value): """Convert value to string""" if PY2: return str(value) return self._unicode_to_mysql(value) def _unicode_to_mysql(self, value): """Convert unicode""" charset = self.charset charset_id = self.charset_id if charset == 'binary': charset = 'utf8' charset_id = CharacterSet.get_charset_info(charset)[0] encoded = value.encode(charset) if charset_id in CharacterSet.slash_charsets: if b'\x5c' in encoded: return HexLiteral(value, charset) return encoded def _bytes_to_mysql(self, value): """Convert value to bytes""" return value def _bytearray_to_mysql(self, value): """Convert value to bytes""" return str(value) def _bool_to_mysql(self, value): """Convert value to boolean""" if value: return 1 else: return 0 def _nonetype_to_mysql(self, value): """ This would return what None would be in MySQL, but instead we leave it None and return it right away. The actual conversion from None to NULL happens in the quoting functionality. Return None. """ return None def _datetime_to_mysql(self, value): """ Converts a datetime instance to a string suitable for MySQL. The returned string has format: %Y-%m-%d %H:%M:%S[.%f] If the instance isn't a datetime.datetime type, it return None. Returns a bytes. """ if value.microsecond: fmt = '{0:d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:06d}' return fmt.format( value.year, value.month, value.day, value.hour, value.minute, value.second, value.microsecond).encode('ascii') fmt = '{0:d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}' return fmt.format( value.year, value.month, value.day, value.hour, value.minute, value.second).encode('ascii') def _date_to_mysql(self, value): """ Converts a date instance to a string suitable for MySQL. The returned string has format: %Y-%m-%d If the instance isn't a datetime.date type, it return None. Returns a bytes. """ return '{0:d}-{1:02d}-{2:02d}'.format(value.year, value.month, value.day).encode('ascii') def _time_to_mysql(self, value): """ Converts a time instance to a string suitable for MySQL. The returned string has format: %H:%M:%S[.%f] If the instance isn't a datetime.time type, it return None. Returns a bytes. """ if value.microsecond: return value.strftime('%H:%M:%S.%f').encode('ascii') return value.strftime('%H:%M:%S').encode('ascii') def _struct_time_to_mysql(self, value): """ Converts a time.struct_time sequence to a string suitable for MySQL. The returned string has format: %Y-%m-%d %H:%M:%S Returns a bytes or None when not valid. """ return time.strftime('%Y-%m-%d %H:%M:%S', value).encode('ascii') def _timedelta_to_mysql(self, value): """ Converts a timedelta instance to a string suitable for MySQL. The returned string has format: %H:%M:%S Returns a bytes. """ seconds = abs(value.days * 86400 + value.seconds) if value.microseconds: fmt = '{0:02d}:{1:02d}:{2:02d}.{3:06d}' if value.days < 0: mcs = 1000000 - value.microseconds seconds -= 1 else: mcs = value.microseconds else: fmt = '{0:02d}:{1:02d}:{2:02d}' if value.days < 0: fmt = '-' + fmt (hours, remainder) = divmod(seconds, 3600) (mins, secs) = divmod(remainder, 60) if value.microseconds: result = fmt.format(hours, mins, secs, mcs) else: result = fmt.format(hours, mins, secs) if PY2: return result else: return result.encode('ascii') def _decimal_to_mysql(self, value): """ Converts a decimal.Decimal instance to a string suitable for MySQL. Returns a bytes or None when not valid. """ if isinstance(value, Decimal): return str(value).encode('ascii') return None def to_python(self, flddsc, value): """ Converts a given value coming from MySQL to a certain type in Python. The flddsc contains additional information for the field in the table. It's an element from MySQLCursor.description. Returns a mixed value. """ if value == 0 and flddsc[1] != FieldType.BIT: # \x00 # Don't go further when we hit a NULL value return None if value is None: return None if not self._cache_field_types: self._cache_field_types = {} for name, info in FieldType.desc.items(): try: self._cache_field_types[info[0]] = getattr( self, '_{0}_to_python'.format(name)) except AttributeError: # We ignore field types which has no method pass try: return self._cache_field_types[flddsc[1]](value, flddsc) except KeyError: # If one type is not defined, we just return the value as str try: return value.decode('utf-8') except UnicodeDecodeError: return value except ValueError as err: raise ValueError("%s (field %s)" % (err, flddsc[0])) except TypeError as err: raise TypeError("%s (field %s)" % (err, flddsc[0])) except: raise def row_to_python(self, row, fields): """Convert a MySQL text result row to Python types The row argument is a sequence containing text result returned by a MySQL server. Each value of the row is converted to the using the field type information in the fields argument. Returns a tuple. """ i = 0 result = [None]*len(fields) if not self._cache_field_types: self._cache_field_types = {} for name, info in FieldType.desc.items(): try: self._cache_field_types[info[0]] = getattr( self, '_{0}_to_python'.format(name)) except AttributeError: # We ignore field types which has no method pass for field in fields: field_type = field[1] if (row[i] == 0 and field_type != FieldType.BIT) or row[i] is None: # Don't convert NULL value i += 1 continue try: result[i] = self._cache_field_types[field_type](row[i], field) except KeyError: # If one type is not defined, we just return the value as str try: result[i] = row[i].decode('utf-8') except UnicodeDecodeError: result[i] = row[i] except (ValueError, TypeError) as err: err.message = "{0} (field {1})".format(str(err), field[0]) raise i += 1 return tuple(result) def _FLOAT_to_python(self, value, desc=None): # pylint: disable=C0103 """ Returns value as float type. """ return float(value) _DOUBLE_to_python = _FLOAT_to_python def _INT_to_python(self, value, desc=None): # pylint: disable=C0103 """ Returns value as int type. """ return int(value) _TINY_to_python = _INT_to_python _SHORT_to_python = _INT_to_python _INT24_to_python = _INT_to_python _LONG_to_python = _INT_to_python _LONGLONG_to_python = _INT_to_python def _DECIMAL_to_python(self, value, desc=None): # pylint: disable=C0103 """ Returns value as a decimal.Decimal. """ val = value.decode(self.charset) return Decimal(val) _NEWDECIMAL_to_python = _DECIMAL_to_python def _str(self, value, desc=None): """ Returns value as str type. """ return str(value) def _BIT_to_python(self, value, dsc=None): # pylint: disable=C0103 """Returns BIT columntype as integer""" int_val = value if len(int_val) < 8: int_val = b'\x00' * (8 - len(int_val)) + int_val return struct_unpack('>Q', int_val)[0] def _DATE_to_python(self, value, dsc=None): # pylint: disable=C0103 """ Returns DATE column type as datetime.date type. """ try: parts = value.split(b'-') return datetime.date(int(parts[0]), int(parts[1]), int(parts[2])) except ValueError: return None _NEWDATE_to_python = _DATE_to_python def _TIME_to_python(self, value, dsc=None): # pylint: disable=C0103 """ Returns TIME column type as datetime.time type. """ time_val = None try: (hms, mcs) = value.split(b'.') mcs = int(mcs.ljust(6, b'0')) except ValueError: hms = value mcs = 0 try: (hours, mins, secs) = [int(d) for d in hms.split(b':')] if value[0] == 45 or value[0] == '-': # if PY3 or PY2 mins, secs, mcs = -mins, -secs, -mcs time_val = datetime.timedelta(hours=hours, minutes=mins, seconds=secs, microseconds=mcs) except ValueError: raise ValueError( "Could not convert {0} to python datetime.timedelta".format( value)) else: return time_val def _DATETIME_to_python(self, value, dsc=None): # pylint: disable=C0103 """ Returns DATETIME column type as datetime.datetime type. """ datetime_val = None try: (date_, time_) = value.split(b' ') if len(time_) > 8: (hms, mcs) = time_.split(b'.') mcs = int(mcs.ljust(6, b'0')) else: hms = time_ mcs = 0 dtval = [int(i) for i in date_.split(b'-')] + \ [int(i) for i in hms.split(b':')] + [mcs, ] datetime_val = datetime.datetime(*dtval) except ValueError: datetime_val = None return datetime_val _TIMESTAMP_to_python = _DATETIME_to_python def _YEAR_to_python(self, value, desc=None): # pylint: disable=C0103 """Returns YEAR column type as integer""" try: year = int(value) except ValueError: raise ValueError("Failed converting YEAR to int (%s)" % value) return year def _SET_to_python(self, value, dsc=None): # pylint: disable=C0103 """Returns SET column typs as set Actually, MySQL protocol sees a SET as a string type field. So this code isn't called directly, but used by STRING_to_python() method. Returns SET column type as a set. """ set_type = None val = value.decode(self.charset) if not val: return set() try: set_type = set(val.split(',')) except ValueError: raise ValueError("Could not convert set %s to a sequence." % value) return set_type def _STRING_to_python(self, value, dsc=None): # pylint: disable=C0103 """ Note that a SET is a string too, but using the FieldFlag we can see whether we have to split it. Returns string typed columns as string type. """ if dsc is not None: # Check if we deal with a SET if dsc[7] & FieldFlag.SET: return self._SET_to_python(value, dsc) if dsc[7] & FieldFlag.BINARY: return value if self.charset == 'binary': return value if isinstance(value, (bytes, bytearray)) and self.use_unicode: return value.decode(self.charset) return value _VAR_STRING_to_python = _STRING_to_python def _BLOB_to_python(self, value, dsc=None): # pylint: disable=C0103 """Convert BLOB data type to Python""" if dsc is not None: if dsc[7] & FieldFlag.BINARY: if PY2: return value else: return bytes(value) return self._STRING_to_python(value, dsc) _LONG_BLOB_to_python = _BLOB_to_python _MEDIUM_BLOB_to_python = _BLOB_to_python _TINY_BLOB_to_python = _BLOB_to_python mysql-connector-python-2.0.4/lib/mysql/connector/cursor.py0000644001577100000120000012245112506707765023257 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Cursor classes """ from collections import namedtuple import re import weakref from . import errors SQL_COMMENT = r"\/\*.*?\*\/" RE_SQL_COMMENT = re.compile( r'''({0})|(["'`][^"'`]*?({0})[^"'`]*?["'`])'''.format(SQL_COMMENT), re.I | re.M | re.S) RE_SQL_ON_DUPLICATE = re.compile( r'''\s*ON\s+DUPLICATE\s+KEY(?:[^"'`]*["'`][^"'`]*["'`])*[^"'`]*$''', re.I | re.M | re.S) RE_SQL_INSERT_STMT = re.compile( r"({0}|\s)*INSERT({0}|\s)*INTO.+VALUES.*".format(SQL_COMMENT), re.I | re.M | re.S) RE_SQL_INSERT_VALUES = re.compile(r'.*VALUES\s*(\(.*\)).*', re.I | re.M | re.S) RE_PY_PARAM = re.compile(b'(%s)') RE_SQL_SPLIT_STMTS = re.compile( b''';(?=(?:[^"'`]*["'`][^"'`]*["'`])*[^"'`]*$)''') RE_SQL_FIND_PARAM = re.compile( b'''%s(?=(?:[^"'`]*["'`][^"'`]*["'`])*[^"'`]*$)''') _ERR_NO_RESULT_TO_FETCH = "No result set to fetch from." class _ParamSubstitutor(object): """ Substitutes parameters into SQL statement. """ def __init__(self, params): self.params = params self.index = 0 def __call__(self, matchobj): index = self.index self.index += 1 try: return bytes(self.params[index]) except IndexError: raise errors.ProgrammingError( "Not enough parameters for the SQL statement") @property def remaining(self): """Returns number of parameters remaining to be substituted""" return len(self.params) - self.index class CursorBase(object): """ Base for defining MySQLCursor. This class is a skeleton and defines methods and members as required for the Python Database API Specification v2.0. It's better to inherite from MySQLCursor. """ def __init__(self): self._description = None self._rowcount = -1 self._last_insert_id = None self.arraysize = 1 def callproc(self, procname, args=()): """Calls a stored procedue with the given arguments The arguments will be set during this session, meaning they will be called like ___arg where is an enumeration (+1) of the arguments. Coding Example: 1) Definining the Stored Routine in MySQL: CREATE PROCEDURE multiply(IN pFac1 INT, IN pFac2 INT, OUT pProd INT) BEGIN SET pProd := pFac1 * pFac2; END 2) Executing in Python: args = (5,5,0) # 0 is to hold pprod cursor.callproc('multiply', args) print(cursor.fetchone()) Does not return a value, but a result set will be available when the CALL-statement execute successfully. Raises exceptions when something is wrong. """ pass def close(self): """Close the cursor.""" pass def execute(self, operation, params=(), multi=False): """Executes the given operation Executes the given operation substituting any markers with the given parameters. For example, getting all rows where id is 5: cursor.execute("SELECT * FROM t1 WHERE id = %s", (5,)) The multi argument should be set to True when executing multiple statements in one operation. If not set and multiple results are found, an InterfaceError will be raised. If warnings where generated, and connection.get_warnings is True, then self._warnings will be a list containing these warnings. Returns an iterator when multi is True, otherwise None. """ pass def executemany(self, operation, seqparams): """Execute the given operation multiple times The executemany() method will execute the operation iterating over the list of parameters in seq_params. Example: Inserting 3 new employees and their phone number data = [ ('Jane','555-001'), ('Joe', '555-001'), ('John', '555-003') ] stmt = "INSERT INTO employees (name, phone) VALUES ('%s','%s')" cursor.executemany(stmt, data) INSERT statements are optimized by batching the data, that is using the MySQL multiple rows syntax. Results are discarded. If they are needed, consider looping over data using the execute() method. """ pass def fetchone(self): """Returns next row of a query result set Returns a tuple or None. """ pass def fetchmany(self, size=1): """Returns the next set of rows of a query result, returning a list of tuples. When no more rows are available, it returns an empty list. The number of rows returned can be specified using the size argument, which defaults to one """ pass def fetchall(self): """Returns all rows of a query result set Returns a list of tuples. """ pass def nextset(self): """Not Implemented.""" pass def setinputsizes(self, sizes): """Not Implemented.""" pass def setoutputsize(self, size, column=None): """Not Implemented.""" pass def reset(self): """Reset the cursor to default""" pass @property def description(self): """Returns description of columns in a result This property returns a list of tuples describing the columns in in a result set. A tuple is described as follows:: (column_name, type, None, None, None, None, null_ok, column_flags) # Addition to PEP-249 specs Returns a list of tuples. """ return self._description @property def rowcount(self): """Returns the number of rows produced or affected This property returns the number of rows produced by queries such as a SELECT, or affected rows when executing DML statements like INSERT or UPDATE. Note that for non-buffered cursors it is impossible to know the number of rows produced before having fetched them all. For those, the number of rows will be -1 right after execution, and incremented when fetching rows. Returns an integer. """ return self._rowcount @property def lastrowid(self): """Returns the value generated for an AUTO_INCREMENT column Returns the value generated for an AUTO_INCREMENT column by the previous INSERT or UPDATE statement or None when there is no such value available. Returns a long value or None. """ return self._last_insert_id class MySQLCursor(CursorBase): """Default cursor for interacting with MySQL This cursor will execute statements and handle the result. It will not automatically fetch all rows. MySQLCursor should be inherited whenever other functionallity is required. An example would to change the fetch* member functions to return dictionaries instead of lists of values. Implements the Python Database API Specification v2.0 (PEP-249) """ def __init__(self, connection=None): CursorBase.__init__(self) self._connection = None self._stored_results = [] self._nextrow = (None, None) self._warnings = None self._warning_count = 0 self._executed = None self._executed_list = [] self._binary = False if connection is not None: self._set_connection(connection) def __iter__(self): """ Iteration over the result set which calls self.fetchone() and returns the next row. """ return iter(self.fetchone, None) def _set_connection(self, connection): """Set the connection""" try: self._connection = weakref.proxy(connection) self._connection._protocol # pylint: disable=W0212,W0104 except (AttributeError, TypeError): raise errors.InterfaceError(errno=2048) def _reset_result(self): """Reset the cursor to default""" self._rowcount = -1 self._nextrow = (None, None) self._stored_results = [] self._warnings = None self._warning_count = 0 self._description = None self._executed = None self._executed_list = [] self.reset() def _have_unread_result(self): """Check whether there is an unread result""" try: return self._connection.unread_result except AttributeError: return False def next(self): """Used for iterating over the result set.""" return self.__next__() def __next__(self): """ Used for iterating over the result set. Calles self.fetchone() to get the next row. """ try: row = self.fetchone() except errors.InterfaceError: raise StopIteration if not row: raise StopIteration return row def close(self): """Close the cursor Returns True when successful, otherwise False. """ if self._connection is None: return False if self._have_unread_result(): raise errors.InternalError("Unread result found.") self._reset_result() self._connection = None return True def _process_params_dict(self, params): """Process query parameters given as dictionary""" try: to_mysql = self._connection.converter.to_mysql escape = self._connection.converter.escape quote = self._connection.converter.quote res = {} for key, value in list(params.items()): conv = value conv = to_mysql(conv) conv = escape(conv) conv = quote(conv) res["%({0})s".format(key).encode()] = conv except Exception as err: raise errors.ProgrammingError( "Failed processing pyformat-parameters; %s" % err) else: return res def _process_params(self, params): """Process query parameters.""" try: res = params to_mysql = self._connection.converter.to_mysql escape = self._connection.converter.escape quote = self._connection.converter.quote res = [to_mysql(i) for i in res] res = [escape(i) for i in res] res = [quote(i) for i in res] except Exception as err: raise errors.ProgrammingError( "Failed processing format-parameters; %s" % err) else: return tuple(res) def _handle_noresultset(self, res): """Handles result of execute() when there is no result set """ try: self._rowcount = res['affected_rows'] self._last_insert_id = res['insert_id'] self._warning_count = res['warning_count'] except (KeyError, TypeError) as err: raise errors.ProgrammingError( "Failed handling non-resultset; {0}".format(err)) if self._connection.get_warnings is True and self._warning_count: self._warnings = self._fetch_warnings() def _handle_resultset(self): """Handles result set This method handles the result set and is called after reading and storing column information in _handle_result(). For non-buffering cursors, this method is usually doing nothing. """ pass def _handle_result(self, result): """ Handle the result after a command was send. The result can be either an OK-packet or a dictionary containing column/eof information. Raises InterfaceError when result is not a dict() or result is invalid. """ if not isinstance(result, dict): raise errors.InterfaceError('Result was not a dict()') if 'columns' in result: # Weak test, must be column/eof information self._description = result['columns'] self._connection.unread_result = True self._handle_resultset() elif 'affected_rows' in result: # Weak test, must be an OK-packet self._connection.unread_result = False self._handle_noresultset(result) else: raise errors.InterfaceError('Invalid result') def _execute_iter(self, query_iter): """Generator returns MySQLCursor objects for multiple statements This method is only used when multiple statements are executed by the execute() method. It uses zip() to make an iterator from the given query_iter (result of MySQLConnection.cmd_query_iter()) and the list of statements that were executed. """ executed_list = RE_SQL_SPLIT_STMTS.split(self._executed) i = 0 while True: result = next(query_iter) self._reset_result() self._handle_result(result) try: self._executed = executed_list[i].strip() i += 1 except IndexError: self._executed = executed_list[0] yield self def execute(self, operation, params=None, multi=False): """Executes the given operation Executes the given operation substituting any markers with the given parameters. For example, getting all rows where id is 5: cursor.execute("SELECT * FROM t1 WHERE id = %s", (5,)) The multi argument should be set to True when executing multiple statements in one operation. If not set and multiple results are found, an InterfaceError will be raised. If warnings where generated, and connection.get_warnings is True, then self._warnings will be a list containing these warnings. Returns an iterator when multi is True, otherwise None. """ if not operation: return None if not self._connection: raise errors.ProgrammingError("Cursor is not connected.") if self._connection.unread_result is True: raise errors.InternalError("Unread result found.") self._reset_result() stmt = '' try: if not isinstance(operation, (bytes, bytearray)): stmt = operation.encode(self._connection.python_charset) else: stmt = operation except (UnicodeDecodeError, UnicodeEncodeError) as err: raise errors.ProgrammingError(str(err)) if params is not None: if isinstance(params, dict): for key, value in self._process_params_dict(params).items(): stmt = stmt.replace(key, value) elif isinstance(params, (list, tuple)): psub = _ParamSubstitutor(self._process_params(params)) stmt = RE_PY_PARAM.sub(psub, stmt) if psub.remaining != 0: raise errors.ProgrammingError( "Not all parameters were used in the SQL statement") if multi: self._executed = stmt self._executed_list = [] return self._execute_iter(self._connection.cmd_query_iter(stmt)) else: self._executed = stmt try: self._handle_result(self._connection.cmd_query(stmt)) except errors.InterfaceError: if self._connection._have_next_result: # pylint: disable=W0212 raise errors.InterfaceError( "Use multi=True when executing multiple statements") raise return None def _batch_insert(self, operation, seq_params): """Implements multi row insert""" def remove_comments(match): """Remove comments from INSERT statements. This function is used while removing comments from INSERT statements. If the matched string is a comment not enclosed by quotes, it returns an empty string, else the string itself. """ if match.group(1): return "" else: return match.group(2) tmp = re.sub(RE_SQL_ON_DUPLICATE, '', re.sub(RE_SQL_COMMENT, remove_comments, operation)) matches = re.search(RE_SQL_INSERT_VALUES, tmp) if not matches: raise errors.InterfaceError( "Failed rewriting statement for multi-row INSERT. " "Check SQL syntax." ) fmt = matches.group(1).encode(self._connection.charset) values = [] try: stmt = operation.encode(self._connection.charset) for params in seq_params: tmp = fmt if isinstance(params, dict): for key, value in self._process_params_dict(params).items(): tmp = tmp.replace(key, value) else: psub = _ParamSubstitutor(self._process_params(params)) tmp = RE_PY_PARAM.sub(psub, tmp) if psub.remaining != 0: raise errors.ProgrammingError( "Not all parameters were used in the SQL statement") #for p in self._process_params(params): # tmp = tmp.replace(b'%s',p,1) values.append(tmp) if fmt in stmt: stmt = stmt.replace(fmt, b','.join(values), 1) self._executed = stmt return stmt else: return None except (UnicodeDecodeError, UnicodeEncodeError) as err: raise errors.ProgrammingError(str(err)) except errors.Error: raise except Exception as err: raise errors.InterfaceError( "Failed executing the operation; %s" % err) def executemany(self, operation, seq_params): """Execute the given operation multiple times The executemany() method will execute the operation iterating over the list of parameters in seq_params. Example: Inserting 3 new employees and their phone number data = [ ('Jane','555-001'), ('Joe', '555-001'), ('John', '555-003') ] stmt = "INSERT INTO employees (name, phone) VALUES ('%s','%s)" cursor.executemany(stmt, data) INSERT statements are optimized by batching the data, that is using the MySQL multiple rows syntax. Results are discarded. If they are needed, consider looping over data using the execute() method. """ if not operation or not seq_params: return None if self._connection.unread_result is True: raise errors.InternalError("Unread result found.") if not isinstance(seq_params, (list, tuple)): raise errors.ProgrammingError( "Parameters for query must be list or tuple.") # Optimize INSERTs by batching them if re.match(RE_SQL_INSERT_STMT, operation): if not seq_params: self._rowcount = 0 return stmt = self._batch_insert(operation, seq_params) if stmt is not None: return self.execute(stmt) rowcnt = 0 try: for params in seq_params: self.execute(operation, params) if self.with_rows and self._have_unread_result(): self.fetchall() rowcnt += self._rowcount except (ValueError, TypeError) as err: raise errors.InterfaceError( "Failed executing the operation; {0}".format(err)) except: # Raise whatever execute() raises raise self._rowcount = rowcnt def stored_results(self): """Returns an iterator for stored results This method returns an iterator over results which are stored when callproc() is called. The iterator will provide MySQLCursorBuffered instances. Returns a iterator. """ return iter(self._stored_results) def callproc(self, procname, args=()): """Calls a stored procedure with the given arguments The arguments will be set during this session, meaning they will be called like ___arg where is an enumeration (+1) of the arguments. Coding Example: 1) Defining the Stored Routine in MySQL: CREATE PROCEDURE multiply(IN pFac1 INT, IN pFac2 INT, OUT pProd INT) BEGIN SET pProd := pFac1 * pFac2; END 2) Executing in Python: args = (5, 5, 0) # 0 is to hold pprod cursor.callproc('multiply', args) print(cursor.fetchone()) For OUT and INOUT parameters the user should provide the type of the parameter as well. The argument should be a tuple with first item as the value of the parameter to pass and second argument the type of the argument. In the above example, one can call callproc method like: args = (5, 5, (0, 'INT')) cursor.callproc('multiply', args) The type of the argument given in the tuple will be used by the MySQL CAST function to convert the values in the corresponding MySQL type (See CAST in MySQL Reference for more information) Does not return a value, but a result set will be available when the CALL-statement execute successfully. Raises exceptions when something is wrong. """ if not procname or not isinstance(procname, str): raise ValueError("procname must be a string") if not isinstance(args, (tuple, list)): raise ValueError("args must be a sequence") argfmt = "@_{name}_arg{index}" self._stored_results = [] results = [] try: argnames = [] argtypes = [] if args: for idx, arg in enumerate(args): argname = argfmt.format(name=procname, index=idx + 1) argnames.append(argname) if isinstance(arg, tuple): argtypes.append(" CAST({0} AS {1})".format(argname, arg[1])) self.execute("SET {0}=%s".format(argname), (arg[0],)) else: argtypes.append(argname) self.execute("SET {0}=%s".format(argname), (arg,)) call = "CALL {0}({1})".format(procname, ','.join(argnames)) for result in self._connection.cmd_query_iter(call): # pylint: disable=W0212 tmp = MySQLCursorBuffered(self._connection._get_self()) tmp._handle_result(result) if tmp._warnings is not None: self._warnings = tmp._warnings # pylint: enable=W0212 if 'columns' in result: results.append(tmp) if argnames: select = "SELECT {0}".format(','.join(argtypes)) self.execute(select) self._stored_results = results return self.fetchone() else: self._stored_results = results return () except errors.Error: raise except Exception as err: raise errors.InterfaceError( "Failed calling stored routine; {0}".format(err)) def getlastrowid(self): """Returns the value generated for an AUTO_INCREMENT column Returns the value generated for an AUTO_INCREMENT column by the previous INSERT or UPDATE statement. Returns a long value or None. """ return self._last_insert_id def _fetch_warnings(self): """ Fetch warnings doing a SHOW WARNINGS. Can be called after getting the result. Returns a result set or None when there were no warnings. """ res = [] try: cur = self._connection.cursor() cur.execute("SHOW WARNINGS") res = cur.fetchall() cur.close() except Exception as err: raise errors.InterfaceError( "Failed getting warnings; %s" % err) if self._connection.raise_on_warnings is True: raise errors.get_mysql_exception(res[0][1], res[0][2]) else: if len(res): return res return None def _handle_eof(self, eof): """Handle EOF packet""" self._connection.unread_result = False self._nextrow = (None, None) self._warning_count = eof['warning_count'] if self._connection.get_warnings is True and eof['warning_count']: self._warnings = self._fetch_warnings() def _fetch_row(self): """Returns the next row in the result set Returns a tuple or None. """ if not self._have_unread_result(): return None row = None if self._nextrow == (None, None): (row, eof) = self._connection.get_row( binary=self._binary, columns=self.description) else: (row, eof) = self._nextrow if row: self._nextrow = self._connection.get_row( binary=self._binary, columns=self.description) eof = self._nextrow[1] if eof is not None: self._handle_eof(eof) if self._rowcount == -1: self._rowcount = 1 else: self._rowcount += 1 if eof: self._handle_eof(eof) return row def fetchwarnings(self): """Returns Warnings.""" return self._warnings def fetchone(self): """Returns next row of a query result set Returns a tuple or None. """ row = self._fetch_row() if row: return self._connection.converter.row_to_python( row, self.description) return None def fetchmany(self, size=None): res = [] cnt = (size or self.arraysize) while cnt > 0 and self._have_unread_result(): cnt -= 1 row = self.fetchone() if row: res.append(row) return res def fetchall(self): if not self._have_unread_result(): raise errors.InterfaceError("No result set to fetch from.") (rows, eof) = self._connection.get_rows() if self._nextrow[0]: rows.insert(0, self._nextrow[0]) res = [self._connection.converter.row_to_python(row, self.description) for row in rows] self._handle_eof(eof) rowcount = len(rows) if rowcount >= 0 and self._rowcount == -1: self._rowcount = 0 self._rowcount += rowcount return res @property def column_names(self): """Returns column names This property returns the columns names as a tuple. Returns a tuple. """ if not self.description: return () return tuple([d[0] for d in self.description]) @property def statement(self): """Returns the executed statement This property returns the executed statement. When multiple statements were executed, the current statement in the iterator will be returned. """ try: return self._executed.strip().decode('utf8') except AttributeError: return self._executed.strip() @property def with_rows(self): """Returns whether the cursor could have rows returned This property returns True when column descriptions are available and possibly also rows, which will need to be fetched. Returns True or False. """ if not self.description: return False return True def __str__(self): fmt = "MySQLCursor: %s" if self._executed: executed = bytearray(self._executed).decode('utf-8') if len(executed) > 30: res = fmt % (executed[:30] + '..') else: res = fmt % (executed) else: res = fmt % '(Nothing executed yet)' return res class MySQLCursorBuffered(MySQLCursor): """Cursor which fetches rows within execute()""" def __init__(self, connection=None): MySQLCursor.__init__(self, connection) self._rows = None self._next_row = 0 def _handle_resultset(self): (self._rows, eof) = self._connection.get_rows() self._rowcount = len(self._rows) self._handle_eof(eof) self._next_row = 0 try: self._connection.unread_result = False except: pass def reset(self): self._rows = None def _fetch_row(self): row = None try: row = self._rows[self._next_row] except: return None else: self._next_row += 1 return row return None def fetchall(self): if self._rows is None: raise errors.InterfaceError("No result set to fetch from.") res = [] for row in self._rows[self._next_row:]: res.append(self._connection.converter.row_to_python( row, self.description)) self._next_row = len(self._rows) return res def fetchmany(self, size=None): res = [] cnt = (size or self.arraysize) while cnt > 0: cnt -= 1 row = self.fetchone() if row: res.append(row) return res @property def with_rows(self): return self._rows is not None class MySQLCursorRaw(MySQLCursor): """ Skips conversion from MySQL datatypes to Python types when fetching rows. """ def fetchone(self): row = self._fetch_row() if row: return row return None def fetchall(self): if not self._have_unread_result(): raise errors.InterfaceError("No result set to fetch from.") (rows, eof) = self._connection.get_rows() if self._nextrow[0]: rows.insert(0, self._nextrow[0]) self._handle_eof(eof) rowcount = len(rows) if rowcount >= 0 and self._rowcount == -1: self._rowcount = 0 self._rowcount += rowcount return rows class MySQLCursorBufferedRaw(MySQLCursorBuffered): """ Cursor which skips conversion from MySQL datatypes to Python types when fetching rows and fetches rows within execute(). """ def fetchone(self): row = self._fetch_row() if row: return row return None def fetchall(self): if self._rows is None: raise errors.InterfaceError("No result set to fetch from.") return [r for r in self._rows[self._next_row:]] @property def with_rows(self): return self._rows is not None class MySQLCursorPrepared(MySQLCursor): """Cursor using MySQL Prepared Statements """ def __init__(self, connection=None): super(MySQLCursorPrepared, self).__init__(connection) self._rows = None self._next_row = 0 self._prepared = None self._binary = True self._have_result = None def callproc(self, *args, **kwargs): """Calls a stored procedue Not supported with MySQLCursorPrepared. """ raise errors.NotSupportedError() def close(self): """Close the cursor This method will try to deallocate the prepared statement and close the cursor. """ if self._prepared: try: self._connection.cmd_stmt_close(self._prepared['statement_id']) except errors.Error: # We tried to deallocate, but it's OK when we fail. pass self._prepared = None super(MySQLCursorPrepared, self).close() def _row_to_python(self, rowdata, desc=None): """Convert row data from MySQL to Python types The conversion is done while reading binary data in the protocol module. """ pass def _handle_result(self, res): """Handle result after execution""" if isinstance(res, dict): self._connection.unread_result = False self._have_result = False self._handle_noresultset(res) else: self._description = res[1] self._connection.unread_result = True self._have_result = True def execute(self, operation, params=(), multi=False): # multi is unused """Prepare and execute a MySQL Prepared Statement This method will preare the given operation and execute it using the optionally given parameters. If the cursor instance already had a prepared statement, it is first closed. """ if operation is not self._executed: if self._prepared: self._connection.cmd_stmt_close(self._prepared['statement_id']) self._executed = operation try: if not isinstance(operation, bytes): operation = operation.encode(self._connection.charset) except (UnicodeDecodeError, UnicodeEncodeError) as err: raise errors.ProgrammingError(str(err)) # need to convert %s to ? before sending it to MySQL if b'%s' in operation: operation = re.sub(RE_SQL_FIND_PARAM, b'?', operation) try: self._prepared = self._connection.cmd_stmt_prepare(operation) except errors.Error: self._executed = None raise self._connection.cmd_stmt_reset(self._prepared['statement_id']) if self._prepared['parameters'] and not params: return elif len(self._prepared['parameters']) != len(params): raise errors.ProgrammingError( errno=1210, msg="Incorrect number of arguments " \ "executing prepared statement") res = self._connection.cmd_stmt_execute( self._prepared['statement_id'], data=params, parameters=self._prepared['parameters']) self._handle_result(res) def executemany(self, operation, seq_params): """Prepare and execute a MySQL Prepared Statement many times This method will prepare the given operation and execute with each tuple found the list seq_params. If the cursor instance already had a prepared statement, it is first closed. executemany() simply calls execute(). """ rowcnt = 0 try: for params in seq_params: self.execute(operation, params) if self.with_rows and self._have_unread_result(): self.fetchall() rowcnt += self._rowcount except (ValueError, TypeError) as err: raise errors.InterfaceError( "Failed executing the operation; {error}".format(error=err)) except: # Raise whatever execute() raises raise self._rowcount = rowcnt def fetchone(self): """Returns next row of a query result set Returns a tuple or None. """ return self._fetch_row() or None def fetchmany(self, size=None): res = [] cnt = (size or self.arraysize) while cnt > 0 and self._have_unread_result(): cnt -= 1 row = self._fetch_row() if row: res.append(row) return res def fetchall(self): if not self._have_unread_result(): raise errors.InterfaceError("No result set to fetch from.") (rows, eof) = self._connection.get_rows( binary=self._binary, columns=self.description) self._rowcount = len(rows) self._handle_eof(eof) return rows class MySQLCursorDict(MySQLCursor): """ Cursor fetching rows as dictionaries. The fetch methods of this class will return dictionaries instead of tuples. Each row is a dictionary that looks like: row = { "col1": value1, "col2": value2 } """ def _row_to_python(self, rowdata, desc=None): """Convert a MySQL text result row to Python types Returns a dictionary. """ row = self._connection.converter.row_to_python(rowdata, desc) if row: return dict(zip(self.column_names, row)) return None def fetchone(self): """Returns next row of a query result set """ row = self._fetch_row() if row: return self._row_to_python(row, self.description) return None def fetchall(self): """Returns all rows of a query result set """ if not self._have_unread_result(): raise errors.InterfaceError(_ERR_NO_RESULT_TO_FETCH) (rows, eof) = self._connection.get_rows() if self._nextrow[0]: rows.insert(0, self._nextrow[0]) res = [self._row_to_python(row, self.description) for row in rows] self._handle_eof(eof) rowcount = len(rows) if rowcount >= 0 and self._rowcount == -1: self._rowcount = 0 self._rowcount += rowcount return res class MySQLCursorNamedTuple(MySQLCursor): """ Cursor fetching rows as named tuple. The fetch methods of this class will return namedtuples instead of tuples. Each row is returned as a namedtuple and the values can be accessed as: row.col1, row.col2 """ def _row_to_python(self, rowdata, desc=None): """Convert a MySQL text result row to Python types Returns a named tuple. """ row = self._connection.converter.row_to_python(rowdata, desc) if row: # pylint: disable=W0201 self.named_tuple = namedtuple('Row', self.column_names) # pylint: enable=W0201 return self.named_tuple(*row) def fetchone(self): """Returns next row of a query result set """ row = self._fetch_row() if row: return self._row_to_python(row, self.description) return None def fetchall(self): """Returns all rows of a query result set """ if not self._have_unread_result(): raise errors.InterfaceError(_ERR_NO_RESULT_TO_FETCH) (rows, eof) = self._connection.get_rows() if self._nextrow[0]: rows.insert(0, self._nextrow[0]) res = [self._row_to_python(row, self.description) for row in rows] self._handle_eof(eof) rowcount = len(rows) if rowcount >= 0 and self._rowcount == -1: self._rowcount = 0 self._rowcount += rowcount return res class MySQLCursorBufferedDict(MySQLCursorDict, MySQLCursorBuffered): """ Buffered Cursor fetching rows as dictionaries. """ def fetchone(self): """Returns next row of a query result set """ row = self._fetch_row() if row: return self._row_to_python(row, self.description) return None def fetchall(self): """Returns all rows of a query result set """ if self._rows is None: raise errors.InterfaceError(_ERR_NO_RESULT_TO_FETCH) res = [] for row in self._rows[self._next_row:]: res.append(self._row_to_python( row, self.description)) self._next_row = len(self._rows) return res class MySQLCursorBufferedNamedTuple(MySQLCursorNamedTuple, MySQLCursorBuffered): """ Buffered Cursor fetching rows as named tuple. """ def fetchone(self): """Returns next row of a query result set """ row = self._fetch_row() if row: return self._row_to_python(row, self.description) return None def fetchall(self): """Returns all rows of a query result set """ if self._rows is None: raise errors.InterfaceError(_ERR_NO_RESULT_TO_FETCH) res = [] for row in self._rows[self._next_row:]: res.append(self._row_to_python( row, self.description)) self._next_row = len(self._rows) return res mysql-connector-python-2.0.4/lib/mysql/connector/errors.py0000644001577100000120000002335112506707765023255 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Python exceptions """ from . import utils from .locales import get_client_error from .catch23 import PY2 # _CUSTOM_ERROR_EXCEPTIONS holds custom exceptions and is ued by the # function custom_error_exception. _ERROR_EXCEPTIONS (at bottom of module) # is similar, but hardcoded exceptions. _CUSTOM_ERROR_EXCEPTIONS = {} def custom_error_exception(error=None, exception=None): """Define custom exceptions for MySQL server errors This function defines custom exceptions for MySQL server errors and returns the current set customizations. If error is a MySQL Server error number, then you have to pass also the exception class. The error argument can also be a dictionary in which case the key is the server error number, and value the exception to be raised. If none of the arguments are given, then custom_error_exception() will simply return the current set customizations. To reset the customizations, simply supply an empty dictionary. Examples: import mysql.connector from mysql.connector import errorcode # Server error 1028 should raise a DatabaseError mysql.connector.custom_error_exception( 1028, mysql.connector.DatabaseError) # Or using a dictionary: mysql.connector.custom_error_exception({ 1028: mysql.connector.DatabaseError, 1029: mysql.connector.OperationalError, }) # Reset mysql.connector.custom_error_exception({}) Returns a dictionary. """ global _CUSTOM_ERROR_EXCEPTIONS # pylint: disable=W0603 if isinstance(error, dict) and not len(error): _CUSTOM_ERROR_EXCEPTIONS = {} return _CUSTOM_ERROR_EXCEPTIONS if not error and not exception: return _CUSTOM_ERROR_EXCEPTIONS if not isinstance(error, (int, dict)): raise ValueError( "The error argument should be either an integer or dictionary") if isinstance(error, int): error = {error: exception} for errno, exception in error.items(): if not isinstance(errno, int): raise ValueError("error number should be an integer") try: if not issubclass(exception, Exception): raise TypeError except TypeError: raise ValueError("exception should be subclass of Exception") _CUSTOM_ERROR_EXCEPTIONS[errno] = exception return _CUSTOM_ERROR_EXCEPTIONS def get_mysql_exception(errno, msg, sqlstate=None): """Get the exception matching the MySQL error This function will return an exception based on the SQLState. The given message will be passed on in the returned exception. The exception returned can be customized using the mysql.connector.custom_error_exception() function. Returns an Exception """ try: return _CUSTOM_ERROR_EXCEPTIONS[errno]( msg=msg, errno=errno, sqlstate=sqlstate) except KeyError: # Error was not mapped to particular exception pass try: return _ERROR_EXCEPTIONS[errno]( msg=msg, errno=errno, sqlstate=sqlstate) except KeyError: # Error was not mapped to particular exception pass if not sqlstate: return DatabaseError(msg=msg, errno=errno) try: return _SQLSTATE_CLASS_EXCEPTION[sqlstate[0:2]]( msg=msg, errno=errno, sqlstate=sqlstate) except KeyError: # Return default InterfaceError return DatabaseError(msg=msg, errno=errno, sqlstate=sqlstate) def get_exception(packet): """Returns an exception object based on the MySQL error Returns an exception object based on the MySQL error in the given packet. Returns an Error-Object. """ errno = errmsg = None if packet[4] != 255: raise ValueError("Packet is not an error packet") sqlstate = None try: packet = packet[5:] (packet, errno) = utils.read_int(packet, 2) if packet[0] != 35: # Error without SQLState if isinstance(packet, (bytes, bytearray)): errmsg = packet.decode('utf8') else: errmsg = packet else: (packet, sqlstate) = utils.read_bytes(packet[1:], 5) sqlstate = sqlstate.decode('utf8') errmsg = packet.decode('utf8') except Exception as err: # pylint: disable=W0703 return InterfaceError("Failed getting Error information (%r)" % err) else: return get_mysql_exception(errno, errmsg, sqlstate) class Error(Exception): """Exception that is base class for all other error exceptions""" def __init__(self, msg=None, errno=None, values=None, sqlstate=None): super(Error, self).__init__() self.msg = msg self._full_msg = self.msg self.errno = errno or -1 self.sqlstate = sqlstate if not self.msg and (2000 <= self.errno < 3000): self.msg = get_client_error(self.errno) if values is not None: try: self.msg = self.msg % values except TypeError as err: self.msg = "{0} (Warning: {1})".format(self.msg, str(err)) elif not self.msg: self._full_msg = self.msg = 'Unknown error' if self.msg and self.errno != -1: fields = { 'errno': self.errno, 'msg': self.msg.encode('utf8') if PY2 else self.msg } if self.sqlstate: fmt = '{errno} ({state}): {msg}' fields['state'] = self.sqlstate else: fmt = '{errno}: {msg}' self._full_msg = fmt.format(**fields) self.args = (self.errno, self._full_msg, self.sqlstate) def __str__(self): return self._full_msg class Warning(Exception): # pylint: disable=W0622 """Exception for important warnings""" pass class InterfaceError(Error): """Exception for errors related to the interface""" pass class DatabaseError(Error): """Exception for errors related to the database""" pass class InternalError(DatabaseError): """Exception for errors internal database errors""" pass class OperationalError(DatabaseError): """Exception for errors related to the database's operation""" pass class ProgrammingError(DatabaseError): """Exception for errors programming errors""" pass class IntegrityError(DatabaseError): """Exception for errors regarding relational integrity""" pass class DataError(DatabaseError): """Exception for errors reporting problems with processed data""" pass class NotSupportedError(DatabaseError): """Exception for errors when an unsupported database feature was used""" pass class PoolError(Error): """Exception for errors relating to connection pooling""" pass class MySQLFabricError(Error): """Exception for errors relating to MySQL Fabric""" _SQLSTATE_CLASS_EXCEPTION = { '02': DataError, # no data '07': DatabaseError, # dynamic SQL error '08': OperationalError, # connection exception '0A': NotSupportedError, # feature not supported '21': DataError, # cardinality violation '22': DataError, # data exception '23': IntegrityError, # integrity constraint violation '24': ProgrammingError, # invalid cursor state '25': ProgrammingError, # invalid transaction state '26': ProgrammingError, # invalid SQL statement name '27': ProgrammingError, # triggered data change violation '28': ProgrammingError, # invalid authorization specification '2A': ProgrammingError, # direct SQL syntax error or access rule violation '2B': DatabaseError, # dependent privilege descriptors still exist '2C': ProgrammingError, # invalid character set name '2D': DatabaseError, # invalid transaction termination '2E': DatabaseError, # invalid connection name '33': DatabaseError, # invalid SQL descriptor name '34': ProgrammingError, # invalid cursor name '35': ProgrammingError, # invalid condition number '37': ProgrammingError, # dynamic SQL syntax error or access rule violation '3C': ProgrammingError, # ambiguous cursor name '3D': ProgrammingError, # invalid catalog name '3F': ProgrammingError, # invalid schema name '40': InternalError, # transaction rollback '42': ProgrammingError, # syntax error or access rule violation '44': InternalError, # with check option violation 'HZ': OperationalError, # remote database access 'XA': IntegrityError, '0K': OperationalError, 'HY': DatabaseError, # default when no SQLState provided by MySQL server } _ERROR_EXCEPTIONS = { 1243: ProgrammingError, 1210: ProgrammingError, } mysql-connector-python-2.0.4/lib/mysql/connector/network.py0000644001577100000120000004224112506707765023431 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Module implementing low-level socket communication with MySQL servers. """ from collections import deque import socket import struct import sys import zlib try: import ssl except: # If import fails, we don't have SSL support. pass from . import constants, errors from .catch23 import PY2, init_bytearray, struct_unpack def _strioerror(err): """Reformat the IOError error message This function reformats the IOError error message. """ if not err.errno: return str(err) return '{errno} {strerr}'.format(errno=err.errno, strerr=err.strerror) def _prepare_packets(buf, pktnr): """Prepare a packet for sending to the MySQL server""" pkts = [] pllen = len(buf) maxpktlen = constants.MAX_PACKET_LENGTH while pllen > maxpktlen: pkts.append(b'\xff\xff\xff' + struct.pack(' 255: self._packet_number = 0 return self._packet_number def open_connection(self): """Open the socket""" raise NotImplementedError def get_address(self): """Get the location of the socket""" raise NotImplementedError def shutdown(self): """Shut down the socket before closing it""" try: self.sock.shutdown(socket.SHUT_RDWR) self.sock.close() del self._packet_queue except (socket.error, AttributeError): pass def close_connection(self): """Close the socket""" try: self.sock.close() del self._packet_queue except (socket.error, AttributeError): pass def send_plain(self, buf, packet_number=None): """Send packets to the MySQL server""" if packet_number is None: self.next_packet_number # pylint: disable=W0104 else: self._packet_number = packet_number packets = _prepare_packets(buf, self._packet_number) for packet in packets: try: if PY2: self.sock.sendall(buffer(packet)) # pylint: disable=E0602 else: self.sock.sendall(packet) except IOError as err: raise errors.OperationalError( errno=2055, values=(self.get_address(), _strioerror(err))) except AttributeError: raise errors.OperationalError(errno=2006) send = send_plain def send_compressed(self, buf, packet_number=None): """Send compressed packets to the MySQL server""" if packet_number is None: self.next_packet_number # pylint: disable=W0104 else: self._packet_number = packet_number pktnr = self._packet_number pllen = len(buf) zpkts = [] maxpktlen = constants.MAX_PACKET_LENGTH if pllen > maxpktlen: pkts = _prepare_packets(buf, pktnr) if PY2: tmpbuf = bytearray() for pkt in pkts: tmpbuf += pkt tmpbuf = buffer(tmpbuf) # pylint: disable=E0602 else: tmpbuf = b''.join(pkts) del pkts seqid = 0 zbuf = zlib.compress(tmpbuf[:16384]) header = (struct.pack(' maxpktlen: zbuf = zlib.compress(tmpbuf[:maxpktlen]) header = (struct.pack(' 50: zbuf = zlib.compress(pkt) zpkts.append(struct.pack(' 0: raise errors.InterfaceError(errno=2013) packet_view = packet_view[read:] rest -= read return packet except IOError as err: raise errors.OperationalError( errno=2055, values=(self.get_address(), _strioerror(err))) def recv_py26_plain(self): """Receive packets from the MySQL server""" try: # Read the header of the MySQL packet, 4 bytes header = bytearray(b'') header_len = 0 while header_len < 4: chunk = self.sock.recv(4 - header_len) if not chunk: raise errors.InterfaceError(errno=2013) header += chunk header_len = len(header) # Save the packet number and payload length self._packet_number = header[3] payload_len = struct_unpack(" 0: chunk = self.sock.recv(rest) if not chunk: raise errors.InterfaceError(errno=2013) payload += chunk rest = payload_len - len(payload) return header + payload except IOError as err: raise errors.OperationalError( errno=2055, values=(self.get_address(), _strioerror(err))) if sys.version_info[0:2] == (2, 6): recv = recv_py26_plain recv_plain = recv_py26_plain else: recv = recv_plain def _split_zipped_payload(self, packet_bunch): """Split compressed payload""" while packet_bunch: payload_length = struct_unpack(", like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 # This file was auto-generated. _GENERATED_ON = '2014-10-10' _MYSQL_VERSION = (5, 7, 5) """This module contains the MySQL Server and Client error codes""" # Start MySQL Errors ER_HASHCHK = 1000 ER_NISAMCHK = 1001 ER_NO = 1002 ER_YES = 1003 ER_CANT_CREATE_FILE = 1004 ER_CANT_CREATE_TABLE = 1005 ER_CANT_CREATE_DB = 1006 ER_DB_CREATE_EXISTS = 1007 ER_DB_DROP_EXISTS = 1008 ER_DB_DROP_DELETE = 1009 ER_DB_DROP_RMDIR = 1010 ER_CANT_DELETE_FILE = 1011 ER_CANT_FIND_SYSTEM_REC = 1012 ER_CANT_GET_STAT = 1013 ER_CANT_GET_WD = 1014 ER_CANT_LOCK = 1015 ER_CANT_OPEN_FILE = 1016 ER_FILE_NOT_FOUND = 1017 ER_CANT_READ_DIR = 1018 ER_CANT_SET_WD = 1019 ER_CHECKREAD = 1020 ER_DISK_FULL = 1021 ER_DUP_KEY = 1022 ER_ERROR_ON_CLOSE = 1023 ER_ERROR_ON_READ = 1024 ER_ERROR_ON_RENAME = 1025 ER_ERROR_ON_WRITE = 1026 ER_FILE_USED = 1027 ER_FILSORT_ABORT = 1028 ER_FORM_NOT_FOUND = 1029 ER_GET_ERRNO = 1030 ER_ILLEGAL_HA = 1031 ER_KEY_NOT_FOUND = 1032 ER_NOT_FORM_FILE = 1033 ER_NOT_KEYFILE = 1034 ER_OLD_KEYFILE = 1035 ER_OPEN_AS_READONLY = 1036 ER_OUTOFMEMORY = 1037 ER_OUT_OF_SORTMEMORY = 1038 ER_UNEXPECTED_EOF = 1039 ER_CON_COUNT_ERROR = 1040 ER_OUT_OF_RESOURCES = 1041 ER_BAD_HOST_ERROR = 1042 ER_HANDSHAKE_ERROR = 1043 ER_DBACCESS_DENIED_ERROR = 1044 ER_ACCESS_DENIED_ERROR = 1045 ER_NO_DB_ERROR = 1046 ER_UNKNOWN_COM_ERROR = 1047 ER_BAD_NULL_ERROR = 1048 ER_BAD_DB_ERROR = 1049 ER_TABLE_EXISTS_ERROR = 1050 ER_BAD_TABLE_ERROR = 1051 ER_NON_UNIQ_ERROR = 1052 ER_SERVER_SHUTDOWN = 1053 ER_BAD_FIELD_ERROR = 1054 ER_WRONG_FIELD_WITH_GROUP = 1055 ER_WRONG_GROUP_FIELD = 1056 ER_WRONG_SUM_SELECT = 1057 ER_WRONG_VALUE_COUNT = 1058 ER_TOO_LONG_IDENT = 1059 ER_DUP_FIELDNAME = 1060 ER_DUP_KEYNAME = 1061 ER_DUP_ENTRY = 1062 ER_WRONG_FIELD_SPEC = 1063 ER_PARSE_ERROR = 1064 ER_EMPTY_QUERY = 1065 ER_NONUNIQ_TABLE = 1066 ER_INVALID_DEFAULT = 1067 ER_MULTIPLE_PRI_KEY = 1068 ER_TOO_MANY_KEYS = 1069 ER_TOO_MANY_KEY_PARTS = 1070 ER_TOO_LONG_KEY = 1071 ER_KEY_COLUMN_DOES_NOT_EXITS = 1072 ER_BLOB_USED_AS_KEY = 1073 ER_TOO_BIG_FIELDLENGTH = 1074 ER_WRONG_AUTO_KEY = 1075 ER_READY = 1076 ER_NORMAL_SHUTDOWN = 1077 ER_GOT_SIGNAL = 1078 ER_SHUTDOWN_COMPLETE = 1079 ER_FORCING_CLOSE = 1080 ER_IPSOCK_ERROR = 1081 ER_NO_SUCH_INDEX = 1082 ER_WRONG_FIELD_TERMINATORS = 1083 ER_BLOBS_AND_NO_TERMINATED = 1084 ER_TEXTFILE_NOT_READABLE = 1085 ER_FILE_EXISTS_ERROR = 1086 ER_LOAD_INFO = 1087 ER_ALTER_INFO = 1088 ER_WRONG_SUB_KEY = 1089 ER_CANT_REMOVE_ALL_FIELDS = 1090 ER_CANT_DROP_FIELD_OR_KEY = 1091 ER_INSERT_INFO = 1092 ER_UPDATE_TABLE_USED = 1093 ER_NO_SUCH_THREAD = 1094 ER_KILL_DENIED_ERROR = 1095 ER_NO_TABLES_USED = 1096 ER_TOO_BIG_SET = 1097 ER_NO_UNIQUE_LOGFILE = 1098 ER_TABLE_NOT_LOCKED_FOR_WRITE = 1099 ER_TABLE_NOT_LOCKED = 1100 ER_BLOB_CANT_HAVE_DEFAULT = 1101 ER_WRONG_DB_NAME = 1102 ER_WRONG_TABLE_NAME = 1103 ER_TOO_BIG_SELECT = 1104 ER_UNKNOWN_ERROR = 1105 ER_UNKNOWN_PROCEDURE = 1106 ER_WRONG_PARAMCOUNT_TO_PROCEDURE = 1107 ER_WRONG_PARAMETERS_TO_PROCEDURE = 1108 ER_UNKNOWN_TABLE = 1109 ER_FIELD_SPECIFIED_TWICE = 1110 ER_INVALID_GROUP_FUNC_USE = 1111 ER_UNSUPPORTED_EXTENSION = 1112 ER_TABLE_MUST_HAVE_COLUMNS = 1113 ER_RECORD_FILE_FULL = 1114 ER_UNKNOWN_CHARACTER_SET = 1115 ER_TOO_MANY_TABLES = 1116 ER_TOO_MANY_FIELDS = 1117 ER_TOO_BIG_ROWSIZE = 1118 ER_STACK_OVERRUN = 1119 ER_WRONG_OUTER_JOIN = 1120 ER_NULL_COLUMN_IN_INDEX = 1121 ER_CANT_FIND_UDF = 1122 ER_CANT_INITIALIZE_UDF = 1123 ER_UDF_NO_PATHS = 1124 ER_UDF_EXISTS = 1125 ER_CANT_OPEN_LIBRARY = 1126 ER_CANT_FIND_DL_ENTRY = 1127 ER_FUNCTION_NOT_DEFINED = 1128 ER_HOST_IS_BLOCKED = 1129 ER_HOST_NOT_PRIVILEGED = 1130 ER_PASSWORD_ANONYMOUS_USER = 1131 ER_PASSWORD_NOT_ALLOWED = 1132 ER_PASSWORD_NO_MATCH = 1133 ER_UPDATE_INFO = 1134 ER_CANT_CREATE_THREAD = 1135 ER_WRONG_VALUE_COUNT_ON_ROW = 1136 ER_CANT_REOPEN_TABLE = 1137 ER_INVALID_USE_OF_NULL = 1138 ER_REGEXP_ERROR = 1139 ER_MIX_OF_GROUP_FUNC_AND_FIELDS = 1140 ER_NONEXISTING_GRANT = 1141 ER_TABLEACCESS_DENIED_ERROR = 1142 ER_COLUMNACCESS_DENIED_ERROR = 1143 ER_ILLEGAL_GRANT_FOR_TABLE = 1144 ER_GRANT_WRONG_HOST_OR_USER = 1145 ER_NO_SUCH_TABLE = 1146 ER_NONEXISTING_TABLE_GRANT = 1147 ER_NOT_ALLOWED_COMMAND = 1148 ER_SYNTAX_ERROR = 1149 ER_UNUSED1 = 1150 ER_UNUSED2 = 1151 ER_ABORTING_CONNECTION = 1152 ER_NET_PACKET_TOO_LARGE = 1153 ER_NET_READ_ERROR_FROM_PIPE = 1154 ER_NET_FCNTL_ERROR = 1155 ER_NET_PACKETS_OUT_OF_ORDER = 1156 ER_NET_UNCOMPRESS_ERROR = 1157 ER_NET_READ_ERROR = 1158 ER_NET_READ_INTERRUPTED = 1159 ER_NET_ERROR_ON_WRITE = 1160 ER_NET_WRITE_INTERRUPTED = 1161 ER_TOO_LONG_STRING = 1162 ER_TABLE_CANT_HANDLE_BLOB = 1163 ER_TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164 ER_UNUSED3 = 1165 ER_WRONG_COLUMN_NAME = 1166 ER_WRONG_KEY_COLUMN = 1167 ER_WRONG_MRG_TABLE = 1168 ER_DUP_UNIQUE = 1169 ER_BLOB_KEY_WITHOUT_LENGTH = 1170 ER_PRIMARY_CANT_HAVE_NULL = 1171 ER_TOO_MANY_ROWS = 1172 ER_REQUIRES_PRIMARY_KEY = 1173 ER_NO_RAID_COMPILED = 1174 ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175 ER_KEY_DOES_NOT_EXITS = 1176 ER_CHECK_NO_SUCH_TABLE = 1177 ER_CHECK_NOT_IMPLEMENTED = 1178 ER_CANT_DO_THIS_DURING_AN_TRANSACTION = 1179 ER_ERROR_DURING_COMMIT = 1180 ER_ERROR_DURING_ROLLBACK = 1181 ER_ERROR_DURING_FLUSH_LOGS = 1182 ER_ERROR_DURING_CHECKPOINT = 1183 ER_NEW_ABORTING_CONNECTION = 1184 ER_DUMP_NOT_IMPLEMENTED = 1185 ER_FLUSH_MASTER_BINLOG_CLOSED = 1186 ER_INDEX_REBUILD = 1187 ER_MASTER = 1188 ER_MASTER_NET_READ = 1189 ER_MASTER_NET_WRITE = 1190 ER_FT_MATCHING_KEY_NOT_FOUND = 1191 ER_LOCK_OR_ACTIVE_TRANSACTION = 1192 ER_UNKNOWN_SYSTEM_VARIABLE = 1193 ER_CRASHED_ON_USAGE = 1194 ER_CRASHED_ON_REPAIR = 1195 ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196 ER_TRANS_CACHE_FULL = 1197 ER_SLAVE_MUST_STOP = 1198 ER_SLAVE_NOT_RUNNING = 1199 ER_BAD_SLAVE = 1200 ER_MASTER_INFO = 1201 ER_SLAVE_THREAD = 1202 ER_TOO_MANY_USER_CONNECTIONS = 1203 ER_SET_CONSTANTS_ONLY = 1204 ER_LOCK_WAIT_TIMEOUT = 1205 ER_LOCK_TABLE_FULL = 1206 ER_READ_ONLY_TRANSACTION = 1207 ER_DROP_DB_WITH_READ_LOCK = 1208 ER_CREATE_DB_WITH_READ_LOCK = 1209 ER_WRONG_ARGUMENTS = 1210 ER_NO_PERMISSION_TO_CREATE_USER = 1211 ER_UNION_TABLES_IN_DIFFERENT_DIR = 1212 ER_LOCK_DEADLOCK = 1213 ER_TABLE_CANT_HANDLE_FT = 1214 ER_CANNOT_ADD_FOREIGN = 1215 ER_NO_REFERENCED_ROW = 1216 ER_ROW_IS_REFERENCED = 1217 ER_CONNECT_TO_MASTER = 1218 ER_QUERY_ON_MASTER = 1219 ER_ERROR_WHEN_EXECUTING_COMMAND = 1220 ER_WRONG_USAGE = 1221 ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222 ER_CANT_UPDATE_WITH_READLOCK = 1223 ER_MIXING_NOT_ALLOWED = 1224 ER_DUP_ARGUMENT = 1225 ER_USER_LIMIT_REACHED = 1226 ER_SPECIFIC_ACCESS_DENIED_ERROR = 1227 ER_LOCAL_VARIABLE = 1228 ER_GLOBAL_VARIABLE = 1229 ER_NO_DEFAULT = 1230 ER_WRONG_VALUE_FOR_VAR = 1231 ER_WRONG_TYPE_FOR_VAR = 1232 ER_VAR_CANT_BE_READ = 1233 ER_CANT_USE_OPTION_HERE = 1234 ER_NOT_SUPPORTED_YET = 1235 ER_MASTER_FATAL_ERROR_READING_BINLOG = 1236 ER_SLAVE_IGNORED_TABLE = 1237 ER_INCORRECT_GLOBAL_LOCAL_VAR = 1238 ER_WRONG_FK_DEF = 1239 ER_KEY_REF_DO_NOT_MATCH_TABLE_REF = 1240 ER_OPERAND_COLUMNS = 1241 ER_SUBQUERY_NO_1_ROW = 1242 ER_UNKNOWN_STMT_HANDLER = 1243 ER_CORRUPT_HELP_DB = 1244 ER_CYCLIC_REFERENCE = 1245 ER_AUTO_CONVERT = 1246 ER_ILLEGAL_REFERENCE = 1247 ER_DERIVED_MUST_HAVE_ALIAS = 1248 ER_SELECT_REDUCED = 1249 ER_TABLENAME_NOT_ALLOWED_HERE = 1250 ER_NOT_SUPPORTED_AUTH_MODE = 1251 ER_SPATIAL_CANT_HAVE_NULL = 1252 ER_COLLATION_CHARSET_MISMATCH = 1253 ER_SLAVE_WAS_RUNNING = 1254 ER_SLAVE_WAS_NOT_RUNNING = 1255 ER_TOO_BIG_FOR_UNCOMPRESS = 1256 ER_ZLIB_Z_MEM_ERROR = 1257 ER_ZLIB_Z_BUF_ERROR = 1258 ER_ZLIB_Z_DATA_ERROR = 1259 ER_CUT_VALUE_GROUP_CONCAT = 1260 ER_WARN_TOO_FEW_RECORDS = 1261 ER_WARN_TOO_MANY_RECORDS = 1262 ER_WARN_NULL_TO_NOTNULL = 1263 ER_WARN_DATA_OUT_OF_RANGE = 1264 WARN_DATA_TRUNCATED = 1265 ER_WARN_USING_OTHER_HANDLER = 1266 ER_CANT_AGGREGATE_2COLLATIONS = 1267 ER_DROP_USER = 1268 ER_REVOKE_GRANTS = 1269 ER_CANT_AGGREGATE_3COLLATIONS = 1270 ER_CANT_AGGREGATE_NCOLLATIONS = 1271 ER_VARIABLE_IS_NOT_STRUCT = 1272 ER_UNKNOWN_COLLATION = 1273 ER_SLAVE_IGNORED_SSL_PARAMS = 1274 ER_SERVER_IS_IN_SECURE_AUTH_MODE = 1275 ER_WARN_FIELD_RESOLVED = 1276 ER_BAD_SLAVE_UNTIL_COND = 1277 ER_MISSING_SKIP_SLAVE = 1278 ER_UNTIL_COND_IGNORED = 1279 ER_WRONG_NAME_FOR_INDEX = 1280 ER_WRONG_NAME_FOR_CATALOG = 1281 ER_WARN_QC_RESIZE = 1282 ER_BAD_FT_COLUMN = 1283 ER_UNKNOWN_KEY_CACHE = 1284 ER_WARN_HOSTNAME_WONT_WORK = 1285 ER_UNKNOWN_STORAGE_ENGINE = 1286 ER_WARN_DEPRECATED_SYNTAX = 1287 ER_NON_UPDATABLE_TABLE = 1288 ER_FEATURE_DISABLED = 1289 ER_OPTION_PREVENTS_STATEMENT = 1290 ER_DUPLICATED_VALUE_IN_TYPE = 1291 ER_TRUNCATED_WRONG_VALUE = 1292 ER_TOO_MUCH_AUTO_TIMESTAMP_COLS = 1293 ER_INVALID_ON_UPDATE = 1294 ER_UNSUPPORTED_PS = 1295 ER_GET_ERRMSG = 1296 ER_GET_TEMPORARY_ERRMSG = 1297 ER_UNKNOWN_TIME_ZONE = 1298 ER_WARN_INVALID_TIMESTAMP = 1299 ER_INVALID_CHARACTER_STRING = 1300 ER_WARN_ALLOWED_PACKET_OVERFLOWED = 1301 ER_CONFLICTING_DECLARATIONS = 1302 ER_SP_NO_RECURSIVE_CREATE = 1303 ER_SP_ALREADY_EXISTS = 1304 ER_SP_DOES_NOT_EXIST = 1305 ER_SP_DROP_FAILED = 1306 ER_SP_STORE_FAILED = 1307 ER_SP_LILABEL_MISMATCH = 1308 ER_SP_LABEL_REDEFINE = 1309 ER_SP_LABEL_MISMATCH = 1310 ER_SP_UNINIT_VAR = 1311 ER_SP_BADSELECT = 1312 ER_SP_BADRETURN = 1313 ER_SP_BADSTATEMENT = 1314 ER_UPDATE_LOG_DEPRECATED_IGNORED = 1315 ER_UPDATE_LOG_DEPRECATED_TRANSLATED = 1316 ER_QUERY_INTERRUPTED = 1317 ER_SP_WRONG_NO_OF_ARGS = 1318 ER_SP_COND_MISMATCH = 1319 ER_SP_NORETURN = 1320 ER_SP_NORETURNEND = 1321 ER_SP_BAD_CURSOR_QUERY = 1322 ER_SP_BAD_CURSOR_SELECT = 1323 ER_SP_CURSOR_MISMATCH = 1324 ER_SP_CURSOR_ALREADY_OPEN = 1325 ER_SP_CURSOR_NOT_OPEN = 1326 ER_SP_UNDECLARED_VAR = 1327 ER_SP_WRONG_NO_OF_FETCH_ARGS = 1328 ER_SP_FETCH_NO_DATA = 1329 ER_SP_DUP_PARAM = 1330 ER_SP_DUP_VAR = 1331 ER_SP_DUP_COND = 1332 ER_SP_DUP_CURS = 1333 ER_SP_CANT_ALTER = 1334 ER_SP_SUBSELECT_NYI = 1335 ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG = 1336 ER_SP_VARCOND_AFTER_CURSHNDLR = 1337 ER_SP_CURSOR_AFTER_HANDLER = 1338 ER_SP_CASE_NOT_FOUND = 1339 ER_FPARSER_TOO_BIG_FILE = 1340 ER_FPARSER_BAD_HEADER = 1341 ER_FPARSER_EOF_IN_COMMENT = 1342 ER_FPARSER_ERROR_IN_PARAMETER = 1343 ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER = 1344 ER_VIEW_NO_EXPLAIN = 1345 ER_FRM_UNKNOWN_TYPE = 1346 ER_WRONG_OBJECT = 1347 ER_NONUPDATEABLE_COLUMN = 1348 ER_VIEW_SELECT_DERIVED = 1349 ER_VIEW_SELECT_CLAUSE = 1350 ER_VIEW_SELECT_VARIABLE = 1351 ER_VIEW_SELECT_TMPTABLE = 1352 ER_VIEW_WRONG_LIST = 1353 ER_WARN_VIEW_MERGE = 1354 ER_WARN_VIEW_WITHOUT_KEY = 1355 ER_VIEW_INVALID = 1356 ER_SP_NO_DROP_SP = 1357 ER_SP_GOTO_IN_HNDLR = 1358 ER_TRG_ALREADY_EXISTS = 1359 ER_TRG_DOES_NOT_EXIST = 1360 ER_TRG_ON_VIEW_OR_TEMP_TABLE = 1361 ER_TRG_CANT_CHANGE_ROW = 1362 ER_TRG_NO_SUCH_ROW_IN_TRG = 1363 ER_NO_DEFAULT_FOR_FIELD = 1364 ER_DIVISION_BY_ZERO = 1365 ER_TRUNCATED_WRONG_VALUE_FOR_FIELD = 1366 ER_ILLEGAL_VALUE_FOR_TYPE = 1367 ER_VIEW_NONUPD_CHECK = 1368 ER_VIEW_CHECK_FAILED = 1369 ER_PROCACCESS_DENIED_ERROR = 1370 ER_RELAY_LOG_FAIL = 1371 ER_PASSWD_LENGTH = 1372 ER_UNKNOWN_TARGET_BINLOG = 1373 ER_IO_ERR_LOG_INDEX_READ = 1374 ER_BINLOG_PURGE_PROHIBITED = 1375 ER_FSEEK_FAIL = 1376 ER_BINLOG_PURGE_FATAL_ERR = 1377 ER_LOG_IN_USE = 1378 ER_LOG_PURGE_UNKNOWN_ERR = 1379 ER_RELAY_LOG_INIT = 1380 ER_NO_BINARY_LOGGING = 1381 ER_RESERVED_SYNTAX = 1382 ER_WSAS_FAILED = 1383 ER_DIFF_GROUPS_PROC = 1384 ER_NO_GROUP_FOR_PROC = 1385 ER_ORDER_WITH_PROC = 1386 ER_LOGGING_PROHIBIT_CHANGING_OF = 1387 ER_NO_FILE_MAPPING = 1388 ER_WRONG_MAGIC = 1389 ER_PS_MANY_PARAM = 1390 ER_KEY_PART_0 = 1391 ER_VIEW_CHECKSUM = 1392 ER_VIEW_MULTIUPDATE = 1393 ER_VIEW_NO_INSERT_FIELD_LIST = 1394 ER_VIEW_DELETE_MERGE_VIEW = 1395 ER_CANNOT_USER = 1396 ER_XAER_NOTA = 1397 ER_XAER_INVAL = 1398 ER_XAER_RMFAIL = 1399 ER_XAER_OUTSIDE = 1400 ER_XAER_RMERR = 1401 ER_XA_RBROLLBACK = 1402 ER_NONEXISTING_PROC_GRANT = 1403 ER_PROC_AUTO_GRANT_FAIL = 1404 ER_PROC_AUTO_REVOKE_FAIL = 1405 ER_DATA_TOO_LONG = 1406 ER_SP_BAD_SQLSTATE = 1407 ER_STARTUP = 1408 ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR = 1409 ER_CANT_CREATE_USER_WITH_GRANT = 1410 ER_WRONG_VALUE_FOR_TYPE = 1411 ER_TABLE_DEF_CHANGED = 1412 ER_SP_DUP_HANDLER = 1413 ER_SP_NOT_VAR_ARG = 1414 ER_SP_NO_RETSET = 1415 ER_CANT_CREATE_GEOMETRY_OBJECT = 1416 ER_FAILED_ROUTINE_BREAK_BINLOG = 1417 ER_BINLOG_UNSAFE_ROUTINE = 1418 ER_BINLOG_CREATE_ROUTINE_NEED_SUPER = 1419 ER_EXEC_STMT_WITH_OPEN_CURSOR = 1420 ER_STMT_HAS_NO_OPEN_CURSOR = 1421 ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG = 1422 ER_NO_DEFAULT_FOR_VIEW_FIELD = 1423 ER_SP_NO_RECURSION = 1424 ER_TOO_BIG_SCALE = 1425 ER_TOO_BIG_PRECISION = 1426 ER_M_BIGGER_THAN_D = 1427 ER_WRONG_LOCK_OF_SYSTEM_TABLE = 1428 ER_CONNECT_TO_FOREIGN_DATA_SOURCE = 1429 ER_QUERY_ON_FOREIGN_DATA_SOURCE = 1430 ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST = 1431 ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE = 1432 ER_FOREIGN_DATA_STRING_INVALID = 1433 ER_CANT_CREATE_FEDERATED_TABLE = 1434 ER_TRG_IN_WRONG_SCHEMA = 1435 ER_STACK_OVERRUN_NEED_MORE = 1436 ER_TOO_LONG_BODY = 1437 ER_WARN_CANT_DROP_DEFAULT_KEYCACHE = 1438 ER_TOO_BIG_DISPLAYWIDTH = 1439 ER_XAER_DUPID = 1440 ER_DATETIME_FUNCTION_OVERFLOW = 1441 ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG = 1442 ER_VIEW_PREVENT_UPDATE = 1443 ER_PS_NO_RECURSION = 1444 ER_SP_CANT_SET_AUTOCOMMIT = 1445 ER_MALFORMED_DEFINER = 1446 ER_VIEW_FRM_NO_USER = 1447 ER_VIEW_OTHER_USER = 1448 ER_NO_SUCH_USER = 1449 ER_FORBID_SCHEMA_CHANGE = 1450 ER_ROW_IS_REFERENCED_2 = 1451 ER_NO_REFERENCED_ROW_2 = 1452 ER_SP_BAD_VAR_SHADOW = 1453 ER_TRG_NO_DEFINER = 1454 ER_OLD_FILE_FORMAT = 1455 ER_SP_RECURSION_LIMIT = 1456 ER_SP_PROC_TABLE_CORRUPT = 1457 ER_SP_WRONG_NAME = 1458 ER_TABLE_NEEDS_UPGRADE = 1459 ER_SP_NO_AGGREGATE = 1460 ER_MAX_PREPARED_STMT_COUNT_REACHED = 1461 ER_VIEW_RECURSIVE = 1462 ER_NON_GROUPING_FIELD_USED = 1463 ER_TABLE_CANT_HANDLE_SPKEYS = 1464 ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA = 1465 ER_REMOVED_SPACES = 1466 ER_AUTOINC_READ_FAILED = 1467 ER_USERNAME = 1468 ER_HOSTNAME = 1469 ER_WRONG_STRING_LENGTH = 1470 ER_NON_INSERTABLE_TABLE = 1471 ER_ADMIN_WRONG_MRG_TABLE = 1472 ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT = 1473 ER_NAME_BECOMES_EMPTY = 1474 ER_AMBIGUOUS_FIELD_TERM = 1475 ER_FOREIGN_SERVER_EXISTS = 1476 ER_FOREIGN_SERVER_DOESNT_EXIST = 1477 ER_ILLEGAL_HA_CREATE_OPTION = 1478 ER_PARTITION_REQUIRES_VALUES_ERROR = 1479 ER_PARTITION_WRONG_VALUES_ERROR = 1480 ER_PARTITION_MAXVALUE_ERROR = 1481 ER_PARTITION_SUBPARTITION_ERROR = 1482 ER_PARTITION_SUBPART_MIX_ERROR = 1483 ER_PARTITION_WRONG_NO_PART_ERROR = 1484 ER_PARTITION_WRONG_NO_SUBPART_ERROR = 1485 ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR = 1486 ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR = 1487 ER_FIELD_NOT_FOUND_PART_ERROR = 1488 ER_LIST_OF_FIELDS_ONLY_IN_HASH_ERROR = 1489 ER_INCONSISTENT_PARTITION_INFO_ERROR = 1490 ER_PARTITION_FUNC_NOT_ALLOWED_ERROR = 1491 ER_PARTITIONS_MUST_BE_DEFINED_ERROR = 1492 ER_RANGE_NOT_INCREASING_ERROR = 1493 ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR = 1494 ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR = 1495 ER_PARTITION_ENTRY_ERROR = 1496 ER_MIX_HANDLER_ERROR = 1497 ER_PARTITION_NOT_DEFINED_ERROR = 1498 ER_TOO_MANY_PARTITIONS_ERROR = 1499 ER_SUBPARTITION_ERROR = 1500 ER_CANT_CREATE_HANDLER_FILE = 1501 ER_BLOB_FIELD_IN_PART_FUNC_ERROR = 1502 ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF = 1503 ER_NO_PARTS_ERROR = 1504 ER_PARTITION_MGMT_ON_NONPARTITIONED = 1505 ER_FOREIGN_KEY_ON_PARTITIONED = 1506 ER_DROP_PARTITION_NON_EXISTENT = 1507 ER_DROP_LAST_PARTITION = 1508 ER_COALESCE_ONLY_ON_HASH_PARTITION = 1509 ER_REORG_HASH_ONLY_ON_SAME_NO = 1510 ER_REORG_NO_PARAM_ERROR = 1511 ER_ONLY_ON_RANGE_LIST_PARTITION = 1512 ER_ADD_PARTITION_SUBPART_ERROR = 1513 ER_ADD_PARTITION_NO_NEW_PARTITION = 1514 ER_COALESCE_PARTITION_NO_PARTITION = 1515 ER_REORG_PARTITION_NOT_EXIST = 1516 ER_SAME_NAME_PARTITION = 1517 ER_NO_BINLOG_ERROR = 1518 ER_CONSECUTIVE_REORG_PARTITIONS = 1519 ER_REORG_OUTSIDE_RANGE = 1520 ER_PARTITION_FUNCTION_FAILURE = 1521 ER_PART_STATE_ERROR = 1522 ER_LIMITED_PART_RANGE = 1523 ER_PLUGIN_IS_NOT_LOADED = 1524 ER_WRONG_VALUE = 1525 ER_NO_PARTITION_FOR_GIVEN_VALUE = 1526 ER_FILEGROUP_OPTION_ONLY_ONCE = 1527 ER_CREATE_FILEGROUP_FAILED = 1528 ER_DROP_FILEGROUP_FAILED = 1529 ER_TABLESPACE_AUTO_EXTEND_ERROR = 1530 ER_WRONG_SIZE_NUMBER = 1531 ER_SIZE_OVERFLOW_ERROR = 1532 ER_ALTER_FILEGROUP_FAILED = 1533 ER_BINLOG_ROW_LOGGING_FAILED = 1534 ER_BINLOG_ROW_WRONG_TABLE_DEF = 1535 ER_BINLOG_ROW_RBR_TO_SBR = 1536 ER_EVENT_ALREADY_EXISTS = 1537 ER_EVENT_STORE_FAILED = 1538 ER_EVENT_DOES_NOT_EXIST = 1539 ER_EVENT_CANT_ALTER = 1540 ER_EVENT_DROP_FAILED = 1541 ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG = 1542 ER_EVENT_ENDS_BEFORE_STARTS = 1543 ER_EVENT_EXEC_TIME_IN_THE_PAST = 1544 ER_EVENT_OPEN_TABLE_FAILED = 1545 ER_EVENT_NEITHER_M_EXPR_NOR_M_AT = 1546 ER_OBSOLETE_COL_COUNT_DOESNT_MATCH_CORRUPTED = 1547 ER_OBSOLETE_CANNOT_LOAD_FROM_TABLE = 1548 ER_EVENT_CANNOT_DELETE = 1549 ER_EVENT_COMPILE_ERROR = 1550 ER_EVENT_SAME_NAME = 1551 ER_EVENT_DATA_TOO_LONG = 1552 ER_DROP_INDEX_FK = 1553 ER_WARN_DEPRECATED_SYNTAX_WITH_VER = 1554 ER_CANT_WRITE_LOCK_LOG_TABLE = 1555 ER_CANT_LOCK_LOG_TABLE = 1556 ER_FOREIGN_DUPLICATE_KEY_OLD_UNUSED = 1557 ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE = 1558 ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR = 1559 ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1560 ER_NDB_CANT_SWITCH_BINLOG_FORMAT = 1561 ER_PARTITION_NO_TEMPORARY = 1562 ER_PARTITION_CONST_DOMAIN_ERROR = 1563 ER_PARTITION_FUNCTION_IS_NOT_ALLOWED = 1564 ER_DDL_LOG_ERROR = 1565 ER_NULL_IN_VALUES_LESS_THAN = 1566 ER_WRONG_PARTITION_NAME = 1567 ER_CANT_CHANGE_TX_CHARACTERISTICS = 1568 ER_DUP_ENTRY_AUTOINCREMENT_CASE = 1569 ER_EVENT_MODIFY_QUEUE_ERROR = 1570 ER_EVENT_SET_VAR_ERROR = 1571 ER_PARTITION_MERGE_ERROR = 1572 ER_CANT_ACTIVATE_LOG = 1573 ER_RBR_NOT_AVAILABLE = 1574 ER_BASE64_DECODE_ERROR = 1575 ER_EVENT_RECURSION_FORBIDDEN = 1576 ER_EVENTS_DB_ERROR = 1577 ER_ONLY_INTEGERS_ALLOWED = 1578 ER_UNSUPORTED_LOG_ENGINE = 1579 ER_BAD_LOG_STATEMENT = 1580 ER_CANT_RENAME_LOG_TABLE = 1581 ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT = 1582 ER_WRONG_PARAMETERS_TO_NATIVE_FCT = 1583 ER_WRONG_PARAMETERS_TO_STORED_FCT = 1584 ER_NATIVE_FCT_NAME_COLLISION = 1585 ER_DUP_ENTRY_WITH_KEY_NAME = 1586 ER_BINLOG_PURGE_EMFILE = 1587 ER_EVENT_CANNOT_CREATE_IN_THE_PAST = 1588 ER_EVENT_CANNOT_ALTER_IN_THE_PAST = 1589 ER_SLAVE_INCIDENT = 1590 ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT = 1591 ER_BINLOG_UNSAFE_STATEMENT = 1592 ER_SLAVE_FATAL_ERROR = 1593 ER_SLAVE_RELAY_LOG_READ_FAILURE = 1594 ER_SLAVE_RELAY_LOG_WRITE_FAILURE = 1595 ER_SLAVE_CREATE_EVENT_FAILURE = 1596 ER_SLAVE_MASTER_COM_FAILURE = 1597 ER_BINLOG_LOGGING_IMPOSSIBLE = 1598 ER_VIEW_NO_CREATION_CTX = 1599 ER_VIEW_INVALID_CREATION_CTX = 1600 ER_SR_INVALID_CREATION_CTX = 1601 ER_TRG_CORRUPTED_FILE = 1602 ER_TRG_NO_CREATION_CTX = 1603 ER_TRG_INVALID_CREATION_CTX = 1604 ER_EVENT_INVALID_CREATION_CTX = 1605 ER_TRG_CANT_OPEN_TABLE = 1606 ER_CANT_CREATE_SROUTINE = 1607 ER_NEVER_USED = 1608 ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT = 1609 ER_SLAVE_CORRUPT_EVENT = 1610 ER_LOAD_DATA_INVALID_COLUMN = 1611 ER_LOG_PURGE_NO_FILE = 1612 ER_XA_RBTIMEOUT = 1613 ER_XA_RBDEADLOCK = 1614 ER_NEED_REPREPARE = 1615 ER_DELAYED_NOT_SUPPORTED = 1616 WARN_NO_MASTER_INFO = 1617 WARN_OPTION_IGNORED = 1618 ER_PLUGIN_DELETE_BUILTIN = 1619 WARN_PLUGIN_BUSY = 1620 ER_VARIABLE_IS_READONLY = 1621 ER_WARN_ENGINE_TRANSACTION_ROLLBACK = 1622 ER_SLAVE_HEARTBEAT_FAILURE = 1623 ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE = 1624 ER_NDB_REPLICATION_SCHEMA_ERROR = 1625 ER_CONFLICT_FN_PARSE_ERROR = 1626 ER_EXCEPTIONS_WRITE_ERROR = 1627 ER_TOO_LONG_TABLE_COMMENT = 1628 ER_TOO_LONG_FIELD_COMMENT = 1629 ER_FUNC_INEXISTENT_NAME_COLLISION = 1630 ER_DATABASE_NAME = 1631 ER_TABLE_NAME = 1632 ER_PARTITION_NAME = 1633 ER_SUBPARTITION_NAME = 1634 ER_TEMPORARY_NAME = 1635 ER_RENAMED_NAME = 1636 ER_TOO_MANY_CONCURRENT_TRXS = 1637 WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED = 1638 ER_DEBUG_SYNC_TIMEOUT = 1639 ER_DEBUG_SYNC_HIT_LIMIT = 1640 ER_DUP_SIGNAL_SET = 1641 ER_SIGNAL_WARN = 1642 ER_SIGNAL_NOT_FOUND = 1643 ER_SIGNAL_EXCEPTION = 1644 ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER = 1645 ER_SIGNAL_BAD_CONDITION_TYPE = 1646 WARN_COND_ITEM_TRUNCATED = 1647 ER_COND_ITEM_TOO_LONG = 1648 ER_UNKNOWN_LOCALE = 1649 ER_SLAVE_IGNORE_SERVER_IDS = 1650 ER_QUERY_CACHE_DISABLED = 1651 ER_SAME_NAME_PARTITION_FIELD = 1652 ER_PARTITION_COLUMN_LIST_ERROR = 1653 ER_WRONG_TYPE_COLUMN_VALUE_ERROR = 1654 ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR = 1655 ER_MAXVALUE_IN_VALUES_IN = 1656 ER_TOO_MANY_VALUES_ERROR = 1657 ER_ROW_SINGLE_PARTITION_FIELD_ERROR = 1658 ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD = 1659 ER_PARTITION_FIELDS_TOO_LONG = 1660 ER_BINLOG_ROW_ENGINE_AND_STMT_ENGINE = 1661 ER_BINLOG_ROW_MODE_AND_STMT_ENGINE = 1662 ER_BINLOG_UNSAFE_AND_STMT_ENGINE = 1663 ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE = 1664 ER_BINLOG_STMT_MODE_AND_ROW_ENGINE = 1665 ER_BINLOG_ROW_INJECTION_AND_STMT_MODE = 1666 ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1667 ER_BINLOG_UNSAFE_LIMIT = 1668 ER_UNUSED4 = 1669 ER_BINLOG_UNSAFE_SYSTEM_TABLE = 1670 ER_BINLOG_UNSAFE_AUTOINC_COLUMNS = 1671 ER_BINLOG_UNSAFE_UDF = 1672 ER_BINLOG_UNSAFE_SYSTEM_VARIABLE = 1673 ER_BINLOG_UNSAFE_SYSTEM_FUNCTION = 1674 ER_BINLOG_UNSAFE_NONTRANS_AFTER_TRANS = 1675 ER_MESSAGE_AND_STATEMENT = 1676 ER_SLAVE_CONVERSION_FAILED = 1677 ER_SLAVE_CANT_CREATE_CONVERSION = 1678 ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1679 ER_PATH_LENGTH = 1680 ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT = 1681 ER_WRONG_NATIVE_TABLE_STRUCTURE = 1682 ER_WRONG_PERFSCHEMA_USAGE = 1683 ER_WARN_I_S_SKIPPED_TABLE = 1684 ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1685 ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1686 ER_SPATIAL_MUST_HAVE_GEOM_COL = 1687 ER_TOO_LONG_INDEX_COMMENT = 1688 ER_LOCK_ABORTED = 1689 ER_DATA_OUT_OF_RANGE = 1690 ER_WRONG_SPVAR_TYPE_IN_LIMIT = 1691 ER_BINLOG_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1692 ER_BINLOG_UNSAFE_MIXED_STATEMENT = 1693 ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1694 ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1695 ER_FAILED_READ_FROM_PAR_FILE = 1696 ER_VALUES_IS_NOT_INT_TYPE_ERROR = 1697 ER_ACCESS_DENIED_NO_PASSWORD_ERROR = 1698 ER_SET_PASSWORD_AUTH_PLUGIN = 1699 ER_GRANT_PLUGIN_USER_EXISTS = 1700 ER_TRUNCATE_ILLEGAL_FK = 1701 ER_PLUGIN_IS_PERMANENT = 1702 ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN = 1703 ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX = 1704 ER_STMT_CACHE_FULL = 1705 ER_MULTI_UPDATE_KEY_CONFLICT = 1706 ER_TABLE_NEEDS_REBUILD = 1707 WARN_OPTION_BELOW_LIMIT = 1708 ER_INDEX_COLUMN_TOO_LONG = 1709 ER_ERROR_IN_TRIGGER_BODY = 1710 ER_ERROR_IN_UNKNOWN_TRIGGER_BODY = 1711 ER_INDEX_CORRUPT = 1712 ER_UNDO_RECORD_TOO_BIG = 1713 ER_BINLOG_UNSAFE_INSERT_IGNORE_SELECT = 1714 ER_BINLOG_UNSAFE_INSERT_SELECT_UPDATE = 1715 ER_BINLOG_UNSAFE_REPLACE_SELECT = 1716 ER_BINLOG_UNSAFE_CREATE_IGNORE_SELECT = 1717 ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT = 1718 ER_BINLOG_UNSAFE_UPDATE_IGNORE = 1719 ER_PLUGIN_NO_UNINSTALL = 1720 ER_PLUGIN_NO_INSTALL = 1721 ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT = 1722 ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC = 1723 ER_BINLOG_UNSAFE_INSERT_TWO_KEYS = 1724 ER_TABLE_IN_FK_CHECK = 1725 ER_UNSUPPORTED_ENGINE = 1726 ER_BINLOG_UNSAFE_AUTOINC_NOT_FIRST = 1727 ER_CANNOT_LOAD_FROM_TABLE_V2 = 1728 ER_MASTER_DELAY_VALUE_OUT_OF_RANGE = 1729 ER_ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT = 1730 ER_PARTITION_EXCHANGE_DIFFERENT_OPTION = 1731 ER_PARTITION_EXCHANGE_PART_TABLE = 1732 ER_PARTITION_EXCHANGE_TEMP_TABLE = 1733 ER_PARTITION_INSTEAD_OF_SUBPARTITION = 1734 ER_UNKNOWN_PARTITION = 1735 ER_TABLES_DIFFERENT_METADATA = 1736 ER_ROW_DOES_NOT_MATCH_PARTITION = 1737 ER_BINLOG_CACHE_SIZE_GREATER_THAN_MAX = 1738 ER_WARN_INDEX_NOT_APPLICABLE = 1739 ER_PARTITION_EXCHANGE_FOREIGN_KEY = 1740 ER_NO_SUCH_KEY_VALUE = 1741 ER_RPL_INFO_DATA_TOO_LONG = 1742 ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE = 1743 ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE = 1744 ER_BINLOG_STMT_CACHE_SIZE_GREATER_THAN_MAX = 1745 ER_CANT_UPDATE_TABLE_IN_CREATE_TABLE_SELECT = 1746 ER_PARTITION_CLAUSE_ON_NONPARTITIONED = 1747 ER_ROW_DOES_NOT_MATCH_GIVEN_PARTITION_SET = 1748 ER_NO_SUCH_PARTITION__UNUSED = 1749 ER_CHANGE_RPL_INFO_REPOSITORY_FAILURE = 1750 ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_CREATED_TEMP_TABLE = 1751 ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_DROPPED_TEMP_TABLE = 1752 ER_MTS_FEATURE_IS_NOT_SUPPORTED = 1753 ER_MTS_UPDATED_DBS_GREATER_MAX = 1754 ER_MTS_CANT_PARALLEL = 1755 ER_MTS_INCONSISTENT_DATA = 1756 ER_FULLTEXT_NOT_SUPPORTED_WITH_PARTITIONING = 1757 ER_DA_INVALID_CONDITION_NUMBER = 1758 ER_INSECURE_PLAIN_TEXT = 1759 ER_INSECURE_CHANGE_MASTER = 1760 ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO = 1761 ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO = 1762 ER_SQLTHREAD_WITH_SECURE_SLAVE = 1763 ER_TABLE_HAS_NO_FT = 1764 ER_VARIABLE_NOT_SETTABLE_IN_SF_OR_TRIGGER = 1765 ER_VARIABLE_NOT_SETTABLE_IN_TRANSACTION = 1766 ER_GTID_NEXT_IS_NOT_IN_GTID_NEXT_LIST = 1767 ER_CANT_CHANGE_GTID_NEXT_IN_TRANSACTION_WHEN_GTID_NEXT_LIST_IS_NULL = 1768 ER_SET_STATEMENT_CANNOT_INVOKE_FUNCTION = 1769 ER_GTID_NEXT_CANT_BE_AUTOMATIC_IF_GTID_NEXT_LIST_IS_NON_NULL = 1770 ER_SKIPPING_LOGGED_TRANSACTION = 1771 ER_MALFORMED_GTID_SET_SPECIFICATION = 1772 ER_MALFORMED_GTID_SET_ENCODING = 1773 ER_MALFORMED_GTID_SPECIFICATION = 1774 ER_GNO_EXHAUSTED = 1775 ER_BAD_SLAVE_AUTO_POSITION = 1776 ER_AUTO_POSITION_REQUIRES_GTID_MODE_ON = 1777 ER_CANT_DO_IMPLICIT_COMMIT_IN_TRX_WHEN_GTID_NEXT_IS_SET = 1778 ER_GTID_MODE_2_OR_3_REQUIRES_ENFORCE_GTID_CONSISTENCY_ON = 1779 ER_GTID_MODE_REQUIRES_BINLOG = 1780 ER_CANT_SET_GTID_NEXT_TO_GTID_WHEN_GTID_MODE_IS_OFF = 1781 ER_CANT_SET_GTID_NEXT_TO_ANONYMOUS_WHEN_GTID_MODE_IS_ON = 1782 ER_CANT_SET_GTID_NEXT_LIST_TO_NON_NULL_WHEN_GTID_MODE_IS_OFF = 1783 ER_FOUND_GTID_EVENT_WHEN_GTID_MODE_IS_OFF = 1784 ER_GTID_UNSAFE_NON_TRANSACTIONAL_TABLE = 1785 ER_GTID_UNSAFE_CREATE_SELECT = 1786 ER_GTID_UNSAFE_CREATE_DROP_TEMPORARY_TABLE_IN_TRANSACTION = 1787 ER_GTID_MODE_CAN_ONLY_CHANGE_ONE_STEP_AT_A_TIME = 1788 ER_MASTER_HAS_PURGED_REQUIRED_GTIDS = 1789 ER_CANT_SET_GTID_NEXT_WHEN_OWNING_GTID = 1790 ER_UNKNOWN_EXPLAIN_FORMAT = 1791 ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION = 1792 ER_TOO_LONG_TABLE_PARTITION_COMMENT = 1793 ER_SLAVE_CONFIGURATION = 1794 ER_INNODB_FT_LIMIT = 1795 ER_INNODB_NO_FT_TEMP_TABLE = 1796 ER_INNODB_FT_WRONG_DOCID_COLUMN = 1797 ER_INNODB_FT_WRONG_DOCID_INDEX = 1798 ER_INNODB_ONLINE_LOG_TOO_BIG = 1799 ER_UNKNOWN_ALTER_ALGORITHM = 1800 ER_UNKNOWN_ALTER_LOCK = 1801 ER_MTS_CHANGE_MASTER_CANT_RUN_WITH_GAPS = 1802 ER_MTS_RECOVERY_FAILURE = 1803 ER_MTS_RESET_WORKERS = 1804 ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2 = 1805 ER_SLAVE_SILENT_RETRY_TRANSACTION = 1806 ER_DISCARD_FK_CHECKS_RUNNING = 1807 ER_TABLE_SCHEMA_MISMATCH = 1808 ER_TABLE_IN_SYSTEM_TABLESPACE = 1809 ER_IO_READ_ERROR = 1810 ER_IO_WRITE_ERROR = 1811 ER_TABLESPACE_MISSING = 1812 ER_TABLESPACE_EXISTS = 1813 ER_TABLESPACE_DISCARDED = 1814 ER_INTERNAL_ERROR = 1815 ER_INNODB_IMPORT_ERROR = 1816 ER_INNODB_INDEX_CORRUPT = 1817 ER_INVALID_YEAR_COLUMN_LENGTH = 1818 ER_NOT_VALID_PASSWORD = 1819 ER_MUST_CHANGE_PASSWORD = 1820 ER_FK_NO_INDEX_CHILD = 1821 ER_FK_NO_INDEX_PARENT = 1822 ER_FK_FAIL_ADD_SYSTEM = 1823 ER_FK_CANNOT_OPEN_PARENT = 1824 ER_FK_INCORRECT_OPTION = 1825 ER_FK_DUP_NAME = 1826 ER_PASSWORD_FORMAT = 1827 ER_FK_COLUMN_CANNOT_DROP = 1828 ER_FK_COLUMN_CANNOT_DROP_CHILD = 1829 ER_FK_COLUMN_NOT_NULL = 1830 ER_DUP_INDEX = 1831 ER_FK_COLUMN_CANNOT_CHANGE = 1832 ER_FK_COLUMN_CANNOT_CHANGE_CHILD = 1833 ER_UNUSED5 = 1834 ER_MALFORMED_PACKET = 1835 ER_READ_ONLY_MODE = 1836 ER_GTID_NEXT_TYPE_UNDEFINED_GROUP = 1837 ER_VARIABLE_NOT_SETTABLE_IN_SP = 1838 ER_CANT_SET_GTID_PURGED_WHEN_GTID_MODE_IS_OFF = 1839 ER_CANT_SET_GTID_PURGED_WHEN_GTID_EXECUTED_IS_NOT_EMPTY = 1840 ER_CANT_SET_GTID_PURGED_WHEN_OWNED_GTIDS_IS_NOT_EMPTY = 1841 ER_GTID_PURGED_WAS_CHANGED = 1842 ER_GTID_EXECUTED_WAS_CHANGED = 1843 ER_BINLOG_STMT_MODE_AND_NO_REPL_TABLES = 1844 ER_ALTER_OPERATION_NOT_SUPPORTED = 1845 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON = 1846 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COPY = 1847 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_PARTITION = 1848 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME = 1849 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE = 1850 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK = 1851 ER_UNUSED6 = 1852 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOPK = 1853 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_AUTOINC = 1854 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_HIDDEN_FTS = 1855 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS = 1856 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS = 1857 ER_SQL_SLAVE_SKIP_COUNTER_NOT_SETTABLE_IN_GTID_MODE = 1858 ER_DUP_UNKNOWN_IN_INDEX = 1859 ER_IDENT_CAUSES_TOO_LONG_PATH = 1860 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL = 1861 ER_MUST_CHANGE_PASSWORD_LOGIN = 1862 ER_ROW_IN_WRONG_PARTITION = 1863 ER_MTS_EVENT_BIGGER_PENDING_JOBS_SIZE_MAX = 1864 ER_INNODB_NO_FT_USES_PARSER = 1865 ER_BINLOG_LOGICAL_CORRUPTION = 1866 ER_WARN_PURGE_LOG_IN_USE = 1867 ER_WARN_PURGE_LOG_IS_ACTIVE = 1868 ER_AUTO_INCREMENT_CONFLICT = 1869 WARN_ON_BLOCKHOLE_IN_RBR = 1870 ER_SLAVE_MI_INIT_REPOSITORY = 1871 ER_SLAVE_RLI_INIT_REPOSITORY = 1872 ER_ACCESS_DENIED_CHANGE_USER_ERROR = 1873 ER_INNODB_READ_ONLY = 1874 ER_STOP_SLAVE_SQL_THREAD_TIMEOUT = 1875 ER_STOP_SLAVE_IO_THREAD_TIMEOUT = 1876 ER_TABLE_CORRUPT = 1877 ER_TEMP_FILE_WRITE_FAILURE = 1878 ER_INNODB_FT_AUX_NOT_HEX_ID = 1879 ER_OLD_TEMPORALS_UPGRADED = 1880 ER_INNODB_FORCED_RECOVERY = 1881 ER_AES_INVALID_IV = 1882 ER_PLUGIN_CANNOT_BE_UNINSTALLED = 1883 ER_GTID_UNSAFE_BINLOG_SPLITTABLE_STATEMENT_AND_GTID_GROUP = 1884 ER_FILE_CORRUPT = 1885 ER_ERROR_ON_MASTER = 1886 ER_INCONSISTENT_ERROR = 1887 ER_STORAGE_ENGINE_NOT_LOADED = 1888 ER_GET_STACKED_DA_WITHOUT_ACTIVE_HANDLER = 1889 ER_WARN_LEGACY_SYNTAX_CONVERTED = 1890 ER_BINLOG_UNSAFE_FULLTEXT_PLUGIN = 1891 ER_CANNOT_DISCARD_TEMPORARY_TABLE = 1892 ER_FK_DEPTH_EXCEEDED = 1893 ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE_V2 = 1894 ER_WARN_TRIGGER_DOESNT_HAVE_CREATED = 1895 ER_REFERENCED_TRG_DOES_NOT_EXIST = 1896 ER_EXPLAIN_NOT_SUPPORTED = 1897 ER_INVALID_FIELD_SIZE = 1898 ER_MISSING_HA_CREATE_OPTION = 1899 ER_ENGINE_OUT_OF_MEMORY = 1900 ER_PASSWORD_EXPIRE_ANONYMOUS_USER = 1901 ER_SLAVE_SQL_THREAD_MUST_STOP = 1902 ER_NO_FT_MATERIALIZED_SUBQUERY = 1903 ER_INNODB_UNDO_LOG_FULL = 1904 ER_INVALID_ARGUMENT_FOR_LOGARITHM = 1905 ER_SLAVE_IO_THREAD_MUST_STOP = 1906 ER_WARN_OPEN_TEMP_TABLES_MUST_BE_ZERO = 1907 ER_WARN_ONLY_MASTER_LOG_FILE_NO_POS = 1908 ER_QUERY_TIMEOUT = 1909 ER_NON_RO_SELECT_DISABLE_TIMER = 1910 ER_DUP_LIST_ENTRY = 1911 ER_SQL_MODE_NO_EFFECT = 1912 ER_AGGREGATE_ORDER_FOR_UNION = 1913 ER_AGGREGATE_ORDER_NON_AGG_QUERY = 1914 ER_SLAVE_WORKER_STOPPED_PREVIOUS_THD_ERROR = 1915 ER_DONT_SUPPORT_SLAVE_PRESERVE_COMMIT_ORDER = 1916 ER_SERVER_OFFLINE_MODE = 1917 ER_GIS_DIFFERENT_SRIDS = 1918 ER_GIS_UNSUPPORTED_ARGUMENT = 1919 ER_GIS_UNKNOWN_ERROR = 1920 ER_GIS_UNKNOWN_EXCEPTION = 1921 ER_GIS_INVALID_DATA = 1922 ER_BOOST_GEOMETRY_EMPTY_INPUT_EXCEPTION = 1923 ER_BOOST_GEOMETRY_CENTROID_EXCEPTION = 1924 ER_BOOST_GEOMETRY_OVERLAY_INVALID_INPUT_EXCEPTION = 1925 ER_BOOST_GEOMETRY_TURN_INFO_EXCEPTION = 1926 ER_BOOST_GEOMETRY_SELF_INTERSECTION_POINT_EXCEPTION = 1927 ER_BOOST_GEOMETRY_UNKNOWN_EXCEPTION = 1928 ER_STD_BAD_ALLOC_ERROR = 1929 ER_STD_DOMAIN_ERROR = 1930 ER_STD_LENGTH_ERROR = 1931 ER_STD_INVALID_ARGUMENT = 1932 ER_STD_OUT_OF_RANGE_ERROR = 1933 ER_STD_OVERFLOW_ERROR = 1934 ER_STD_RANGE_ERROR = 1935 ER_STD_UNDERFLOW_ERROR = 1936 ER_STD_LOGIC_ERROR = 1937 ER_STD_RUNTIME_ERROR = 1938 ER_STD_UNKNOWN_EXCEPTION = 1939 ER_GIS_DATA_WRONG_ENDIANESS = 1940 ER_CHANGE_MASTER_PASSWORD_LENGTH = 1941 ER_USER_LOCK_WRONG_NAME = 1942 ER_USER_LOCK_DEADLOCK = 1943 ER_REPLACE_INACCESSIBLE_ROWS = 1944 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_GIS = 1945 ER_ILLEGAL_USER_VAR = 1946 ER_GTID_MODE_OFF = 1947 ER_UNSUPPORTED_BY_REPLICATION_THREAD = 1948 ER_INCORRECT_TYPE = 1949 ER_FIELD_IN_ORDER_NOT_SELECT = 1950 ER_AGGREGATE_IN_ORDER_NOT_SELECT = 1951 ER_INVALID_RPL_WILD_TABLE_FILTER_PATTERN = 1952 ER_NET_OK_PACKET_TOO_LARGE = 1953 ER_INVALID_JSON_DATA = 1954 ER_INVALID_GEOJSON_MISSING_MEMBER = 1955 ER_INVALID_GEOJSON_WRONG_TYPE = 1956 ER_INVALID_GEOJSON_UNSPECIFIED = 1957 ER_DIMENSION_UNSUPPORTED = 1958 CR_UNKNOWN_ERROR = 2000 CR_SOCKET_CREATE_ERROR = 2001 CR_CONNECTION_ERROR = 2002 CR_CONN_HOST_ERROR = 2003 CR_IPSOCK_ERROR = 2004 CR_UNKNOWN_HOST = 2005 CR_SERVER_GONE_ERROR = 2006 CR_VERSION_ERROR = 2007 CR_OUT_OF_MEMORY = 2008 CR_WRONG_HOST_INFO = 2009 CR_LOCALHOST_CONNECTION = 2010 CR_TCP_CONNECTION = 2011 CR_SERVER_HANDSHAKE_ERR = 2012 CR_SERVER_LOST = 2013 CR_COMMANDS_OUT_OF_SYNC = 2014 CR_NAMEDPIPE_CONNECTION = 2015 CR_NAMEDPIPEWAIT_ERROR = 2016 CR_NAMEDPIPEOPEN_ERROR = 2017 CR_NAMEDPIPESETSTATE_ERROR = 2018 CR_CANT_READ_CHARSET = 2019 CR_NET_PACKET_TOO_LARGE = 2020 CR_EMBEDDED_CONNECTION = 2021 CR_PROBE_SLAVE_STATUS = 2022 CR_PROBE_SLAVE_HOSTS = 2023 CR_PROBE_SLAVE_CONNECT = 2024 CR_PROBE_MASTER_CONNECT = 2025 CR_SSL_CONNECTION_ERROR = 2026 CR_MALFORMED_PACKET = 2027 CR_WRONG_LICENSE = 2028 CR_NULL_POINTER = 2029 CR_NO_PREPARE_STMT = 2030 CR_PARAMS_NOT_BOUND = 2031 CR_DATA_TRUNCATED = 2032 CR_NO_PARAMETERS_EXISTS = 2033 CR_INVALID_PARAMETER_NO = 2034 CR_INVALID_BUFFER_USE = 2035 CR_UNSUPPORTED_PARAM_TYPE = 2036 CR_SHARED_MEMORY_CONNECTION = 2037 CR_SHARED_MEMORY_CONNECT_REQUEST_ERROR = 2038 CR_SHARED_MEMORY_CONNECT_ANSWER_ERROR = 2039 CR_SHARED_MEMORY_CONNECT_FILE_MAP_ERROR = 2040 CR_SHARED_MEMORY_CONNECT_MAP_ERROR = 2041 CR_SHARED_MEMORY_FILE_MAP_ERROR = 2042 CR_SHARED_MEMORY_MAP_ERROR = 2043 CR_SHARED_MEMORY_EVENT_ERROR = 2044 CR_SHARED_MEMORY_CONNECT_ABANDONED_ERROR = 2045 CR_SHARED_MEMORY_CONNECT_SET_ERROR = 2046 CR_CONN_UNKNOW_PROTOCOL = 2047 CR_INVALID_CONN_HANDLE = 2048 CR_UNUSED_1 = 2049 CR_FETCH_CANCELED = 2050 CR_NO_DATA = 2051 CR_NO_STMT_METADATA = 2052 CR_NO_RESULT_SET = 2053 CR_NOT_IMPLEMENTED = 2054 CR_SERVER_LOST_EXTENDED = 2055 CR_STMT_CLOSED = 2056 CR_NEW_STMT_METADATA = 2057 CR_ALREADY_CONNECTED = 2058 CR_AUTH_PLUGIN_CANNOT_LOAD = 2059 CR_DUPLICATE_CONNECTION_ATTR = 2060 CR_AUTH_PLUGIN_ERR = 2061 # End MySQL Errors mysql-connector-python-2.0.4/lib/mysql/connector/dbapi.py0000644001577100000120000000443212506707765023017 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """ This module implements some constructors and singletons as required by the DB API v2.0 (PEP-249). """ # Python Db API v2 apilevel = '2.0' threadsafety = 1 paramstyle = 'pyformat' import time import datetime from . import constants class _DBAPITypeObject(object): def __init__(self, *values): self.values = values def __eq__(self, other): if other in self.values: return True else: return False def __ne__(self, other): if other in self.values: return False else: return True Date = datetime.date Time = datetime.time Timestamp = datetime.datetime def DateFromTicks(ticks): return Date(*time.localtime(ticks)[:3]) def TimeFromTicks(ticks): return Time(*time.localtime(ticks)[3:6]) def TimestampFromTicks(ticks): return Timestamp(*time.localtime(ticks)[:6]) Binary = bytes STRING = _DBAPITypeObject(*constants.FieldType.get_string_types()) BINARY = _DBAPITypeObject(*constants.FieldType.get_binary_types()) NUMBER = _DBAPITypeObject(*constants.FieldType.get_number_types()) DATETIME = _DBAPITypeObject(*constants.FieldType.get_timestamp_types()) ROWID = _DBAPITypeObject() mysql-connector-python-2.0.4/lib/mysql/connector/authentication.py0000644001577100000120000001451512506707765024762 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Implementing support for MySQL Authentication Plugins""" from hashlib import sha1 import struct from . import errors from .catch23 import PY2, isstr class BaseAuthPlugin(object): """Base class for authentication plugins Classes inheriting from BaseAuthPlugin should implement the method prepare_password(). When instantiating, auth_data argument is required. The username, password and database are optional. The ssl_enabled argument can be used to tell the plugin whether SSL is active or not. The method auth_response() method is used to retrieve the password which was prepared by prepare_password(). """ requires_ssl = False plugin_name = '' def __init__(self, auth_data, username=None, password=None, database=None, ssl_enabled=False): """Initialization""" self._auth_data = auth_data self._username = username self._password = password self._database = database self._ssl_enabled = ssl_enabled def prepare_password(self): """Prepares and returns password to be send to MySQL This method needs to be implemented by classes inheriting from this class. It is used by the auth_response() method. Raises NotImplementedError. """ raise NotImplementedError def auth_response(self): """Returns the prepared password to send to MySQL Raises InterfaceError on errors. For example, when SSL is required by not enabled. Returns str """ if self.requires_ssl and not self._ssl_enabled: raise errors.InterfaceError("{name} requires SSL".format( name=self.plugin_name)) return self.prepare_password() class MySQLNativePasswordAuthPlugin(BaseAuthPlugin): """Class implementing the MySQL Native Password authentication plugin""" requires_ssl = False plugin_name = 'mysql_native_password' def prepare_password(self): """Prepares and returns password as native MySQL 4.1+ password""" if not self._auth_data: raise errors.InterfaceError("Missing authentication data (seed)") if not self._password: return b'\x00' password = self._password if isstr(self._password): password = self._password.encode('utf-8') else: password = self._password if PY2: password = buffer(password) # pylint: disable=E0602 try: auth_data = buffer(self._auth_data) # pylint: disable=E0602 except TypeError: raise errors.InterfaceError("Authentication data incorrect") else: password = password auth_data = self._auth_data hash4 = None try: hash1 = sha1(password).digest() hash2 = sha1(hash1).digest() hash3 = sha1(auth_data + hash2).digest() if PY2: xored = [ord(h1) ^ ord(h3) for (h1, h3) in zip(hash1, hash3)] else: xored = [h1 ^ h3 for (h1, h3) in zip(hash1, hash3)] hash4 = struct.pack('20B', *xored) except Exception as exc: raise errors.InterfaceError( "Failed scrambling password; {0}".format(exc)) return hash4 class MySQLClearPasswordAuthPlugin(BaseAuthPlugin): """Class implementing the MySQL Clear Password authentication plugin""" requires_ssl = True plugin_name = 'mysql_clear_password' def prepare_password(self): """Returns password as as clear text""" if not self._password: return b'\x00' password = self._password if PY2: if isinstance(password, unicode): # pylint: disable=E0602 password = password.encode('utf8') elif isinstance(password, str): password = password.encode('utf8') return password + b'\x00' class MySQLSHA256PasswordAuthPlugin(BaseAuthPlugin): """Class implementing the MySQL SHA256 authentication plugin Note that encrypting using RSA is not supported since the Python Standard Library does not provide this OpenSSL functionality. """ requires_ssl = True plugin_name = 'sha256_password' def prepare_password(self): """Returns password as as clear text""" if not self._password: return b'\x00' password = self._password if PY2: if isinstance(password, unicode): # pylint: disable=E0602 password = password.encode('utf8') elif isinstance(password, str): password = password.encode('utf8') return password + b'\x00' def get_auth_plugin(plugin_name): """Return authentication class based on plugin name This function returns the class for the authentication plugin plugin_name. The returned class is a subclass of BaseAuthPlugin. Raises errors.NotSupportedError when plugin_name is not supported. Returns subclass of BaseAuthPlugin. """ for authclass in BaseAuthPlugin.__subclasses__(): # pylint: disable=E1101 if authclass.plugin_name == plugin_name: return authclass raise errors.NotSupportedError( "Authentication plugin '{0}' is not supported".format(plugin_name)) mysql-connector-python-2.0.4/lib/mysql/connector/fabric/0000755001577100000120000000000012506713751022601 5ustar pb2userwheelmysql-connector-python-2.0.4/lib/mysql/connector/fabric/connection.py0000644001577100000120000013302012506707765025321 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Implementing communication with MySQL Fabric""" import sys import datetime import time import uuid from base64 import b16decode from bisect import bisect from hashlib import md5 import logging import socket import collections # pylint: disable=F0401,E0611 try: from xmlrpclib import Fault, ServerProxy, Transport import urllib2 from httplib import BadStatusLine except ImportError: # Python v3 from xmlrpc.client import Fault, ServerProxy, Transport import urllib.request as urllib2 from http.client import BadStatusLine if sys.version_info[0] == 2: try: from httplib import HTTPSConnection except ImportError: HAVE_SSL = False else: HAVE_SSL = True else: try: from http.client import HTTPSConnection except ImportError: HAVE_SSL = False else: HAVE_SSL = True # pylint: enable=F0401,E0611 import mysql.connector from ..pooling import MySQLConnectionPool from ..errors import ( Error, InterfaceError, NotSupportedError, MySQLFabricError, InternalError, DatabaseError ) from ..cursor import ( MySQLCursor, MySQLCursorBuffered, MySQLCursorRaw, MySQLCursorBufferedRaw ) from .. import errorcode from . import FabricMySQLServer, FabricShard from .caching import FabricCache from .balancing import WeightedRoundRobin from .. import version from ..catch23 import PY2, isunicode, UNICODE_TYPES RESET_CACHE_ON_ERROR = ( errorcode.CR_SERVER_LOST, errorcode.ER_OPTION_PREVENTS_STATEMENT, ) # Errors to be reported to Fabric REPORT_ERRORS = ( errorcode.CR_SERVER_LOST, errorcode.CR_SERVER_GONE_ERROR, errorcode.CR_CONN_HOST_ERROR, errorcode.CR_CONNECTION_ERROR, errorcode.CR_IPSOCK_ERROR, ) REPORT_ERRORS_EXTRA = [] MYSQL_FABRIC_PORT = 32274 FABRICS = {} # For attempting to connect with Fabric _CNX_ATTEMPT_DELAY = 1 _CNX_ATTEMPT_MAX = 3 _GETCNX_ATTEMPT_DELAY = 1 _GETCNX_ATTEMPT_MAX = 3 MODE_READONLY = 1 MODE_WRITEONLY = 2 MODE_READWRITE = 3 STATUS_FAULTY = 0 STATUS_SPARE = 1 STATUS_SECONDARY = 2 STATUS_PRIMARY = 3 SCOPE_GLOBAL = 'GLOBAL' SCOPE_LOCAL = 'LOCAL' _SERVER_STATUS_FAULTY = 'FAULTY' _CNX_PROPERTIES = { # name: ((valid_types), description, default) 'group': ((str,), "Name of group of servers", None), 'key': (tuple([int, str, datetime.datetime, datetime.date] + list(UNICODE_TYPES)), "Sharding key", None), 'tables': ((tuple, list), "List of tables in query", None), 'mode': ((int,), "Read-Only, Write-Only or Read-Write", MODE_READWRITE), 'shard': ((str,), "Identity of the shard for direct connection", None), 'mapping': ((str,), "", None), 'scope': ((str,), "GLOBAL for accessing Global Group, or LOCAL", SCOPE_LOCAL), 'attempts': ((int,), "Attempts for getting connection", _GETCNX_ATTEMPT_MAX), 'attempt_delay': ((int,), "Seconds to wait between each attempt", _GETCNX_ATTEMPT_DELAY), } _LOGGER = logging.getLogger('myconnpy-fabric') class FabricResponse(object): """Class used to parse a response got from Fabric. """ SUPPORTED_VERSION = 1 def __init__(self, data): """Initialize the FabricResponse object """ (format_version, fabric_uuid_str, ttl, error, rows) = data if error: raise InterfaceError(error) if format_version != FabricResponse.SUPPORTED_VERSION: raise InterfaceError( "Supported protocol has version {sversion}. Got a response " "from MySQL Fabric with version {gversion}.".format( sversion=FabricResponse.SUPPORTED_VERSION, gversion=format_version) ) self.format_version = format_version self.fabric_uuid_str = fabric_uuid_str self.ttl = ttl self.coded_rows = rows class FabricSet(FabricResponse): """Iterator to navigate through the result set returned from Fabric """ def __init__(self, data): """Initialize the FabricSet object. """ super(FabricSet, self).__init__(data) assert len(self.coded_rows) == 1 self.__names = self.coded_rows[0]['info']['names'] self.__rows = self.coded_rows[0]['rows'] assert all(len(self.__names) == len(row) for row in self.__rows) or \ len(self.__rows) == 0 self.__result = collections.namedtuple('ResultSet', self.__names) def rowcount(self): """The number of rows in the result set. """ return len(self.__rows) def rows(self): """Iterate over the rows of the result set. Each row is a named tuple. """ for row in self.__rows: yield self.__result(*row) def row(self, index): """Indexing method for a row. Each row is a named tuple. """ return self.__result(*self.__rows[index]) def extra_failure_report(error_codes): """Add MySQL error to be reported to Fabric This function adds error_codes to the error list to be reported to Fabric. To reset the custom error reporting list, pass None or empty list. The error_codes argument can be either a MySQL error code defined in the errorcode module, or list of error codes. Raises AttributeError when code is not an int. """ global REPORT_ERRORS_EXTRA # pylint: disable=W0603 if not error_codes: REPORT_ERRORS_EXTRA = [] if not isinstance(error_codes, (list, tuple)): error_codes = [error_codes] for code in error_codes: if not isinstance(code, int) or not (code >= 1000 and code < 3000): raise AttributeError("Unknown or invalid error code.") REPORT_ERRORS_EXTRA.append(code) def _fabric_xmlrpc_uri(host, port): """Create an XMLRPC URI for connecting to Fabric This method will create a URI using the host and TCP/IP port suitable for connecting to a MySQL Fabric instance. Returns a URI. """ return 'http://{host}:{port}'.format(host=host, port=port) def _fabric_server_uuid(host, port): """Create a UUID using host and port""" return uuid.uuid3(uuid.NAMESPACE_URL, _fabric_xmlrpc_uri(host, port)) def _validate_ssl_args(ssl_ca, ssl_key, ssl_cert): """Validate the SSL argument. Raises AttributeError is required argument is not set. Returns dict or None. """ if not HAVE_SSL: raise InterfaceError("Python does not support SSL") if any([ssl_ca, ssl_key, ssl_cert]): if not ssl_ca: raise AttributeError("Missing ssl_ca argument.") if (ssl_key or ssl_cert) and not (ssl_key and ssl_cert): raise AttributeError( "ssl_key and ssl_cert need to be both " "specified, or neither." ) return { 'ca': ssl_ca, 'key': ssl_key, 'cert': ssl_cert, } return None if HAVE_SSL: class FabricHTTPSHandler(urllib2.HTTPSHandler): """Class handling HTTPS connections""" def __init__(self, ssl_config): #pylint: disable=E1002 """Initialize""" if PY2: urllib2.HTTPSHandler.__init__(self) else: super().__init__() # pylint: disable=W0104 self._ssl_config = ssl_config def https_open(self, req): """Open HTTPS connection""" return self.do_open(self.get_https_connection, req) def get_https_connection(self, host, timeout=300): """Returns a HTTPSConnection""" return HTTPSConnection( host, key_file=self._ssl_config['key'], cert_file=self._ssl_config['cert'] ) class FabricTransport(Transport): """Custom XMLRPC Transport for Fabric""" user_agent = 'MySQL Connector Python/{0}'.format(version.VERSION_TEXT) def __init__(self, username, password, #pylint: disable=E1002 verbose=0, use_datetime=False, https_handler=None): """Initialize""" if PY2: Transport.__init__(self, use_datetime=False) else: super().__init__(use_datetime=False) self._username = username self._password = password self._use_datetime = use_datetime self.verbose = verbose self._username = username self._password = password self._handlers = [] if self._username and self._password: self._passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm() self._auth_handler = urllib2.HTTPDigestAuthHandler(self._passmgr) else: self._auth_handler = None self._passmgr = None if https_handler: self._handlers.append(https_handler) self._scheme = 'https' else: self._scheme = 'http' if self._auth_handler: self._handlers.append(self._auth_handler) def request(self, host, handler, request_body, verbose=0): """Send XMLRPC request""" uri = '{scheme}://{host}{handler}'.format(scheme=self._scheme, host=host, handler=handler) if self._passmgr: self._passmgr.add_password(None, uri, self._username, self._password) if self.verbose: _LOGGER.debug("FabricTransport: {0}".format(uri)) opener = urllib2.build_opener(*self._handlers) headers = { 'Content-Type': 'text/xml', 'User-Agent': self.user_agent, } req = urllib2.Request(uri, request_body, headers=headers) try: return self.parse_response(opener.open(req)) except (urllib2.URLError, urllib2.HTTPError) as exc: try: code = -1 if exc.code == 400: reason = 'Permission denied' code = exc.code else: reason = exc.reason msg = "{reason} ({code})".format(reason=reason, code=code) except AttributeError: if 'SSL' in str(exc): msg = "SSL error" else: msg = str(exc) raise InterfaceError("Connection with Fabric failed: " + msg) except BadStatusLine: raise InterfaceError("Connection with Fabric failed: check SSL") class Fabric(object): """Class managing MySQL Fabric instances""" def __init__(self, host, username=None, password=None, port=MYSQL_FABRIC_PORT, connect_attempts=_CNX_ATTEMPT_MAX, connect_delay=_CNX_ATTEMPT_DELAY, report_errors=False, ssl_ca=None, ssl_key=None, ssl_cert=None, user=None): """Initialize""" self._fabric_instances = {} self._fabric_uuid = None self._ttl = 1 * 60 # one minute by default self._version_token = None self._connect_attempts = connect_attempts self._connect_delay = connect_delay self._cache = FabricCache() self._group_balancers = {} self._init_host = host self._init_port = port self._ssl = _validate_ssl_args(ssl_ca, ssl_key, ssl_cert) self._report_errors = report_errors if user and username: raise ValueError("can not specify both user and username") self._username = user or username self._password = password @property def username(self): """Return username used to authenticate with Fabric""" return self._username @property def password(self): """Return password used to authenticate with Fabric""" return self._password @property def ssl_config(self): """Return the SSL configuration""" return self._ssl def seed(self, host=None, port=None): """Get MySQL Fabric Instances This method uses host and port to connect to a MySQL Fabric server and get all the instances managing the same metadata. Raises InterfaceError on errors. """ host = host or self._init_host port = port or self._init_port fabinst = FabricConnection(self, host, port, connect_attempts=self._connect_attempts, connect_delay=self._connect_delay) fabinst.connect() fabric_uuid, fabric_version, ttl, fabrics = self.get_fabric_servers( fabinst) if not fabrics: # Raise, something went wrong. raise InterfaceError("Failed getting list of Fabric servers") if self._version_token == fabric_version: return _LOGGER.info( "Loading Fabric configuration version {version}".format( version=fabric_version)) self._fabric_uuid = fabric_uuid self._version_token = fabric_version if ttl > 0: self._ttl = ttl # Update the Fabric servers for fabric in fabrics: inst = FabricConnection(self, fabric['host'], fabric['port'], connect_attempts=self._connect_attempts, connect_delay=self._connect_delay) inst_uuid = inst.uuid if inst_uuid not in self._fabric_instances: inst.connect() self._fabric_instances[inst_uuid] = inst _LOGGER.debug( "Added new Fabric server {host}:{port}".format( host=inst.host, port=inst.port)) def reset_cache(self, group=None): """Reset cached information This method destroys all cached information. """ if group: _LOGGER.debug("Resetting cache for group '{group}'".format( group=group)) self.get_group_servers(group, use_cache=False) else: _LOGGER.debug("Resetting cache") self._cache = FabricCache() def get_instance(self): """Get a MySQL Fabric Instance This method will get the next available MySQL Fabric Instance. Raises InterfaceError when no instance is available or connected. """ nxt = 0 errmsg = "No MySQL Fabric instance available" if not self._fabric_instances: raise InterfaceError(errmsg + " (not seeded?)") if PY2: instance_list = self._fabric_instances.keys() inst = self._fabric_instances[instance_list[nxt]] else: inst = self._fabric_instances[list(self._fabric_instances)[nxt]] if not inst.is_connected: inst.connect() return inst def report_failure(self, server_uuid, errno): """Report failure to Fabric This method sets the status of a MySQL server identified by server_uuid. """ if not self._report_errors: return errno = int(errno) current_host = socket.getfqdn() if errno in REPORT_ERRORS or errno in REPORT_ERRORS_EXTRA: _LOGGER.debug("Reporting error %d of server %s", errno, server_uuid) inst = self.get_instance() try: data = inst.proxy.threat.report_failure( server_uuid, current_host, errno ) FabricResponse(data) except (Fault, socket.error) as exc: _LOGGER.debug("Failed reporting server to Fabric (%s)", str(exc)) # Not requiring further action def get_fabric_servers(self, fabric_cnx=None): """Get all MySQL Fabric instances This method looks up the other MySQL Fabric instances which uses the same metadata. The returned list contains dictionaries with connection information such ass host and port. For example: [ {'host': 'fabric_prod_1.example.com', 'port': 32274 }, {'host': 'fabric_prod_2.example.com', 'port': 32274 }, ] Returns a list of dictionaries """ inst = fabric_cnx or self.get_instance() result = [] err_msg = "Looking up Fabric servers failed using {host}:{port}: {err}" try: data = inst.proxy.dump.fabric_nodes('protocol.xmlrpc') fset = FabricSet(data) for row in fset.rows(): result.append({'host': row.host, 'port': row.port}) except (Fault, socket.error) as exc: msg = err_msg.format(err=str(exc), host=inst.host, port=inst.port) raise InterfaceError(msg) except (TypeError, AttributeError) as exc: msg = err_msg.format( err="No Fabric server available ({0})".format(exc), host=inst.host, port=inst.port) raise InterfaceError(msg) try: fabric_uuid = uuid.UUID(fset.fabric_uuid_str) except TypeError: fabric_uuid = uuid.uuid4() fabric_version = 0 return fabric_uuid, fabric_version, fset.ttl, result def get_group_servers(self, group, use_cache=True): """Get all MySQL servers in a group This method returns information about all MySQL part of the given high-availability group. When use_cache is set to True, the cached information will be used. Raises InterfaceError on errors. Returns list of FabricMySQLServer objects. """ # Get group information from cache if use_cache: entry = self._cache.group_search(group) if entry: # Cache group information return entry.servers inst = self.get_instance() result = [] try: data = inst.proxy.dump.servers(self._version_token, group) fset = FabricSet(data) except (Fault, socket.error) as exc: msg = ("Looking up MySQL servers failed for group " "{group}: {error}").format(error=str(exc), group=group) raise InterfaceError(msg) weights = [] for row in fset.rows(): # We make sure, when using local groups, we skip the global group if row.group_id == group: mysqlserver = FabricMySQLServer( row.server_uuid, row.group_id, row.host, row.port, row.mode, row.status, row.weight ) result.append(mysqlserver) if mysqlserver.status == STATUS_SECONDARY: weights.append((mysqlserver.uuid, mysqlserver.weight)) self._cache.cache_group(group, result) if weights: self._group_balancers[group] = WeightedRoundRobin(*weights) return result def get_group_server(self, group, mode=None, status=None): """Get a MySQL server from a group The method uses MySQL Fabric to get the correct MySQL server for the specified group. You can specify mode or status, but not both. The mode argument will decide whether the primary or a secondary server is returned. When no secondary server is available, the primary is returned. Status is used to force getting either a primary or a secondary. The returned tuple contains host, port and uuid. Raises InterfaceError on errors; ValueError when both mode and status are given. Returns a FabricMySQLServer object. """ if mode and status: raise ValueError( "Either mode or status must be given, not both") errmsg = "No MySQL server available for group '{group}'" servers = self.get_group_servers(group, use_cache=True) if not servers: raise InterfaceError(errmsg.format(group=group)) # Get the Master and return list (host, port, UUID) primary = None secondary = [] for server in servers: if server.status == STATUS_SECONDARY: secondary.append(server) elif server.status == STATUS_PRIMARY: primary = server if mode in (MODE_WRITEONLY, MODE_READWRITE) or status == STATUS_PRIMARY: if not primary: self.reset_cache(group=group) raise InterfaceError((errmsg + ' {query}={value}').format( query='status' if status else 'mode', group=group, value=status or mode)) return primary # Return primary if no secondary is available if not secondary and primary: return primary elif group in self._group_balancers: next_secondary = self._group_balancers[group].get_next()[0] for mysqlserver in secondary: if next_secondary == mysqlserver.uuid: return mysqlserver self.reset_cache(group=group) raise InterfaceError(errmsg.format(group=group, mode=mode)) def get_sharding_information(self, tables=None, database=None): """Get and cache the sharding information for given tables This method is fetching sharding information from MySQL Fabric and caches the result. The tables argument must be sequence of sequences contain the name of the database and table. If no database is given, the value for the database argument will be used. Examples: tables = [('salary',), ('employees',)] get_sharding_information(tables, database='employees') tables = [('salary', 'employees'), ('employees', employees)] get_sharding_information(tables) Raises InterfaceError on errors; ValueError when something is wrong with the tables argument. """ if not isinstance(tables, (list, tuple)): raise ValueError("tables should be a sequence") patterns = [] for table in tables: if not isinstance(table, (list, tuple)) and not database: raise ValueError("No database specified for table {0}".format( table)) if isinstance(table, (list, tuple)): dbase = table[1] tbl = table[0] else: dbase = database tbl = table patterns.append("{0}.{1}".format(dbase, tbl)) inst = self.get_instance() try: data = inst.proxy.dump.sharding_information( self._version_token, ','.join(patterns) ) fset = FabricSet(data) except (Fault, socket.error) as exc: msg = "Looking up sharding information failed : {error}".format( error=str(exc)) raise InterfaceError(msg) for row in fset.rows(): self._cache.sharding_cache_table( FabricShard(row.schema_name, row.table_name, row.column_name, row.lower_bound, row.shard_id, row.type_name, row.group_id, row.global_group) ) def get_shard_server(self, tables, key, scope=SCOPE_LOCAL, mode=None): """Get MySQL server information for a particular shard Raises DatabaseError when the table is unknown or when tables are not on the same shard. ValueError is raised when there is a problem with the methods arguments. InterfaceError is raised for other errors. """ if not isinstance(tables, (list, tuple)): raise ValueError("tables should be a sequence") groups = [] for dbobj in tables: try: database, table = dbobj.split('.') except ValueError: raise ValueError( "tables should be given as ., " "was {0}".format(dbobj)) entry = self._cache.sharding_search(database, table) if not entry: self.get_sharding_information((table,), database) entry = self._cache.sharding_search(database, table) if not entry: raise DatabaseError( errno=errorcode.ER_BAD_TABLE_ERROR, msg="Unknown table '{database}.{table}'".format( database=database, table=table)) if scope == 'GLOBAL': return self.get_group_server(entry.global_group, mode=mode) if entry.shard_type == 'RANGE': try: range_key = int(key) except ValueError: raise ValueError("Key must be an integer for RANGE") partitions = entry.keys index = partitions[bisect(partitions, range_key) - 1] partition = entry.partitioning[index] elif entry.shard_type == 'RANGE_DATETIME': if not isinstance(key, (datetime.date, datetime.datetime)): raise ValueError( "Key must be datetime.date or datetime.datetime for " "RANGE_DATETIME") index = None for partkey in entry.keys_reversed: if key >= partkey: index = partkey break try: partition = entry.partitioning[index] except KeyError: raise ValueError("Key invalid; was '{0}'".format(key)) elif entry.shard_type == 'RANGE_STRING': if not isunicode(key): raise ValueError("Key must be a unicode value") index = None for partkey in entry.keys_reversed: if key >= partkey: index = partkey break try: partition = entry.partitioning[index] except KeyError: raise ValueError("Key invalid; was '{0}'".format(key)) elif entry.shard_type == 'HASH': md5key = md5(str(key)) index = entry.keys_reversed[-1] for partkey in entry.keys_reversed: if md5key.digest() >= b16decode(partkey): index = partkey break partition = entry.partitioning[index] else: raise InterfaceError( "Unsupported sharding type {0}".format(entry.shard_type)) groups.append(partition['group']) if not all(group == groups[0] for group in groups): raise DatabaseError( "Tables are located in different shards.") return self.get_group_server(groups[0], mode=mode) def execute(self, group, command, *args, **kwargs): """Execute a Fabric command from given group This method will execute the given Fabric command from the given group using the given arguments. It returns an instance of FabricSet. Raises ValueError when group.command is not valid and raises InterfaceError when an error occurs while executing. Returns FabricSet. """ inst = self.get_instance() try: grp = getattr(inst.proxy, group) cmd = getattr(grp, command) except AttributeError as exc: raise ValueError("{group}.{command} not available ({err})".format( group=group, command=command, err=str(exc))) fab_set = None try: data = cmd(*args, **kwargs) fab_set = FabricSet(data) except (Fault, socket.error, InterfaceError) as exc: msg = "Executing {group}.{command} failed: {error}".format( group=group, command=command, error=str(exc)) raise InterfaceError(msg) return fab_set class FabricConnection(object): """Class holding a connection to a MySQL Fabric server""" def __init__(self, fabric, host, port=MYSQL_FABRIC_PORT, connect_attempts=_CNX_ATTEMPT_MAX, connect_delay=_CNX_ATTEMPT_DELAY): """Initialize""" if not isinstance(fabric, Fabric): raise ValueError("fabric must be instance of class Fabric") self._fabric = fabric self._host = host self._port = port self._proxy = None self._connect_attempts = connect_attempts self._connect_delay = connect_delay @property def host(self): """Returns server IP or name of current Fabric connection""" return self._host @property def port(self): """Returns TCP/IP port of current Fabric connection""" return self._port @property def uri(self): """Returns the XMLRPC URI for current Fabric connection""" return _fabric_xmlrpc_uri(self._host, self._port) @property def uuid(self): """Returns UUID of the Fabric server we are connected with""" return _fabric_server_uuid(self._host, self._port) @property def proxy(self): """Returns the XMLRPC Proxy of current Fabric connection""" return self._proxy def _xmlrpc_get_proxy(self): """Return the XMLRPC server proxy instance to MySQL Fabric This method tries to get a valid connection to a MySQL Fabric server. Returns a XMLRPC ServerProxy instance. """ if self.is_connected: return self._proxy attempts = self._connect_attempts delay = self._connect_delay proxy = None counter = 0 while counter != attempts: counter += 1 try: if self._fabric.ssl_config: if not HAVE_SSL: raise InterfaceError("Python does not support SSL") https_handler = FabricHTTPSHandler(self._fabric.ssl_config) else: https_handler = None transport = FabricTransport(self._fabric.username, self._fabric.password, verbose=0, https_handler=https_handler) proxy = ServerProxy(self.uri, transport=transport, verbose=0) proxy._some_nonexisting_method() # pylint: disable=W0212 except Fault: # We are actually connected return proxy except socket.error as exc: if counter == attempts: raise InterfaceError( "Connection to MySQL Fabric failed ({0})".format(exc)) _LOGGER.debug( "Retrying {host}:{port}, attempts {counter}".format( host=self.host, port=self.port, counter=counter)) if delay > 0: time.sleep(delay) def connect(self): """Connect with MySQL Fabric""" self._proxy = self._xmlrpc_get_proxy() @property def is_connected(self): """Check whether connection with Fabric is valid Return True if we can still interact with the Fabric server; False if Not. Returns True or False. """ try: self._proxy._some_nonexisting_method() # pylint: disable=W0212 except Fault: return True except (TypeError, AttributeError): return False else: return False def __repr__(self): return "{class_}(host={host}, port={port})".format( class_=self.__class__, host=self._host, port=self._port, ) class MySQLFabricConnection(object): """Connection to a MySQL server through MySQL Fabric""" def __init__(self, **kwargs): """Initialize""" self._mysql_cnx = None self._fabric = None self._fabric_mysql_server = None self._mysql_config = None self._cnx_properties = {} self.reset_properties() # Validity of fabric-argument is checked in config()-method if 'fabric' not in kwargs: raise ValueError("Configuration parameters for Fabric missing") if kwargs: self.store_config(**kwargs) def __getattr__(self, attr): """Return the return value of the MySQLConnection instance""" if attr.startswith('cmd_'): raise NotSupportedError( "Calling {attr} is not supported for connections managed by " "MySQL Fabric.".format(attr=attr)) return getattr(self._mysql_cnx, attr) @property def fabric_uuid(self): """Returns the Fabric UUID of the MySQL server""" if self._fabric_mysql_server: return self._fabric_mysql_server.uuid return None @property def properties(self): """Returns connection properties""" return self._cnx_properties def reset_cache(self, group=None): """Reset cache for this connection's group""" if not group and self._fabric_mysql_server: group = self._fabric_mysql_server.group self._fabric.reset_cache(group=group) def is_connected(self): """Check whether we are connected with the MySQL server Returns True or False """ return self._mysql_cnx is not None def reset_properties(self): """Resets the connection properties This method can be called to reset the connection properties to their default values. """ self._cnx_properties = {} for key, attr in _CNX_PROPERTIES.items(): self._cnx_properties[key] = attr[2] def set_property(self, **properties): """Set one or more connection properties Arguments to the set_property() method will be used as properties. They are validated against the _CNX_PROPERTIES constant. Raise ValueError in case an invalid property is being set. TypeError is raised when the type of the value is not correct. To unset a property, set it to None. """ try: self.close() except Error: # We tried, but it's OK when we fail. pass props = self._cnx_properties for name, value in properties.items(): if name not in _CNX_PROPERTIES: raise ValueError( "Invalid property connection {0}".format(name)) elif value and not isinstance(value, _CNX_PROPERTIES[name][0]): valid_types_str = ' or '.join( [atype.__name__ for atype in _CNX_PROPERTIES[name][0]]) raise TypeError( "{name} is not valid, excepted {typename}".format( name=name, typename=valid_types_str)) if (name == 'group' and value and (props['key'] or props['tables'])): raise ValueError( "'group' property can not be set when 'key' or " "'tables' are set") elif name in ('key', 'tables') and value and props['group']: raise ValueError( "'key' and 'tables' property can not be " "set together with 'group'") elif name == 'scope' and value not in (SCOPE_LOCAL, SCOPE_GLOBAL): raise ValueError("Invalid value for 'scope'") elif name == 'mode' and value not in ( MODE_READWRITE, MODE_READONLY): raise ValueError("Invalid value for 'mode'") if value is None: # Set the default props[name] = _CNX_PROPERTIES[name][2] else: props[name] = value def _configure_fabric(self, config): """Configure the Fabric connection The config argument can be either a dictionary containing the necessary information to setup the connection. Or config can be an instance of Fabric. """ if isinstance(config, Fabric): self._fabric = config else: required_keys = ['host'] for required_key in required_keys: if required_key not in config: raise ValueError( "Missing configuration parameter '{parameter}' " "for fabric".format(parameter=required_key)) host = config['host'] port = config.get('port', MYSQL_FABRIC_PORT) server_uuid = _fabric_server_uuid(host, port) try: self._fabric = FABRICS[server_uuid] except KeyError: _LOGGER.debug("New Fabric connection") self._fabric = Fabric(**config) self._fabric.seed() # Cache the new connection FABRICS[server_uuid] = self._fabric def store_config(self, **kwargs): """Store configuration of MySQL connections to use with Fabric The configuration found in the dictionary kwargs is used when instanciating a MySQLConnection object. The host and port entries are used to connect to MySQL Fabric. Raises ValueError when the Fabric configuration parameter is not correct or missing; AttributeError is raised when when a paramater is not valid. """ config = kwargs.copy() # Configure the Fabric connection if 'fabric' in config: self._configure_fabric(config['fabric']) del config['fabric'] if 'unix_socket' in config: _LOGGER.warning("MySQL Fabric does not use UNIX sockets.") config['unix_socket'] = None # Try to use the configuration test_config = config.copy() if 'pool_name' in test_config: del test_config['pool_name'] if 'pool_size' in test_config: del test_config['pool_size'] if 'pool_reset_session' in test_config: del test_config['pool_reset_session'] try: pool = MySQLConnectionPool(pool_name=str(uuid.uuid4())) pool.set_config(**test_config) except AttributeError as err: raise AttributeError( "Connection configuration not valid: {0}".format(err)) self._mysql_config = config def _connect(self): """Get a MySQL server based on properties and connect This method gets a MySQL server from MySQL Fabric using already properties set using the set_property() method. You can specify how many times and the delay between trying using attempts and attempt_delay. Raises ValueError when there are problems with arguments or properties; InterfaceError on connectivity errors. """ if self.is_connected(): return props = self._cnx_properties attempts = props['attempts'] attempt_delay = props['attempt_delay'] dbconfig = self._mysql_config.copy() counter = 0 while counter != attempts: counter += 1 try: group = None if props['tables']: if props['scope'] == 'LOCAL' and not props['key']: raise ValueError( "Scope 'LOCAL' needs key property to be set") mysqlserver = self._fabric.get_shard_server( props['tables'], props['key'], scope=props['scope'], mode=props['mode']) elif props['group']: group = props['group'] mysqlserver = self._fabric.get_group_server( group, mode=props['mode']) else: raise ValueError( "Missing group or key and tables properties") except InterfaceError as exc: _LOGGER.debug( "Trying to get MySQL server (attempt {0}; {1})".format( counter, exc)) if counter == attempts: raise InterfaceError("Error getting connection: {0}".format( exc)) if attempt_delay > 0: _LOGGER.debug("Waiting {0}".format(attempt_delay)) time.sleep(attempt_delay) continue # Make sure we do not change the stored configuration dbconfig['host'] = mysqlserver.host dbconfig['port'] = mysqlserver.port try: self._mysql_cnx = mysql.connector.connect(**dbconfig) except Error as exc: if counter == attempts: self.reset_cache(mysqlserver.group) self._fabric.report_failure(mysqlserver.uuid, exc.errno) raise InterfaceError( "Reported faulty server to Fabric ({0})".format(exc)) if attempt_delay > 0: time.sleep(attempt_delay) continue else: self._fabric_mysql_server = mysqlserver break def disconnect(self): """Close connection to MySQL server""" try: self.rollback() self._mysql_cnx.close() except AttributeError: pass # There was no connection except Error: raise finally: self._mysql_cnx = None self._fabric_mysql_server = None close = disconnect def cursor(self, buffered=None, raw=None, prepared=None, cursor_class=None): """Instantiates and returns a cursor This method is similar to MySQLConnection.cursor() except that it checks whether the connection is available and raises an InterfaceError when not. cursor_class argument is not supported and will raise a NotSupportedError exception. Returns a MySQLCursor or subclass. """ self._connect() if cursor_class: raise NotSupportedError( "Custom cursors not supported with MySQL Fabric") if prepared: raise NotSupportedError( "Prepared Statements are not supported with MySQL Fabric") if self._unread_result is True: raise InternalError("Unread result found.") buffered = buffered or self._buffered raw = raw or self._raw cursor_type = 0 if buffered is True: cursor_type |= 1 if raw is True: cursor_type |= 2 types = ( MySQLCursor, # 0 MySQLCursorBuffered, MySQLCursorRaw, MySQLCursorBufferedRaw, ) return (types[cursor_type])(self) def handle_mysql_error(self, exc): """Handles MySQL errors This method takes a mysql.connector.errors.Error exception and checks the error code. Based on the value, it takes certain actions such as clearing the cache. """ if exc.errno in RESET_CACHE_ON_ERROR: self.reset_cache() self.disconnect() raise MySQLFabricError( "Temporary error ({error}); " "retry transaction".format(error=str(exc))) raise exc def commit(self): """Commit current transaction Raises whatever MySQLConnection.commit() raises, but raises MySQLFabricError when MySQL returns error ER_OPTION_PREVENTS_STATEMENT. """ try: self._mysql_cnx.commit() except Error as exc: self.handle_mysql_error(exc) def rollback(self): """Rollback current transaction Raises whatever MySQLConnection.rollback() raises, but raises MySQLFabricError when MySQL returns error ER_OPTION_PREVENTS_STATEMENT. """ try: self._mysql_cnx.rollback() except Error as exc: self.handle_mysql_error(exc) def cmd_query(self, statement): """Send a statement to the MySQL server Raises whatever MySQLConnection.cmd_query() raises, but raises MySQLFabricError when MySQL returns error ER_OPTION_PREVENTS_STATEMENT. Returns a dictionary. """ self._connect() try: return self._mysql_cnx.cmd_query(statement) except Error as exc: self.handle_mysql_error(exc) def cmd_query_iter(self, statements): """Send one or more statements to the MySQL server Raises whatever MySQLConnection.cmd_query_iter() raises, but raises MySQLFabricError when MySQL returns error ER_OPTION_PREVENTS_STATEMENT. Returns a dictionary. """ self._connect() try: return self._mysql_cnx.cmd_query_iter(statements) except Error as exc: self.handle_mysql_error(exc) mysql-connector-python-2.0.4/lib/mysql/connector/fabric/caching.py0000644001577100000120000002221612506707765024562 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Implementing caching mechanisms for MySQL Fabric""" import bisect from datetime import datetime, timedelta from hashlib import sha1 import logging import threading from . import FabricShard _LOGGER = logging.getLogger('myconnpy-fabric') _CACHE_TTL = 1 * 60 # 1 minute def insort_right_rev(alist, new_element, low=0, high=None): """Similar to bisect.insort_right but for reverse sorted lists This code is similar to the Python code found in Lib/bisect.py. We simply change the comparison from 'less than' to 'greater than'. """ if low < 0: raise ValueError('low must be non-negative') if high is None: high = len(alist) while low < high: middle = (low + high) // 2 if new_element > alist[middle]: high = middle else: low = middle + 1 alist.insert(low, new_element) class CacheEntry(object): """Base class for MySQL Fabric cache entries""" def __init__(self, version=None, fabric_uuid=None, ttl=_CACHE_TTL): self.version = version self.fabric_uuid = fabric_uuid self.last_updated = datetime.utcnow() self._ttl = ttl @classmethod def hash_index(cls, part1, part2=None): """Create hash for indexing""" raise NotImplementedError @property def invalid(self): """Returns True if entry is not valid any longer This property returns True when the entry is not valid any longer. The entry is valid when now > (last updated + ttl), where ttl is in seconds. """ if not self.last_updated: return False atime = self.last_updated + timedelta(seconds=self._ttl) return datetime.utcnow() > atime def reset_ttl(self): """Reset the Time to Live""" self.last_updated = datetime.utcnow() def invalidate(self): """Invalidates the cache entry""" self.last_updated = None class CacheShardTable(CacheEntry): """Cache entry for a Fabric sharded table""" def __init__(self, shard, version=None, fabric_uuid=None): if not isinstance(shard, FabricShard): raise ValueError("shard argument must be a FabricShard instance") super(CacheShardTable, self).__init__(version=version, fabric_uuid=fabric_uuid) self.partitioning = {} self._shard = shard self.keys = [] self.keys_reversed = [] if shard.key and shard.group: self.add_partition(shard.key, shard.group) def __getattr__(self, attr): return getattr(self._shard, attr) def add_partition(self, key, group): """Add sharding information for a group""" if self.shard_type == 'RANGE': key = int(key) elif self.shard_type == 'RANGE_DATETIME': try: if ':' in key: key = datetime.strptime(key, "%Y-%m-%d %H:%M:%S") else: key = datetime.strptime(key, "%Y-%m-%d").date() except: raise ValueError( "RANGE_DATETIME key could not be parsed, was: {0}".format( key )) elif self.shard_type == 'RANGE_STRING': pass elif self.shard_type == "HASH": pass else: raise ValueError("Unsupported sharding type {0}".format( self.shard_type )) self.partitioning[key] = { 'group': group, } self.reset_ttl() bisect.insort_right(self.keys, key) insort_right_rev(self.keys_reversed, key) @classmethod def hash_index(cls, part1, part2=None): """Create hash for indexing""" return sha1(part1.encode('utf-8') + part2.encode('utf-8')).hexdigest() def __repr__(self): return "{class_}({database}.{table}.{column})".format( class_=self.__class__, database=self.database, table=self.table, column=self.column ) class CacheGroup(CacheEntry): """Cache entry for a Fabric group""" def __init__(self, group_name, servers): super(CacheGroup, self).__init__(version=None, fabric_uuid=None) self.group_name = group_name self.servers = servers @classmethod def hash_index(cls, part1, part2=None): """Create hash for indexing""" return sha1(part1.encode('utf-8')).hexdigest() def __repr__(self): return "{class_}({group})".format( class_=self.__class__, group=self.group_name, ) class FabricCache(object): """Singleton class for caching Fabric data Only one instance of this class can exists globally. """ def __init__(self, ttl=_CACHE_TTL): self._ttl = ttl self._sharding = {} self._groups = {} self.__sharding_lock = threading.Lock() self.__groups_lock = threading.Lock() def remove_group(self, entry_hash): """Remove cache entry for group""" with self.__groups_lock: try: del self._groups[entry_hash] except KeyError: # not cached, that's OK pass else: _LOGGER.debug("Group removed from cache") def remove_shardtable(self, entry_hash): """Remove cache entry for shard""" with self.__sharding_lock: try: del self._sharding[entry_hash] except KeyError: # not cached, that's OK pass def sharding_cache_table(self, shard, version=None, fabric_uuid=None): """Cache information about a shard""" entry_hash = CacheShardTable.hash_index(shard.database, shard.table) with self.__sharding_lock: try: entry = self._sharding[entry_hash] entry.add_partition(shard.key, shard.group) except KeyError: # New cache entry entry = CacheShardTable(shard, version=version, fabric_uuid=fabric_uuid) self._sharding[entry_hash] = entry def cache_group(self, group_name, servers): """Cache information about a group""" entry_hash = CacheGroup.hash_index(group_name) with self.__groups_lock: try: entry = self._groups[entry_hash] entry.servers = servers entry.reset_ttl() _LOGGER.debug("Recaching group {0} with {1}".format( group_name, servers)) except KeyError: # New cache entry entry = CacheGroup(group_name, servers) self._groups[entry_hash] = entry _LOGGER.debug("Caching group {0} with {1}".format( group_name, servers)) def sharding_search(self, database, table): """Search cache for a shard based on database and table""" entry_hash = CacheShardTable.hash_index(database, table) entry = None try: entry = self._sharding[entry_hash] if entry.invalid: _LOGGER.debug("{entry} invalidated".format(entry)) self.remove_shardtable(entry_hash) return None except KeyError: # Nothing in cache return None return entry def group_search(self, group_name): """Search cache for a group based on its name""" entry_hash = CacheGroup.hash_index(group_name) entry = None try: entry = self._groups[entry_hash] if entry.invalid: _LOGGER.debug("{entry} invalidated".format(entry)) self.remove_group(entry_hash) return None except KeyError: # Nothing in cache return None return entry def __repr__(self): return "{class_}(groups={nrgroups},shards={nrshards})".format( class_=self.__class__, nrgroups=len(self._groups), nrshards=len(self._sharding) ) mysql-connector-python-2.0.4/lib/mysql/connector/fabric/balancing.py0000644001577100000120000001141112506707765025077 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Implementing load balancing""" import decimal def _calc_ratio(part, whole): """Calculate ratio Returns int """ return int((part/whole*100).quantize( decimal.Decimal('1'), rounding=decimal.ROUND_HALF_DOWN)) class BaseScheduling(object): """Base class for all scheduling classes dealing with load balancing""" def __init__(self): """Initialize""" self._members = [] self._ratios = [] def set_members(self, *args): """Set members and ratios This methods sets the members using the arguments passed. Each argument must be a sequence where the second item is the weight. The first element is an identifier. For example: ('server1', 0.6), ('server2', 0.8) Setting members means that the load will be reset. If the members are the same as previously set, nothing will be reset or set. If no arguments were given the members will be set to an empty list. Raises ValueError when weight can't be converted to a Decimal. """ raise NotImplementedError def get_next(self): """Returns the next member""" raise NotImplementedError @property def members(self): """Returns the members of this loadbalancer""" return self._members @property def ratios(self): """Returns the ratios for all members""" return self._ratios class WeightedRoundRobin(BaseScheduling): """Class for doing Weighted Round Robin balancing""" def __init__(self, *args): """Initializing""" super(WeightedRoundRobin, self).__init__() self._load = [] self._next_member = 0 self._nr_members = 0 if args: self.set_members(*args) @property def load(self): """Returns the current load""" return self._load def set_members(self, *args): if not args: # Reset members if nothing was given self._members = [] return new_members = [] for member in args: member = list(member) try: member[1] = decimal.Decimal(str(member[1])) except decimal.InvalidOperation: raise ValueError("Member '{member}' is invalid".format( member=member)) new_members.append(tuple(member)) new_members.sort(key=lambda x: x[1], reverse=True) if self._members == new_members: return self._members = new_members self._nr_members = len(new_members) min_weight = min(i[1] for i in self._members) self._ratios = [] for _, weight in self._members: self._ratios.append(int(weight/min_weight * 100)) self.reset() def reset(self): """Reset the load""" self._next_member = 0 self._load = [0] * self._nr_members def get_next(self): """Returns the next member""" if self._ratios == self._load: self.reset() # Figure out the member to return current = self._next_member while self._load[current] == self._ratios[current]: current = (current + 1) % self._nr_members # Update the load and set next member self._load[current] += 1 self._next_member = (current + 1) % self._nr_members # Return current return self._members[current] def __repr__(self): return "{class_}(load={load}, ratios={ratios})".format( class_=self.__class__, load=self.load, ratios=self.ratios ) def __eq__(self, other): return self._members == other.members mysql-connector-python-2.0.4/lib/mysql/connector/fabric/__init__.py0000644001577100000120000000473112506707765024727 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """MySQL Fabric support""" from collections import namedtuple # Order of field_names must match how Fabric is returning the data FabricMySQLServer = namedtuple( 'FabricMySQLServer', ['uuid', 'group', 'host', 'port', 'mode', 'status', 'weight'] ) # Order of field_names must match how Fabric is returning the data FabricShard = namedtuple( 'FabricShard', ['database', 'table', 'column', 'key', 'shard', 'shard_type', 'group', 'global_group'] ) CNX_FABRIC_ARGS = ['fabric_host', 'fabric_username', 'fabric_password', 'fabric_port', 'fabric_connect_attempts', 'fabric_connect_delay', 'fabric_report_errors', 'fabric_ssl_ca', 'fabric_ssl_key', 'fabric_ssl_cert', 'fabric_user'] from .connection import ( MODE_READONLY, MODE_READWRITE, STATUS_PRIMARY, STATUS_SECONDARY, SCOPE_GLOBAL, SCOPE_LOCAL, Fabric, FabricConnection, MySQLFabricConnection, FabricSet, ) def connect(**kwargs): """Create a MySQLFabricConnection object""" return MySQLFabricConnection(**kwargs) __all__ = [ 'MODE_READWRITE', 'MODE_READONLY', 'STATUS_PRIMARY', 'STATUS_SECONDARY', 'SCOPE_GLOBAL', 'SCOPE_LOCAL', 'FabricMySQLServer', 'FabricShard', 'connect', 'Fabric', 'FabricConnection', 'MySQLFabricConnection', 'FabricSet', ] mysql-connector-python-2.0.4/lib/mysql/connector/locales/0000755001577100000120000000000012506713751022775 5ustar pb2userwheelmysql-connector-python-2.0.4/lib/mysql/connector/locales/eng/0000755001577100000120000000000012506713751023546 5ustar pb2userwheelmysql-connector-python-2.0.4/lib/mysql/connector/locales/eng/client_error.py0000644001577100000120000001357012506707765026625 0ustar pb2userwheel# -*- coding: utf-8 -*- # MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 # This file was auto-generated. _GENERATED_ON = '2014-10-10' _MYSQL_VERSION = (5, 7, 5) # Start MySQL Error messages CR_UNKNOWN_ERROR = u"Unknown MySQL error" CR_SOCKET_CREATE_ERROR = u"Can't create UNIX socket (%s)" CR_CONNECTION_ERROR = u"Can't connect to local MySQL server through socket '%-.100s' (%s)" CR_CONN_HOST_ERROR = u"Can't connect to MySQL server on '%-.100s' (%s)" CR_IPSOCK_ERROR = u"Can't create TCP/IP socket (%s)" CR_UNKNOWN_HOST = u"Unknown MySQL server host '%-.100s' (%s)" CR_SERVER_GONE_ERROR = u"MySQL server has gone away" CR_VERSION_ERROR = u"Protocol mismatch; server version = %s, client version = %s" CR_OUT_OF_MEMORY = u"MySQL client ran out of memory" CR_WRONG_HOST_INFO = u"Wrong host info" CR_LOCALHOST_CONNECTION = u"Localhost via UNIX socket" CR_TCP_CONNECTION = u"%-.100s via TCP/IP" CR_SERVER_HANDSHAKE_ERR = u"Error in server handshake" CR_SERVER_LOST = u"Lost connection to MySQL server during query" CR_COMMANDS_OUT_OF_SYNC = u"Commands out of sync; you can't run this command now" CR_NAMEDPIPE_CONNECTION = u"Named pipe: %-.32s" CR_NAMEDPIPEWAIT_ERROR = u"Can't wait for named pipe to host: %-.64s pipe: %-.32s (%s)" CR_NAMEDPIPEOPEN_ERROR = u"Can't open named pipe to host: %-.64s pipe: %-.32s (%s)" CR_NAMEDPIPESETSTATE_ERROR = u"Can't set state of named pipe to host: %-.64s pipe: %-.32s (%s)" CR_CANT_READ_CHARSET = u"Can't initialize character set %-.32s (path: %-.100s)" CR_NET_PACKET_TOO_LARGE = u"Got packet bigger than 'max_allowed_packet' bytes" CR_EMBEDDED_CONNECTION = u"Embedded server" CR_PROBE_SLAVE_STATUS = u"Error on SHOW SLAVE STATUS:" CR_PROBE_SLAVE_HOSTS = u"Error on SHOW SLAVE HOSTS:" CR_PROBE_SLAVE_CONNECT = u"Error connecting to slave:" CR_PROBE_MASTER_CONNECT = u"Error connecting to master:" CR_SSL_CONNECTION_ERROR = u"SSL connection error: %-.100s" CR_MALFORMED_PACKET = u"Malformed packet" CR_WRONG_LICENSE = u"This client library is licensed only for use with MySQL servers having '%s' license" CR_NULL_POINTER = u"Invalid use of null pointer" CR_NO_PREPARE_STMT = u"Statement not prepared" CR_PARAMS_NOT_BOUND = u"No data supplied for parameters in prepared statement" CR_DATA_TRUNCATED = u"Data truncated" CR_NO_PARAMETERS_EXISTS = u"No parameters exist in the statement" CR_INVALID_PARAMETER_NO = u"Invalid parameter number" CR_INVALID_BUFFER_USE = u"Can't send long data for non-string/non-binary data types (parameter: %s)" CR_UNSUPPORTED_PARAM_TYPE = u"Using unsupported buffer type: %s (parameter: %s)" CR_SHARED_MEMORY_CONNECTION = u"Shared memory: %-.100s" CR_SHARED_MEMORY_CONNECT_REQUEST_ERROR = u"Can't open shared memory; client could not create request event (%s)" CR_SHARED_MEMORY_CONNECT_ANSWER_ERROR = u"Can't open shared memory; no answer event received from server (%s)" CR_SHARED_MEMORY_CONNECT_FILE_MAP_ERROR = u"Can't open shared memory; server could not allocate file mapping (%s)" CR_SHARED_MEMORY_CONNECT_MAP_ERROR = u"Can't open shared memory; server could not get pointer to file mapping (%s)" CR_SHARED_MEMORY_FILE_MAP_ERROR = u"Can't open shared memory; client could not allocate file mapping (%s)" CR_SHARED_MEMORY_MAP_ERROR = u"Can't open shared memory; client could not get pointer to file mapping (%s)" CR_SHARED_MEMORY_EVENT_ERROR = u"Can't open shared memory; client could not create %s event (%s)" CR_SHARED_MEMORY_CONNECT_ABANDONED_ERROR = u"Can't open shared memory; no answer from server (%s)" CR_SHARED_MEMORY_CONNECT_SET_ERROR = u"Can't open shared memory; cannot send request event to server (%s)" CR_CONN_UNKNOW_PROTOCOL = u"Wrong or unknown protocol" CR_INVALID_CONN_HANDLE = u"Invalid connection handle" CR_UNUSED_1 = u"Connection using old (pre-4.1.1) authentication protocol refused (client option 'secure_auth' enabled)" CR_FETCH_CANCELED = u"Row retrieval was canceled by mysql_stmt_close() call" CR_NO_DATA = u"Attempt to read column without prior row fetch" CR_NO_STMT_METADATA = u"Prepared statement contains no metadata" CR_NO_RESULT_SET = u"Attempt to read a row while there is no result set associated with the statement" CR_NOT_IMPLEMENTED = u"This feature is not implemented yet" CR_SERVER_LOST_EXTENDED = u"Lost connection to MySQL server at '%s', system error: %s" CR_STMT_CLOSED = u"Statement closed indirectly because of a preceeding %s() call" CR_NEW_STMT_METADATA = u"The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again" CR_ALREADY_CONNECTED = u"This handle is already connected. Use a separate handle for each connection." CR_AUTH_PLUGIN_CANNOT_LOAD = u"Authentication plugin '%s' cannot be loaded: %s" CR_DUPLICATE_CONNECTION_ATTR = u"There is an attribute with the same name already" CR_AUTH_PLUGIN_ERR = u"Authentication plugin '%s' reported error: %s" # End MySQL Error messages mysql-connector-python-2.0.4/lib/mysql/connector/locales/eng/__init__.py0000644001577100000120000000220112506707765025662 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """English Content """ mysql-connector-python-2.0.4/lib/mysql/connector/locales/__init__.py0000644001577100000120000000457712506707765025133 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Translations """ __all__ = [ 'get_client_error' ] from .. import errorcode def get_client_error(error, language='eng'): """Lookup client error This function will lookup the client error message based on the given error and return the error message. If the error was not found, None will be returned. Error can be either an integer or a string. For example: error: 2000 error: CR_UNKNOWN_ERROR The language attribute can be used to retrieve a localized message, when available. Returns a string or None. """ try: tmp = __import__('mysql.connector.locales.{0}'.format(language), globals(), locals(), ['client_error']) except ImportError: raise ImportError("No localization support for language '{0}'".format( language)) client_error = tmp.client_error if isinstance(error, int): errno = error for key, value in errorcode.__dict__.items(): if value == errno: error = key break if isinstance(error, (str)): try: return getattr(client_error, error) except AttributeError: return None raise ValueError("error argument needs to be either an integer or string") mysql-connector-python-2.0.4/lib/mysql/connector/optionfiles.py0000644001577100000120000003310312506707765024270 0ustar pb2userwheel# MySQL Connector/Python - MySQL driver written in Python. # Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. # MySQL Connector/Python is licensed under the terms of the GPLv2 # , like most # MySQL Connectors. There are special exceptions to the terms and # conditions of the GPLv2 as it is applied to this software, see the # FOSS License Exception # . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 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 """Implements parser to parse MySQL option files. """ import codecs import io import os import re from .catch23 import PY2 from .connection import DEFAULT_CONFIGURATION from .pooling import CNX_POOL_ARGS from .fabric import CNX_FABRIC_ARGS # pylint: disable=F0401 if PY2: from ConfigParser import SafeConfigParser, MissingSectionHeaderError else: from configparser import (ConfigParser as SafeConfigParser, MissingSectionHeaderError) # pylint: enable=F0401 DEFAULT_EXTENSIONS = { 'nt': ('ini', 'cnf'), 'posix': ('cnf',) } def read_option_files(**config): """ Read option files for connection parameters. Checks if connection arguments contain option file arguments, and then reads option files accordingly. """ if 'option_files' in config: try: if isinstance(config['option_groups'], str): config['option_groups'] = [config['option_groups']] groups = config['option_groups'] del config['option_groups'] except KeyError: groups = ['client', 'connector_python'] if isinstance(config['option_files'], str): config['option_files'] = [config['option_files']] option_parser = MySQLOptionsParser(list(config['option_files']), keep_dashes=False) del config['option_files'] config_from_file = option_parser.get_groups_as_dict_with_priority( *groups) config_options = {} fabric_options = {} for group in groups: try: for option, value in config_from_file[group].items(): try: if option == 'socket': option = 'unix_socket' if option in CNX_FABRIC_ARGS: if (option not in fabric_options or fabric_options[option][1] <= value[1]): fabric_options[option] = value continue if (option not in CNX_POOL_ARGS and option not in ['fabric', 'failover']): # pylint: disable=W0104 DEFAULT_CONFIGURATION[option] # pylint: enable=W0104 if (option not in config_options or config_options[option][1] <= value[1]): config_options[option] = value except KeyError: if group is 'connector_python': raise AttributeError("Unsupported argument " "'{0}'".format(option)) except KeyError: continue not_evaluate = ('password', 'passwd') for option, value in config_options.items(): if option not in config: try: if option in not_evaluate: config[option] = value[0] else: config[option] = eval(value[0]) # pylint: disable=W0123 except (NameError, SyntaxError): config[option] = value[0] if fabric_options: config['fabric'] = {} for option, value in fabric_options.items(): try: # pylint: disable=W0123 config['fabric'][option.split('_', 1)[1]] = eval(value[0]) # pylint: enable=W0123 except (NameError, SyntaxError): config['fabric'][option.split('_', 1)[1]] = value[0] return config class MySQLOptionsParser(SafeConfigParser): # pylint: disable=R0901 """This class implements methods to parse MySQL option files""" def __init__(self, files=None, keep_dashes=True): # pylint: disable=W0231 """Initialize If defaults is True, default option files are read first Raises ValueError if defaults is set to True but defaults files cannot be found. """ # Regular expression to allow options with no value(For Python v2.6) self.OPTCRE = re.compile( # pylint: disable=C0103 r'(?P