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) tmpbuf = ''.join(pkts) del pkts seqid = 0 zbuf = zlib.compress(tmpbuf[:16384]) zpkts.append( struct.pack('<I', len(zbuf))[0:3] + struct.pack('<B', seqid) + '\x00\x40\x00' + zbuf) tmpbuf = tmpbuf[16384:] pllen = len(tmpbuf) seqid = seqid + 1 while pllen > maxpktlen: zbuf = zlib.compress(tmpbuf[:maxpktlen]) zpkts.append( struct.pack('<I', len(zbuf))[0:3] + struct.pack('<B', seqid) + '\xff\xff\xff' + zbuf) tmpbuf = tmpbuf[maxpktlen:] pllen = len(tmpbuf) seqid = seqid + 1 if tmpbuf: zbuf = zlib.compress(tmpbuf) zpkts.append( struct.pack('<I', len(zbuf))[0:3] + struct.pack('<B', seqid) + struct.pack('<I', pllen)[0:3] + zbuf) del tmpbuf else: pkt = (struct.pack('<I', pllen)[0:3] + struct.pack('<B', pktnr) + buf) pllen = len(pkt) if pllen > 50: zbuf = zlib.compress(pkt) zpkts.append( struct.pack('<I', len(zbuf))[0:3] + struct.pack('<B', 0) + struct.pack('<I', pllen)[0:3] + zbuf) else: zpkts.append( struct.pack('<I', pllen)[0:3] + struct.pack('<B', 0) + struct.pack('<I', 0)[0:3] + pkt) for zip_packet in zpkts: try: self.sock.sendall(zip_packet) except IOError as err: raise errors.OperationalError(errno=2055, values=(self.get_address(), _strioerror(err))) except AttributeError: raise errors.OperationalError(errno=2006)
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: yield from self._socket.drain() 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: yield from self._socket.drain() self._socket.send(b'') except AttributeError: raise errors.OperationalError( "MySQL Connection not available.") return (yield from self._socket.recv())
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: 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)
def get_pool_conn_implicitly(_db_config): """ :param _db_config: :return: """ config = _db_config if 'pool' not in config: pool = None else: pool = config['pool'] if 'use' not in pool: raise errors.OperationalError("MySQL pool config error" " must pool key use") if 'size' not in pool: raise errors.OperationalError("MySQL pool config error" " must pool key size") if 'name' not in pool: raise errors.OperationalError("MySQL pool config error " "must pool key name") if pool and pool['use']: conn = PyMysqlPool.mysql.connector.connect(pool_name=pool['name'], pool_size=pool['size'], host=config['host'], port=config['port'], user=config['user'], passwd=config['passwd'], db=config['db'], charset=config['charset'], use_unicode=True, connect_timeout=1000) conn.set_converter_class(FuzzyMySQLConverter) else: conn = MySQLdb.connect(host=config['host'], port=config['port'], user=config['user'], passwd=config['passwd'], db=config['db'], charset=config['charset'], use_unicode=True) conn.start_transaction( consistent_snapshot=config.get('consistent_snapshot', False), isolation_level=config.get('isolation_level', None), readonly=config.get('readonly', None), ) return conn
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: yield from self._socket.drain() 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 (yield from self._socket.recv())
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) yield from 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 recv_plain(self): """Receive packets from the MySQL server""" try: # Read the header of the MySQL packet, 4 bytes packet = yield from self._reader.readexactly(4) # Save the packet number and payload length self._packet_number = packet[3] payload_len = struct.unpack("<I", packet[0:3] + b'\x00')[0] # Read the payload rest = payload_len spacket = packet packet = bytearray(4 + payload_len) packet[:4] = spacket packet_view = memoryview(packet) # pylint: disable=E0602 packet_view = packet_view[4:] while rest: read = yield from self._reader.read(rest) lrd = len(read) if lrd == 0 and rest > 0: raise errors.InterfaceError(errno=2013) packet_view[:lrd] = read packet_view = packet_view[lrd:] rest -= lrd return packet except IOError as err: raise errors.OperationalError(errno=2055, values=(self.get_address(), _strioerror(err)))
def recv_plain(self): """Receive packets from the MySQL server""" packet = '' try: # Read the header of the MySQL packet, 4 bytes packet = self.sock.recv(1) while len(packet) < 4: chunk = self.sock.recv(1) if not chunk: raise errors.InterfaceError(errno=2013) packet += chunk # Save the packet number and total packet length from header self._packet_number = ord(packet[3]) packet_totlen = struct.unpack("<I", packet[0:3] + '\x00')[0] + 4 # Read the rest of the packet rest = packet_totlen - len(packet) while rest > 0: chunk = self.sock.recv(rest) if not chunk: raise errors.InterfaceError(errno=2013) packet += chunk rest = packet_totlen - len(packet) return packet except IOError as err: raise errors.OperationalError(errno=2055, values=(self.get_address(), _strioerror(err)))
def recv_compressed(self): """Receive compressed packets from the MySQL server""" try: return self._packet_queue.popleft() except IndexError: pass header = '' packets = [] try: abyte = self.sock.recv(1) while abyte and len(header) < 7: header += abyte abyte = self.sock.recv(1) while header: if len(header) < 7: raise errors.InterfaceError(errno=2013) zip_payload_length = struct.unpack("<I", header[0:3] + '\x00')[0] payload_length = struct.unpack("<I", header[4:7] + '\x00')[0] zip_payload = abyte while len(zip_payload) < zip_payload_length: chunk = self.sock.recv(zip_payload_length - len(zip_payload)) if len(chunk) == 0: raise errors.InterfaceError(errno=2013) zip_payload = zip_payload + chunk if payload_length == 0: self._split_zipped_payload(zip_payload) return self._packet_queue.popleft() packets.append(header + zip_payload) if payload_length != 16384: break header = '' abyte = self.sock.recv(1) while abyte and len(header) < 7: header += abyte abyte = self.sock.recv(1) except IOError as err: raise errors.OperationalError(errno=2055, values=(self.get_address(), _strioerror(err))) tmp = [] for packet in packets: payload_length = struct.unpack("<I", header[4:7] + '\x00')[0] if payload_length == 0: tmp.append(packet[7:]) else: tmp.append(zlib.decompress(packet[7:])) self._split_zipped_payload(''.join(tmp)) del tmp try: return self._packet_queue.popleft() except IndexError: pass
def send_plain(self, buf, packet_number=None): """Send packets to the MySQL server""" if packet_number is None: self.next_packet_number else: self._packet_number = packet_number packets = _prepare_packets(buf, self._packet_number) for packet in packets: try: self.sock.sendall(packet) except Exception, err: raise errors.OperationalError(str(err))
def get_pool_conn_implicitly(_db_config): config = _db_config if 'pool' not in config: pool = None else: pool = config['pool'] if 'use' not in pool: raise errors.OperationalError( "MySQL pool config error must pool key use") if 'size' not in pool: raise errors.OperationalError( "MySQL pool config error must pool key size") if 'name' not in pool: raise errors.OperationalError( "MySQL pool config error must pool key name") if pool and pool['use']: conn = PyMysqlPool.mysql.connector.connect( pool_name=pool['name'], pool_size=pool['size'], host=config['host'], port=config.get('port', 3306), user=config['user'], passwd=config['password'], db=config['database'], charset=config.get('charset', 'utf8'), use_unicode=True, connect_timeout=1000) conn.set_converter_class(FuzzyMySQLConverter) else: conn = MySQLdb.connect(host=config['host'], port=config.get('port', 3306), user=config['user'], passwd=config['password'], db=config['database'], charset=config.get('charset', 'utf8'), use_unicode=True) return conn
def _handle_load_data_infile(self, filename): try: if "~" in filename: filename = str(filename).replace("~", os.path.expanduser('~')) data_file = open(filename, 'rb') except IOError: 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_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 open_connection(self): """Open the TCP/IP connection to the MySQL server """ # Get address information addrinfo = [None] * 5 try: addrinfos = socket.getaddrinfo(self.server_host, self.server_port, 0, socket.SOCK_STREAM, socket.SOL_TCP) # If multiple results we favor IPv4, unless IPv6 was forced. for info in addrinfos: if self.force_ipv6 and info[0] == socket.AF_INET6: addrinfo = info break elif info[0] == socket.AF_INET: addrinfo = info break if self.force_ipv6 and addrinfo[0] is None: raise errors.InterfaceError( "No IPv6 address found for {0}".format(self.server_host)) if addrinfo[0] is None: addrinfo = addrinfos[0] except IOError as err: raise errors.InterfaceError(errno=2003, values=(self.get_address(), _strioerror(err))) else: (self._family, socktype, proto, _, sockaddr) = addrinfo # Instanciate the socket and connect try: self._reader, self._writer = (yield from (asyncio.wait_for( asyncio.open_connection(self.server_host, port=self.server_port, loop=self._loop, limit=self._default_buffer_limit), timeout=self._connection_timeout, loop=self._loop))) except IOError as err: raise errors.InterfaceError(errno=2003, values=(self.get_address(), _strioerror(err))) except Exception as err: raise errors.OperationalError(str(err))
def open_connection(self): """Open the TCP/IP connection to the MySQL server """ # Get address information addrinfo = None try: addrinfos = socket.getaddrinfo(self.server_host, self.server_port, 0, 0, socket.SOL_TCP) # If multiple results we favor IPv4, unless IPv6 was forced. for info in addrinfos: if self.force_ipv6 and info[0] == socket.AF_INET6: addrinfo = info break elif info[0] == socket.AF_INET: addrinfo = info break if self.force_ipv6 and not addrinfo: raise errors.InterfaceError( "No IPv6 address found for {0}".format(self.server_host)) if not addrinfo: addrinfo = addrinfos[0] except IOError as err: raise errors.InterfaceError(errno=2003, values=(self.get_address(), _strioerror(err))) (self._family, socktype, proto, canonname, sockaddr) = addrinfo # Instanciate the socket and connect try: self.sock = socket.socket(self._family, socktype, proto) self.sock.settimeout(self._connection_timeout) self.sock.connect(sockaddr) except IOError as err: raise errors.InterfaceError(errno=2003, values=(self.get_address(), _strioerror(err))) except Exception as err: raise errors.OperationalError(str(err))
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. """ cn = yield from self.is_connected() if not cn: raise errors.OperationalError("MySQL Connection not available.") try: yield from 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: yield from self.cmd_change_user(self._user, self._password, self._database, self._charset_id) cur = yield from self.cursor() if user_variables: for key, value in user_variables.items(): yield from cur.execute("SET @`{0}` = %s".format(key), (value,)) if session_variables: for key, value in session_variables.items(): yield from cur.execute("SET SESSION `{0}` = %s".format(key), (value,))
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.") connected = yield from self.is_connected() if not 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: AioMySQLCursor, # 0 1: AioMySQLCursorBuffered, 2: AioMySQLCursorRaw, 3: AioMySQLCursorBufferedRaw, 4: AioMySQLCursorDict, 5: AioMySQLCursorBufferedDict, 8: AioMySQLCursorNamedTuple, 9: AioMySQLCursorBufferedNamedTuple, 16: AioMySQLCursorPrepared } 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]))