updating the mysql library to 0.3pre
This commit is contained in:
parent
51fbf3eef8
commit
d812487d23
|
@ -1,5 +1,5 @@
|
||||||
# MySQL Connector/Python - MySQL driver written in Python.
|
# MySQL Connector/Python - MySQL driver written in Python.
|
||||||
# Copyright 2009 Sun Microsystems, Inc. All rights reserved
|
# Copyright (c) 2009,2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
# Use is subject to license terms. (See COPYING)
|
# Use is subject to license terms. (See COPYING)
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -25,15 +25,6 @@
|
||||||
MySQL Connector/Python - MySQL drive written in Python
|
MySQL Connector/Python - MySQL drive written in Python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
|
||||||
_name = 'MySQL Connector/Python'
|
|
||||||
if not hasattr(sys, "version_info") or sys.version_info < (2,4):
|
|
||||||
raise RuntimeError("%s requires Python 2.4 or higher." % (_name))
|
|
||||||
elif sys.version_info >= (3,0):
|
|
||||||
raise RuntimeError("%s does not yet support Python v3." % (_name))
|
|
||||||
del _name
|
|
||||||
del sys
|
|
||||||
|
|
||||||
# Python Db API v2
|
# Python Db API v2
|
||||||
apilevel = '2.0'
|
apilevel = '2.0'
|
||||||
threadsafety = 1
|
threadsafety = 1
|
||||||
|
@ -43,21 +34,22 @@ paramstyle = 'pyformat'
|
||||||
import _version
|
import _version
|
||||||
__version__ = _version.version
|
__version__ = _version.version
|
||||||
|
|
||||||
from mysql import MySQL
|
from connection import MySQLConnection
|
||||||
from errors import *
|
from errors import *
|
||||||
from constants import FieldFlag, FieldType, CharacterSet, RefreshOption
|
from constants import FieldFlag, FieldType, CharacterSet,\
|
||||||
|
RefreshOption, ClientFlag
|
||||||
from dbapi import *
|
from dbapi import *
|
||||||
|
|
||||||
def Connect(*args, **kwargs):
|
def Connect(*args, **kwargs):
|
||||||
"""Shortcut for creating a mysql.MySQL object."""
|
"""Shortcut for creating a connection.MySQLConnection object."""
|
||||||
return MySQL(*args, **kwargs)
|
return MySQLConnection(*args, **kwargs)
|
||||||
connect = Connect
|
connect = Connect
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'MySQL', 'Connect',
|
'MySQLConnection', 'Connect',
|
||||||
|
|
||||||
# Some useful constants
|
# Some useful constants
|
||||||
'FieldType','FieldFlag','CharacterSet','RefreshOption',
|
'FieldType','FieldFlag','ClientFlag','CharacterSet','RefreshOption',
|
||||||
|
|
||||||
# Error handling
|
# Error handling
|
||||||
'Error','Warning',
|
'Error','Warning',
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# MySQL Connector/Python - MySQL driver written in Python.
|
# MySQL Connector/Python - MySQL driver written in Python.
|
||||||
# Copyright 2009 Sun Microsystems, Inc. All rights reserved
|
# Copyright (c) 2009,2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
# Use is subject to license terms. (See COPYING)
|
# Use is subject to license terms. (See COPYING)
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -25,4 +25,4 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Next line is generated
|
# Next line is generated
|
||||||
version = (0, 1, 0, 'devel', '')
|
version = (0, 3, 0, 'devel', '')
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# MySQL Connector/Python - MySQL driver written in Python.
|
# MySQL Connector/Python - MySQL driver written in Python.
|
||||||
# Copyright 2009 Sun Microsystems, Inc. All rights reserved
|
# Copyright (c) 2009,2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
# Use is subject to license terms. (See COPYING)
|
# Use is subject to license terms. (See COPYING)
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -24,31 +24,35 @@
|
||||||
"""Implementing communication to MySQL servers
|
"""Implementing communication to MySQL servers
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
import socket
|
import socket
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import weakref
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
import constants
|
||||||
|
import conversion
|
||||||
import protocol
|
import protocol
|
||||||
import errors
|
import errors
|
||||||
from constants import CharacterSet
|
import utils
|
||||||
|
import cursor
|
||||||
|
|
||||||
class MySQLBaseConnection(object):
|
logger = logging.getLogger('myconnpy')
|
||||||
|
|
||||||
|
class MySQLBaseSocket(object):
|
||||||
"""Base class for MySQL Connections subclasses.
|
"""Base class for MySQL Connections subclasses.
|
||||||
|
|
||||||
Should not be used directly but overloaded, changing the
|
Should not be used directly but overloaded, changing the
|
||||||
open_connection part. Examples over subclasses are
|
open_connection part. Examples of subclasses are
|
||||||
MySQLTCPConnection
|
MySQLTCPSocket
|
||||||
MySQLUNIXConnection
|
MySQLUnixSocket
|
||||||
"""
|
"""
|
||||||
def __init__(self, prtcls=None):
|
def __init__(self):
|
||||||
self.sock = None # holds the socket connection
|
self.sock = None # holds the socket connection
|
||||||
self.connection_timeout = None
|
self.connection_timeout = None
|
||||||
self.protocol = None
|
self.buffer = deque()
|
||||||
self.socket_flags = 0
|
self.recvsize = 1024*8
|
||||||
try:
|
|
||||||
self.protocol = prtcls(self)
|
|
||||||
except:
|
|
||||||
self.protocol = protocol.MySQLProtocol(self)
|
|
||||||
self._set_socket_flags()
|
|
||||||
|
|
||||||
def open_connection(self):
|
def open_connection(self):
|
||||||
pass
|
pass
|
||||||
|
@ -59,9 +63,11 @@ class MySQLBaseConnection(object):
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def get_address(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def send(self, buf):
|
def send(self, buf):
|
||||||
"""
|
"""Send packets over the socket
|
||||||
Send packets using the socket to the server.
|
|
||||||
"""
|
"""
|
||||||
pktlen = len(buf)
|
pktlen = len(buf)
|
||||||
try:
|
try:
|
||||||
|
@ -71,47 +77,55 @@ class MySQLBaseConnection(object):
|
||||||
raise errors.OperationalError('%s' % e)
|
raise errors.OperationalError('%s' % e)
|
||||||
|
|
||||||
def recv(self):
|
def recv(self):
|
||||||
"""
|
"""Receive packets from the socket
|
||||||
Receive packets using the socket from the server.
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
header = self.sock.recv(4, self.socket_flags)
|
return self.buffer.popleft()
|
||||||
(pktsize, pktnr) = self.protocol.handle_header(header)
|
except IndexError:
|
||||||
buf = header + self.sock.recv(pktsize, self.socket_flags)
|
pass
|
||||||
self.protocol.is_error(buf)
|
|
||||||
|
pktnr = -1
|
||||||
|
try:
|
||||||
|
buf = self.sock.recv(self.recvsize)
|
||||||
|
while buf:
|
||||||
|
totalsize = len(buf)
|
||||||
|
if pktnr == -1 and totalsize > 4:
|
||||||
|
pktsize = utils.intread(buf[0:3])
|
||||||
|
pktnr = utils.intread(buf[3])
|
||||||
|
if pktnr > -1 and totalsize >= pktsize+4:
|
||||||
|
size = pktsize+4
|
||||||
|
self.buffer.append(buf[0:size])
|
||||||
|
buf = buf[size:]
|
||||||
|
pktnr = -1
|
||||||
|
if len(buf) == 0:
|
||||||
|
break
|
||||||
|
elif len(buf) < pktsize+4:
|
||||||
|
buf += self.sock.recv(self.recvsize)
|
||||||
|
except socket.timeout, e:
|
||||||
|
raise errors.InterfaceError(errno=2013)
|
||||||
|
except socket.error, e:
|
||||||
|
raise errors.InterfaceError(errno=2055,
|
||||||
|
values=dict(socketaddr=self.get_address(),errno=e.errno))
|
||||||
except:
|
except:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
return (buf, pktsize, pktnr)
|
|
||||||
|
|
||||||
def set_protocol(self, prtcls):
|
|
||||||
try:
|
try:
|
||||||
self.protocol = prtcls(self, self.protocol.handshake)
|
return self.buffer.popleft()
|
||||||
except:
|
except IndexError, e:
|
||||||
self.protocol = protocol.MySQLProtocol(self)
|
pass
|
||||||
|
|
||||||
def set_connection_timeout(self, timeout):
|
def set_connection_timeout(self, timeout):
|
||||||
self.connection_timeout = timeout
|
self.connection_timeout = timeout
|
||||||
|
|
||||||
def _set_socket_flags(self, flags=None):
|
class MySQLUnixSocket(MySQLBaseSocket):
|
||||||
self.socket_flags = 0
|
|
||||||
if flags is None:
|
|
||||||
if os.name == 'nt':
|
|
||||||
flags = 0
|
|
||||||
else:
|
|
||||||
flags = socket.MSG_WAITALL
|
|
||||||
|
|
||||||
if flags is not None:
|
|
||||||
self.socket_flags = flags
|
|
||||||
|
|
||||||
|
|
||||||
class MySQLUnixConnection(MySQLBaseConnection):
|
|
||||||
"""Opens a connection through the UNIX socket of the MySQL Server."""
|
"""Opens a connection through the UNIX socket of the MySQL Server."""
|
||||||
|
|
||||||
def __init__(self, prtcls=None,unix_socket='/tmp/mysql.sock'):
|
def __init__(self, unix_socket='/tmp/mysql.sock'):
|
||||||
MySQLBaseConnection.__init__(self, prtcls=prtcls)
|
MySQLBaseSocket.__init__(self)
|
||||||
self.unix_socket = unix_socket
|
self.unix_socket = unix_socket
|
||||||
self.socket_flags = socket.MSG_WAITALL
|
|
||||||
|
def get_address(self):
|
||||||
|
return self.unix_socket
|
||||||
|
|
||||||
def open_connection(self):
|
def open_connection(self):
|
||||||
"""Opens a UNIX socket and checks the MySQL handshake."""
|
"""Opens a UNIX socket and checks the MySQL handshake."""
|
||||||
|
@ -119,30 +133,480 @@ class MySQLUnixConnection(MySQLBaseConnection):
|
||||||
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
self.sock.settimeout(self.connection_timeout)
|
self.sock.settimeout(self.connection_timeout)
|
||||||
self.sock.connect(self.unix_socket)
|
self.sock.connect(self.unix_socket)
|
||||||
|
except socket.error, e:
|
||||||
|
try:
|
||||||
|
m = e.errno
|
||||||
|
except:
|
||||||
|
m = e
|
||||||
|
raise errors.InterfaceError(errno=2002,
|
||||||
|
values=dict(socketaddr=self.get_address(),errno=m))
|
||||||
except StandardError, e:
|
except StandardError, e:
|
||||||
raise errors.OperationalError('%s' % e)
|
raise errors.InterfaceError('%s' % e)
|
||||||
|
|
||||||
buf = self.recv()[0]
|
class MySQLTCPSocket(MySQLBaseSocket):
|
||||||
self.protocol.handle_handshake(buf)
|
|
||||||
|
|
||||||
class MySQLTCPConnection(MySQLBaseConnection):
|
|
||||||
"""Opens a TCP connection to the MySQL Server."""
|
"""Opens a TCP connection to the MySQL Server."""
|
||||||
|
|
||||||
def __init__(self, prtcls=None, host='127.0.0.1', port=3306):
|
def __init__(self, host='127.0.0.1', port=3306):
|
||||||
MySQLBaseConnection.__init__(self, prtcls=prtcls)
|
MySQLBaseSocket.__init__(self)
|
||||||
self.server_host = host
|
self.server_host = host
|
||||||
self.server_port = port
|
self.server_port = port
|
||||||
|
|
||||||
|
def get_address(self):
|
||||||
|
return "%s:%s" % (self.server_host,self.server_port)
|
||||||
|
|
||||||
def open_connection(self):
|
def open_connection(self):
|
||||||
"""Opens a TCP Connection and checks the MySQL handshake."""
|
"""Opens a TCP Connection and checks the MySQL handshake."""
|
||||||
try:
|
try:
|
||||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
self.sock.settimeout(self.connection_timeout)
|
self.sock.settimeout(self.connection_timeout)
|
||||||
self.sock.connect( (self.server_host, self.server_port) )
|
self.sock.connect( (self.server_host, self.server_port) )
|
||||||
|
except socket.error, e:
|
||||||
|
try:
|
||||||
|
m = e.errno
|
||||||
|
except:
|
||||||
|
m = e
|
||||||
|
raise errors.InterfaceError(errno=2003,
|
||||||
|
values=dict(socketaddr=self.get_address(),errno=m))
|
||||||
except StandardError, e:
|
except StandardError, e:
|
||||||
raise errors.OperationalError('%s' % e)
|
raise errors.InterfaceError('%s' % e)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
buf = self.recv()[0]
|
class MySQLConnection(object):
|
||||||
self.protocol.handle_handshake(buf)
|
"""MySQL"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Initializing"""
|
||||||
|
self.conn = None # Holding the connection
|
||||||
|
self.protocol = None
|
||||||
|
self.converter = None
|
||||||
|
self.cursors = []
|
||||||
|
|
||||||
|
self.client_flags = constants.ClientFlag.get_default()
|
||||||
|
self._charset = 33
|
||||||
|
|
||||||
|
self._username = ''
|
||||||
|
self._database = ''
|
||||||
|
self._server_host = '127.0.0.1'
|
||||||
|
self._server_port = 3306
|
||||||
|
self._unix_socket = None
|
||||||
|
self.client_host = ''
|
||||||
|
self.client_port = 0
|
||||||
|
|
||||||
|
self.affected_rows = 0
|
||||||
|
self.server_status = 0
|
||||||
|
self.warning_count = 0
|
||||||
|
self.field_count = 0
|
||||||
|
self.insert_id = 0
|
||||||
|
self.info_msg = ''
|
||||||
|
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.raw = False
|
||||||
|
|
||||||
|
if len(kwargs) > 0:
|
||||||
|
self.connect(*args, **kwargs)
|
||||||
|
|
||||||
|
def connect(self, database=None, user='', password='',
|
||||||
|
host='127.0.0.1', port=3306, unix_socket=None,
|
||||||
|
use_unicode=True, charset='utf8', collation=None,
|
||||||
|
autocommit=False,
|
||||||
|
time_zone=None, sql_mode=None,
|
||||||
|
get_warnings=False, raise_on_warnings=False,
|
||||||
|
connection_timeout=None, client_flags=0,
|
||||||
|
buffered=False, raw=False,
|
||||||
|
passwd=None, db=None, connect_timeout=None, dsn=None):
|
||||||
|
if db and not database:
|
||||||
|
database = db
|
||||||
|
if passwd and not password:
|
||||||
|
password = passwd
|
||||||
|
if connect_timeout and not connection_timeout:
|
||||||
|
connection_timeout = connect_timeout
|
||||||
|
|
||||||
|
if dsn is not None:
|
||||||
|
errors.NotSupportedError("Data source name is not supported")
|
||||||
|
|
||||||
|
self._server_host = host
|
||||||
|
self._server_port = port
|
||||||
|
self._unix_socket = unix_socket
|
||||||
|
if database is not None:
|
||||||
|
self._database = database.strip()
|
||||||
|
else:
|
||||||
|
self._database = None
|
||||||
|
self._username = user
|
||||||
|
|
||||||
|
self.set_warnings(get_warnings,raise_on_warnings)
|
||||||
|
self.connection_timeout = connection_timeout
|
||||||
|
self.buffered = buffered
|
||||||
|
self.raw = raw
|
||||||
|
self.use_unicode = use_unicode
|
||||||
|
self.set_client_flags(client_flags)
|
||||||
|
self._charset = constants.CharacterSet.get_charset_info(charset)[0]
|
||||||
|
|
||||||
|
if user or password:
|
||||||
|
self.set_login(user, password)
|
||||||
|
|
||||||
|
self.disconnect()
|
||||||
|
self._open_connection(username=user, password=password, database=database,
|
||||||
|
client_flags=self.client_flags, charset=charset)
|
||||||
|
self._post_connection(time_zone=time_zone, sql_mode=sql_mode,
|
||||||
|
collation=collation)
|
||||||
|
|
||||||
|
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)
|
||||||
|
conn.set_connection_timeout(self.connection_timeout)
|
||||||
|
return conn
|
||||||
|
|
||||||
|
def _open_connection(self, username=None, password=None, database=None,
|
||||||
|
client_flags=None, charset=None):
|
||||||
|
"""Opens the connection
|
||||||
|
|
||||||
|
Open the connection, check the MySQL version, and set the
|
||||||
|
protocol.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.protocol = protocol.MySQLProtocol(self._get_connection())
|
||||||
|
self.protocol.do_handshake()
|
||||||
|
version = self.protocol.server_version
|
||||||
|
if version < (4,1):
|
||||||
|
raise errors.InterfaceError(
|
||||||
|
"MySQL Version %s is not supported." % version)
|
||||||
|
self.protocol.do_auth(username, password, database, client_flags,
|
||||||
|
self._charset)
|
||||||
|
(self._charset, self.charset_name, c) = \
|
||||||
|
constants.CharacterSet.get_charset_info(charset)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def _post_connection(self, time_zone=None, autocommit=False,
|
||||||
|
sql_mode=None, collation=None):
|
||||||
|
"""Post connection session setup
|
||||||
|
|
||||||
|
Should be called after a connection was established"""
|
||||||
|
self.set_converter_class(conversion.MySQLConverter)
|
||||||
|
try:
|
||||||
|
if collation is not None:
|
||||||
|
self.collation = collation
|
||||||
|
self.autocommit = autocommit
|
||||||
|
if time_zone is not None:
|
||||||
|
self.time_zone = time_zone
|
||||||
|
if sql_mode is not None:
|
||||||
|
self.sql_mode = sql_mode
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def is_connected(self):
|
||||||
|
"""
|
||||||
|
Check whether we are connected to the MySQL server.
|
||||||
|
"""
|
||||||
|
return self.protocol.cmd_ping()
|
||||||
|
ping = is_connected
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
"""
|
||||||
|
Disconnect from the MySQL server.
|
||||||
|
"""
|
||||||
|
if not self.protocol:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.protocol.conn.sock is not None:
|
||||||
|
self.protocol.cmd_quit()
|
||||||
|
try:
|
||||||
|
self.protocol.conn.close_connection()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.protocol = None
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
self.converter_class = convclass
|
||||||
|
self.converter = convclass(self.charset_name, self.use_unicode)
|
||||||
|
|
||||||
|
def get_server_version(self):
|
||||||
|
"""Returns the server version as a tuple"""
|
||||||
|
try:
|
||||||
|
return self.protocol.server_version
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_server_info(self):
|
||||||
|
"""Returns the server version as a string"""
|
||||||
|
return self.protocol.server_version_original
|
||||||
|
|
||||||
|
@property
|
||||||
|
def connection_id(self):
|
||||||
|
"""MySQL connection ID"""
|
||||||
|
threadid = None
|
||||||
|
try:
|
||||||
|
threadid = self.protocol.server_threadid
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return threadid
|
||||||
|
|
||||||
|
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.username = username.strip()
|
||||||
|
else:
|
||||||
|
self.username = ''
|
||||||
|
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(self, charset):
|
||||||
|
try:
|
||||||
|
(idx, charset_name, c) = \
|
||||||
|
constants.CharacterSet.get_charset_info(charset)
|
||||||
|
self._execute_query("SET NAMES '%s'" % charset_name)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
self._charset = idx
|
||||||
|
self.charset_name = charset_name
|
||||||
|
self.converter.set_charset(charset_name)
|
||||||
|
def get_charset(self):
|
||||||
|
return self._info_query(
|
||||||
|
"SELECT @@session.character_set_connection")[0]
|
||||||
|
charset = property(get_charset, set_charset,
|
||||||
|
doc="Character set for this connection")
|
||||||
|
|
||||||
|
def set_collation(self, collation):
|
||||||
|
try:
|
||||||
|
self._execute_query(
|
||||||
|
"SET @@session.collation_connection = '%s'" % collation)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
def get_collation(self):
|
||||||
|
return self._info_query(
|
||||||
|
"SELECT @@session.collation_connection")[0]
|
||||||
|
collation = property(get_collation, set_collation,
|
||||||
|
doc="Collation for this connection")
|
||||||
|
|
||||||
|
def set_warnings(self, fetch=False, raise_on_warnings=False):
|
||||||
|
"""Set how to handle warnings coming from MySQL
|
||||||
|
|
||||||
|
Set wheter we should get warnings whenever an operation produced some.
|
||||||
|
If you set raise_on_warnings to True, any warning will be raised
|
||||||
|
as a DataError exception.
|
||||||
|
"""
|
||||||
|
if raise_on_warnings is True:
|
||||||
|
self.get_warnings = True
|
||||||
|
self.raise_on_warnings = True
|
||||||
|
else:
|
||||||
|
self.get_warnings = fetch
|
||||||
|
self.raise_on_warnings = False
|
||||||
|
|
||||||
|
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.
|
||||||
|
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])
|
||||||
|
|
||||||
|
Returns self.client_flags
|
||||||
|
"""
|
||||||
|
if isinstance(flags,int) and flags > 0:
|
||||||
|
self.set_client_flags(flags)
|
||||||
|
else:
|
||||||
|
if isinstance(flags,(tuple,list)):
|
||||||
|
for f in flags:
|
||||||
|
if f < 0:
|
||||||
|
self.unset_client_flag(abs(f))
|
||||||
|
else:
|
||||||
|
self.set_client_flag(f)
|
||||||
|
return self.client_flags
|
||||||
|
|
||||||
|
def set_client_flag(self, flag):
|
||||||
|
if flag > 0:
|
||||||
|
self.client_flags |= flag
|
||||||
|
|
||||||
|
def unset_client_flag(self, flag):
|
||||||
|
if flag > 0:
|
||||||
|
self.client_flags &= ~flag
|
||||||
|
|
||||||
|
def isset_client_flag(self, flag):
|
||||||
|
if (self.client_flags & flag) > 0:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user(self):
|
||||||
|
"""User used while connecting to MySQL"""
|
||||||
|
return self._username
|
||||||
|
|
||||||
|
@property
|
||||||
|
def server_host(self):
|
||||||
|
"""MySQL server IP address or name"""
|
||||||
|
return self._server_host
|
||||||
|
|
||||||
|
@property
|
||||||
|
def server_port(self):
|
||||||
|
"MySQL server TCP/IP port"
|
||||||
|
return self._server_port
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unix_socket(self):
|
||||||
|
"MySQL Unix socket file location"
|
||||||
|
return self._unix_socket
|
||||||
|
|
||||||
|
def set_database(self, value):
|
||||||
|
try:
|
||||||
|
self.protocol.cmd_query("USE %s" % value)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
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):
|
||||||
|
try:
|
||||||
|
self.protocol.cmd_query("SET @@session.time_zone = %s" % value)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
def get_time_zone(self):
|
||||||
|
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):
|
||||||
|
try:
|
||||||
|
self.protocol.cmd_query("SET @@session.sql_mode = %s" % value)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
def get_sql_mode(self):
|
||||||
|
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):
|
||||||
|
try:
|
||||||
|
if value:
|
||||||
|
s = 'ON'
|
||||||
|
else:
|
||||||
|
s = 'OFF'
|
||||||
|
self._execute_query("SET @@session.autocommit = %s" % s)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
def get_autocommit(self):
|
||||||
|
value = self._info_query("SELECT @@session.autocommit")[0]
|
||||||
|
if value == 1:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
autocommit = property(get_autocommit, set_autocommit,
|
||||||
|
doc="autocommit value for current MySQL session")
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
del self.cursors[:]
|
||||||
|
self.disconnect()
|
||||||
|
|
||||||
|
def remove_cursor(self, c):
|
||||||
|
try:
|
||||||
|
self.cursors.remove(c)
|
||||||
|
except ValueError:
|
||||||
|
raise errors.ProgrammingError(
|
||||||
|
"Cursor could not be removed.")
|
||||||
|
|
||||||
|
def cursor(self, buffered=None, raw=None, cursor_class=None):
|
||||||
|
"""Instantiates and returns a cursor
|
||||||
|
|
||||||
|
By default, MySQLCursor is returned. Depending on the options
|
||||||
|
while connecting, a buffered and/or raw cursor instantiated
|
||||||
|
instead.
|
||||||
|
|
||||||
|
It is possible to also give a custom cursor through the
|
||||||
|
cursor_class paramter, but it needs to be a subclass of
|
||||||
|
mysql.connector.cursor.CursorBase.
|
||||||
|
|
||||||
|
Returns a cursor-object
|
||||||
|
"""
|
||||||
|
if cursor_class is not None:
|
||||||
|
if not issubclass(cursor_class, cursor.CursorBase):
|
||||||
|
raise errors.ProgrammingError(
|
||||||
|
"Cursor class needs be subclass of cursor.CursorBase")
|
||||||
|
c = (cursor_class)(self)
|
||||||
|
else:
|
||||||
|
buffered = buffered or self.buffered
|
||||||
|
raw = raw or self.raw
|
||||||
|
|
||||||
|
t = 0
|
||||||
|
if buffered is True:
|
||||||
|
t |= 1
|
||||||
|
if raw is True:
|
||||||
|
t |= 2
|
||||||
|
|
||||||
|
types = {
|
||||||
|
0 : cursor.MySQLCursor,
|
||||||
|
1 : cursor.MySQLCursorBuffered,
|
||||||
|
2 : cursor.MySQLCursorRaw,
|
||||||
|
3 : cursor.MySQLCursorBufferedRaw,
|
||||||
|
}
|
||||||
|
c = (types[t])(self)
|
||||||
|
|
||||||
|
if c not in self.cursors:
|
||||||
|
self.cursors.append(c)
|
||||||
|
return c
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
"""Commit current transaction"""
|
||||||
|
self._execute_query("COMMIT")
|
||||||
|
|
||||||
|
def rollback(self):
|
||||||
|
"""Rollback current transaction"""
|
||||||
|
self._execute_query("ROLLBACK")
|
||||||
|
|
||||||
|
def _execute_query(self, query):
|
||||||
|
if self.unread_result is True:
|
||||||
|
raise errors.InternalError("Unread result found.")
|
||||||
|
|
||||||
|
self.protocol.cmd_query(query)
|
||||||
|
|
||||||
|
def _info_query(self, query):
|
||||||
|
try:
|
||||||
|
cur = self.cursor(buffered=True)
|
||||||
|
cur.execute(query)
|
||||||
|
row = cur.fetchone()
|
||||||
|
cur.close()
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
return row
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# MySQL Connector/Python - MySQL driver written in Python.
|
# MySQL Connector/Python - MySQL driver written in Python.
|
||||||
# Copyright 2009 Sun Microsystems, Inc. All rights reserved
|
# Copyright (c) 2009,2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
# Use is subject to license terms. (See COPYING)
|
# Use is subject to license terms. (See COPYING)
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -26,6 +26,14 @@
|
||||||
|
|
||||||
from errors import ProgrammingError
|
from errors import ProgrammingError
|
||||||
|
|
||||||
|
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):
|
class _constants(object):
|
||||||
|
|
||||||
prefix = ''
|
prefix = ''
|
||||||
|
@ -36,21 +44,20 @@ class _constants(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_desc(cls,name):
|
def get_desc(cls,name):
|
||||||
res = ''
|
|
||||||
try:
|
try:
|
||||||
res = cls.desc[name][1]
|
return cls.desc[name][1]
|
||||||
except KeyError, e:
|
except:
|
||||||
raise KeyError, e
|
return None
|
||||||
else:
|
|
||||||
return res
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_info(cls,n):
|
def get_info(cls,n):
|
||||||
res = ()
|
try:
|
||||||
for k,v in cls.desc.items():
|
res = {}
|
||||||
if v[0] == n:
|
for v in cls.desc.items():
|
||||||
return v[1]
|
res[v[1][0]] = v[0]
|
||||||
raise KeyError, e
|
return res[n]
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_full_info(cls):
|
def get_full_info(cls):
|
||||||
|
@ -62,6 +69,19 @@ class _constants(object):
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
class _constantflags(_constants):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_bit_info(cls, v):
|
||||||
|
"""Get the name of all bits set
|
||||||
|
|
||||||
|
Returns a list of strings."""
|
||||||
|
res = []
|
||||||
|
for name,d in cls.desc.items():
|
||||||
|
if v & d[0]:
|
||||||
|
res.append(name)
|
||||||
|
return res
|
||||||
|
|
||||||
class FieldType(_constants):
|
class FieldType(_constants):
|
||||||
|
|
||||||
prefix = 'FIELD_TYPE_'
|
prefix = 'FIELD_TYPE_'
|
||||||
|
@ -155,7 +175,7 @@ class FieldType(_constants):
|
||||||
cls.DATETIME, cls.TIMESTAMP,
|
cls.DATETIME, cls.TIMESTAMP,
|
||||||
]
|
]
|
||||||
|
|
||||||
class FieldFlag(_constants):
|
class FieldFlag(_constantflags):
|
||||||
"""
|
"""
|
||||||
Field flags as found in MySQL sources mysql-src/include/mysql_com.h
|
Field flags as found in MySQL sources mysql-src/include/mysql_com.h
|
||||||
"""
|
"""
|
||||||
|
@ -214,7 +234,6 @@ class FieldFlag(_constants):
|
||||||
'FIELD_IS_RENAMED': (1 << 21, "Intern: Field is being renamed"),
|
'FIELD_IS_RENAMED': (1 << 21, "Intern: Field is being renamed"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ServerCmd(_constants):
|
class ServerCmd(_constants):
|
||||||
_prefix = 'COM_'
|
_prefix = 'COM_'
|
||||||
SLEEP = 0
|
SLEEP = 0
|
||||||
|
@ -248,7 +267,7 @@ class ServerCmd(_constants):
|
||||||
STMT_FETCH = 28
|
STMT_FETCH = 28
|
||||||
DAEMON = 29
|
DAEMON = 29
|
||||||
|
|
||||||
class ClientFlag(_constants):
|
class ClientFlag(_constantflags):
|
||||||
"""
|
"""
|
||||||
Client Options as found in the MySQL sources mysql-src/include/mysql_com.h
|
Client Options as found in the MySQL sources mysql-src/include/mysql_com.h
|
||||||
"""
|
"""
|
||||||
|
@ -314,7 +333,7 @@ class ClientFlag(_constants):
|
||||||
flags |= f
|
flags |= f
|
||||||
return flags
|
return flags
|
||||||
|
|
||||||
class ServerFlag(_constants):
|
class ServerFlag(_constantflags):
|
||||||
"""
|
"""
|
||||||
Server flags as found in the MySQL sources mysql-src/include/mysql_com.h
|
Server flags as found in the MySQL sources mysql-src/include/mysql_com.h
|
||||||
"""
|
"""
|
||||||
|
@ -364,204 +383,330 @@ class RefreshOption(_constants):
|
||||||
}
|
}
|
||||||
|
|
||||||
class CharacterSet(_constants):
|
class CharacterSet(_constants):
|
||||||
"""
|
"""MySQL supported character sets and collations
|
||||||
List of supported character sets with their collations. This maps to the
|
|
||||||
character set we get from the server within the handshake packet.
|
|
||||||
|
|
||||||
To update this list, use the following query:
|
List of character sets with their collations supported by MySQL. This
|
||||||
SELECT ID,CHARACTER_SET_NAME, COLLATION_NAME
|
maps to the character set we get from the server within the handshake
|
||||||
FROM INFORMATION_SCHEMA.COLLATIONS
|
packet.
|
||||||
ORDER BY ID
|
|
||||||
|
|
||||||
This list is hardcoded because we want to avoid doing each time the above
|
The list is hardcode so we avoid a database query when getting the
|
||||||
query to get the name of the character set used.
|
name of the used character set or collation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_max_id = 211 # SELECT MAX(ID)+1 FROM INFORMATION_SCHEMA.COLLATIONS
|
desc = [
|
||||||
|
# (character set name, collation, default)
|
||||||
@classmethod
|
None,
|
||||||
def _init_desc(cls):
|
("big5","big5_chinese_ci",True), # 1
|
||||||
if not cls.__dict__.has_key('desc'):
|
("latin2","latin2_czech_cs",False), # 2
|
||||||
|
("dec8","dec8_swedish_ci",True), # 3
|
||||||
# Do not forget to update the tests in test_constants!
|
("cp850","cp850_general_ci",True), # 4
|
||||||
cls.desc = [ None for i in range(cls._max_id)]
|
("latin1","latin1_german1_ci",False), # 5
|
||||||
cls.desc[1] = ('big5','big5_chinese_ci')
|
("hp8","hp8_english_ci",True), # 6
|
||||||
cls.desc[2] = ('latin2','latin2_czech_cs')
|
("koi8r","koi8r_general_ci",True), # 7
|
||||||
cls.desc[3] = ('dec8','dec8_swedish_ci')
|
("latin1","latin1_swedish_ci",True), # 8
|
||||||
cls.desc[4] = ('cp850','cp850_general_ci')
|
("latin2","latin2_general_ci",True), # 9
|
||||||
cls.desc[5] = ('latin1','latin1_german1_ci')
|
("swe7","swe7_swedish_ci",True), # 10
|
||||||
cls.desc[6] = ('hp8','hp8_english_ci')
|
("ascii","ascii_general_ci",True), # 11
|
||||||
cls.desc[7] = ('koi8r','koi8r_general_ci')
|
("ujis","ujis_japanese_ci",True), # 12
|
||||||
cls.desc[8] = ('latin1','latin1_swedish_ci')
|
("sjis","sjis_japanese_ci",True), # 13
|
||||||
cls.desc[9] = ('latin2','latin2_general_ci')
|
("cp1251","cp1251_bulgarian_ci",False), # 14
|
||||||
cls.desc[10] = ('swe7','swe7_swedish_ci')
|
("latin1","latin1_danish_ci",False), # 15
|
||||||
cls.desc[11] = ('ascii','ascii_general_ci')
|
("hebrew","hebrew_general_ci",True), # 16
|
||||||
cls.desc[12] = ('ujis','ujis_japanese_ci')
|
None,
|
||||||
cls.desc[13] = ('sjis','sjis_japanese_ci')
|
("tis620","tis620_thai_ci",True), # 18
|
||||||
cls.desc[14] = ('cp1251','cp1251_bulgarian_ci')
|
("euckr","euckr_korean_ci",True), # 19
|
||||||
cls.desc[15] = ('latin1','latin1_danish_ci')
|
("latin7","latin7_estonian_cs",False), # 20
|
||||||
cls.desc[16] = ('hebrew','hebrew_general_ci')
|
("latin2","latin2_hungarian_ci",False), # 21
|
||||||
cls.desc[18] = ('tis620','tis620_thai_ci')
|
("koi8u","koi8u_general_ci",True), # 22
|
||||||
cls.desc[19] = ('euckr','euckr_korean_ci')
|
("cp1251","cp1251_ukrainian_ci",False), # 23
|
||||||
cls.desc[20] = ('latin7','latin7_estonian_cs')
|
("gb2312","gb2312_chinese_ci",True), # 24
|
||||||
cls.desc[21] = ('latin2','latin2_hungarian_ci')
|
("greek","greek_general_ci",True), # 25
|
||||||
cls.desc[22] = ('koi8u','koi8u_general_ci')
|
("cp1250","cp1250_general_ci",True), # 26
|
||||||
cls.desc[23] = ('cp1251','cp1251_ukrainian_ci')
|
("latin2","latin2_croatian_ci",False), # 27
|
||||||
cls.desc[24] = ('gb2312','gb2312_chinese_ci')
|
("gbk","gbk_chinese_ci",True), # 28
|
||||||
cls.desc[25] = ('greek','greek_general_ci')
|
("cp1257","cp1257_lithuanian_ci",False), # 29
|
||||||
cls.desc[26] = ('cp1250','cp1250_general_ci')
|
("latin5","latin5_turkish_ci",True), # 30
|
||||||
cls.desc[27] = ('latin2','latin2_croatian_ci')
|
("latin1","latin1_german2_ci",False), # 31
|
||||||
cls.desc[28] = ('gbk','gbk_chinese_ci')
|
("armscii8","armscii8_general_ci",True), # 32
|
||||||
cls.desc[29] = ('cp1257','cp1257_lithuanian_ci')
|
("utf8","utf8_general_ci",True), # 33
|
||||||
cls.desc[30] = ('latin5','latin5_turkish_ci')
|
("cp1250","cp1250_czech_cs",False), # 34
|
||||||
cls.desc[31] = ('latin1','latin1_german2_ci')
|
("ucs2","ucs2_general_ci",True), # 35
|
||||||
cls.desc[32] = ('armscii8','armscii8_general_ci')
|
("cp866","cp866_general_ci",True), # 36
|
||||||
cls.desc[33] = ('utf8','utf8_general_ci')
|
("keybcs2","keybcs2_general_ci",True), # 37
|
||||||
cls.desc[34] = ('cp1250','cp1250_czech_cs')
|
("macce","macce_general_ci",True), # 38
|
||||||
cls.desc[35] = ('ucs2','ucs2_general_ci')
|
("macroman","macroman_general_ci",True), # 39
|
||||||
cls.desc[36] = ('cp866','cp866_general_ci')
|
("cp852","cp852_general_ci",True), # 40
|
||||||
cls.desc[37] = ('keybcs2','keybcs2_general_ci')
|
("latin7","latin7_general_ci",True), # 41
|
||||||
cls.desc[38] = ('macce','macce_general_ci')
|
("latin7","latin7_general_cs",False), # 42
|
||||||
cls.desc[39] = ('macroman','macroman_general_ci')
|
("macce","macce_bin",False), # 43
|
||||||
cls.desc[40] = ('cp852','cp852_general_ci')
|
("cp1250","cp1250_croatian_ci",False), # 44
|
||||||
cls.desc[41] = ('latin7','latin7_general_ci')
|
None,
|
||||||
cls.desc[42] = ('latin7','latin7_general_cs')
|
None,
|
||||||
cls.desc[43] = ('macce','macce_bin')
|
("latin1","latin1_bin",False), # 47
|
||||||
cls.desc[44] = ('cp1250','cp1250_croatian_ci')
|
("latin1","latin1_general_ci",False), # 48
|
||||||
cls.desc[47] = ('latin1','latin1_bin')
|
("latin1","latin1_general_cs",False), # 49
|
||||||
cls.desc[48] = ('latin1','latin1_general_ci')
|
("cp1251","cp1251_bin",False), # 50
|
||||||
cls.desc[49] = ('latin1','latin1_general_cs')
|
("cp1251","cp1251_general_ci",True), # 51
|
||||||
cls.desc[50] = ('cp1251','cp1251_bin')
|
("cp1251","cp1251_general_cs",False), # 52
|
||||||
cls.desc[51] = ('cp1251','cp1251_general_ci')
|
("macroman","macroman_bin",False), # 53
|
||||||
cls.desc[52] = ('cp1251','cp1251_general_cs')
|
None,
|
||||||
cls.desc[53] = ('macroman','macroman_bin')
|
None,
|
||||||
cls.desc[57] = ('cp1256','cp1256_general_ci')
|
None,
|
||||||
cls.desc[58] = ('cp1257','cp1257_bin')
|
("cp1256","cp1256_general_ci",True), # 57
|
||||||
cls.desc[59] = ('cp1257','cp1257_general_ci')
|
("cp1257","cp1257_bin",False), # 58
|
||||||
cls.desc[63] = ('binary','binary')
|
("cp1257","cp1257_general_ci",True), # 59
|
||||||
cls.desc[64] = ('armscii8','armscii8_bin')
|
None,
|
||||||
cls.desc[65] = ('ascii','ascii_bin')
|
None,
|
||||||
cls.desc[66] = ('cp1250','cp1250_bin')
|
None,
|
||||||
cls.desc[67] = ('cp1256','cp1256_bin')
|
("binary","binary",True), # 63
|
||||||
cls.desc[68] = ('cp866','cp866_bin')
|
("armscii8","armscii8_bin",False), # 64
|
||||||
cls.desc[69] = ('dec8','dec8_bin')
|
("ascii","ascii_bin",False), # 65
|
||||||
cls.desc[70] = ('greek','greek_bin')
|
("cp1250","cp1250_bin",False), # 66
|
||||||
cls.desc[71] = ('hebrew','hebrew_bin')
|
("cp1256","cp1256_bin",False), # 67
|
||||||
cls.desc[72] = ('hp8','hp8_bin')
|
("cp866","cp866_bin",False), # 68
|
||||||
cls.desc[73] = ('keybcs2','keybcs2_bin')
|
("dec8","dec8_bin",False), # 69
|
||||||
cls.desc[74] = ('koi8r','koi8r_bin')
|
("greek","greek_bin",False), # 70
|
||||||
cls.desc[75] = ('koi8u','koi8u_bin')
|
("hebrew","hebrew_bin",False), # 71
|
||||||
cls.desc[77] = ('latin2','latin2_bin')
|
("hp8","hp8_bin",False), # 72
|
||||||
cls.desc[78] = ('latin5','latin5_bin')
|
("keybcs2","keybcs2_bin",False), # 73
|
||||||
cls.desc[79] = ('latin7','latin7_bin')
|
("koi8r","koi8r_bin",False), # 74
|
||||||
cls.desc[80] = ('cp850','cp850_bin')
|
("koi8u","koi8u_bin",False), # 75
|
||||||
cls.desc[81] = ('cp852','cp852_bin')
|
None,
|
||||||
cls.desc[82] = ('swe7','swe7_bin')
|
("latin2","latin2_bin",False), # 77
|
||||||
cls.desc[83] = ('utf8','utf8_bin')
|
("latin5","latin5_bin",False), # 78
|
||||||
cls.desc[84] = ('big5','big5_bin')
|
("latin7","latin7_bin",False), # 79
|
||||||
cls.desc[85] = ('euckr','euckr_bin')
|
("cp850","cp850_bin",False), # 80
|
||||||
cls.desc[86] = ('gb2312','gb2312_bin')
|
("cp852","cp852_bin",False), # 81
|
||||||
cls.desc[87] = ('gbk','gbk_bin')
|
("swe7","swe7_bin",False), # 82
|
||||||
cls.desc[88] = ('sjis','sjis_bin')
|
("utf8","utf8_bin",False), # 83
|
||||||
cls.desc[89] = ('tis620','tis620_bin')
|
("big5","big5_bin",False), # 84
|
||||||
cls.desc[90] = ('ucs2','ucs2_bin')
|
("euckr","euckr_bin",False), # 85
|
||||||
cls.desc[91] = ('ujis','ujis_bin')
|
("gb2312","gb2312_bin",False), # 86
|
||||||
cls.desc[92] = ('geostd8','geostd8_general_ci')
|
("gbk","gbk_bin",False), # 87
|
||||||
cls.desc[93] = ('geostd8','geostd8_bin')
|
("sjis","sjis_bin",False), # 88
|
||||||
cls.desc[94] = ('latin1','latin1_spanish_ci')
|
("tis620","tis620_bin",False), # 89
|
||||||
cls.desc[95] = ('cp932','cp932_japanese_ci')
|
("ucs2","ucs2_bin",False), # 90
|
||||||
cls.desc[96] = ('cp932','cp932_bin')
|
("ujis","ujis_bin",False), # 91
|
||||||
cls.desc[97] = ('eucjpms','eucjpms_japanese_ci')
|
("geostd8","geostd8_general_ci",True), # 92
|
||||||
cls.desc[98] = ('eucjpms','eucjpms_bin')
|
("geostd8","geostd8_bin",False), # 93
|
||||||
cls.desc[128] = ('ucs2','ucs2_unicode_ci')
|
("latin1","latin1_spanish_ci",False), # 94
|
||||||
cls.desc[129] = ('ucs2','ucs2_icelandic_ci')
|
("cp932","cp932_japanese_ci",True), # 95
|
||||||
cls.desc[130] = ('ucs2','ucs2_latvian_ci')
|
("cp932","cp932_bin",False), # 96
|
||||||
cls.desc[131] = ('ucs2','ucs2_romanian_ci')
|
("eucjpms","eucjpms_japanese_ci",True), # 97
|
||||||
cls.desc[132] = ('ucs2','ucs2_slovenian_ci')
|
("eucjpms","eucjpms_bin",False), # 98
|
||||||
cls.desc[133] = ('ucs2','ucs2_polish_ci')
|
("cp1250","cp1250_polish_ci",False), # 99
|
||||||
cls.desc[134] = ('ucs2','ucs2_estonian_ci')
|
None,
|
||||||
cls.desc[135] = ('ucs2','ucs2_spanish_ci')
|
None,
|
||||||
cls.desc[136] = ('ucs2','ucs2_swedish_ci')
|
None,
|
||||||
cls.desc[137] = ('ucs2','ucs2_turkish_ci')
|
None,
|
||||||
cls.desc[138] = ('ucs2','ucs2_czech_ci')
|
None,
|
||||||
cls.desc[139] = ('ucs2','ucs2_danish_ci')
|
None,
|
||||||
cls.desc[140] = ('ucs2','ucs2_lithuanian_ci')
|
None,
|
||||||
cls.desc[141] = ('ucs2','ucs2_slovak_ci')
|
None,
|
||||||
cls.desc[142] = ('ucs2','ucs2_spanish2_ci')
|
None,
|
||||||
cls.desc[143] = ('ucs2','ucs2_roman_ci')
|
None,
|
||||||
cls.desc[144] = ('ucs2','ucs2_persian_ci')
|
None,
|
||||||
cls.desc[145] = ('ucs2','ucs2_esperanto_ci')
|
None,
|
||||||
cls.desc[146] = ('ucs2','ucs2_hungarian_ci')
|
None,
|
||||||
cls.desc[192] = ('utf8','utf8_unicode_ci')
|
None,
|
||||||
cls.desc[193] = ('utf8','utf8_icelandic_ci')
|
None,
|
||||||
cls.desc[194] = ('utf8','utf8_latvian_ci')
|
None,
|
||||||
cls.desc[195] = ('utf8','utf8_romanian_ci')
|
None,
|
||||||
cls.desc[196] = ('utf8','utf8_slovenian_ci')
|
None,
|
||||||
cls.desc[197] = ('utf8','utf8_polish_ci')
|
None,
|
||||||
cls.desc[198] = ('utf8','utf8_estonian_ci')
|
None,
|
||||||
cls.desc[199] = ('utf8','utf8_spanish_ci')
|
None,
|
||||||
cls.desc[200] = ('utf8','utf8_swedish_ci')
|
None,
|
||||||
cls.desc[201] = ('utf8','utf8_turkish_ci')
|
None,
|
||||||
cls.desc[202] = ('utf8','utf8_czech_ci')
|
None,
|
||||||
cls.desc[203] = ('utf8','utf8_danish_ci')
|
None,
|
||||||
cls.desc[204] = ('utf8','utf8_lithuanian_ci')
|
None,
|
||||||
cls.desc[205] = ('utf8','utf8_slovak_ci')
|
None,
|
||||||
cls.desc[206] = ('utf8','utf8_spanish2_ci')
|
None,
|
||||||
cls.desc[207] = ('utf8','utf8_roman_ci')
|
("ucs2","ucs2_unicode_ci",False), # 128
|
||||||
cls.desc[208] = ('utf8','utf8_persian_ci')
|
("ucs2","ucs2_icelandic_ci",False), # 129
|
||||||
cls.desc[209] = ('utf8','utf8_esperanto_ci')
|
("ucs2","ucs2_latvian_ci",False), # 130
|
||||||
cls.desc[210] = ('utf8','utf8_hungarian_ci')
|
("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
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
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
|
||||||
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_info(cls,setid):
|
def get_info(cls,setid):
|
||||||
"""Returns information about the charset for given MySQL ID."""
|
"""Retrieves character set information as tuple using an ID
|
||||||
cls._init_desc()
|
|
||||||
res = ()
|
Retrieves character set and collation information based on the
|
||||||
errmsg = "Character set with id '%d' unsupported." % (setid)
|
given MySQL ID.
|
||||||
|
|
||||||
|
Returns a tuple.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
res = cls.desc[setid]
|
r = cls.desc[setid]
|
||||||
|
if r is None:
|
||||||
|
raise
|
||||||
|
return r[0:2]
|
||||||
except:
|
except:
|
||||||
raise ProgrammingError, errmsg
|
raise ProgrammingError("Character set '%d' unsupported" % (setid))
|
||||||
|
|
||||||
if res is None:
|
|
||||||
raise ProgrammingError, errmsg
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_desc(cls,setid):
|
def get_desc(cls,setid):
|
||||||
"""Returns info string about the charset for given MySQL ID."""
|
"""Retrieves character set information as string using an ID
|
||||||
res = ()
|
|
||||||
|
Retrieves character set and collation information based on the
|
||||||
|
given MySQL ID.
|
||||||
|
|
||||||
|
Returns a tuple.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
res = "%s/%s" % self.get_info(setid)
|
return "%s/%s" % cls.get_info(setid)
|
||||||
except ProgrammingError, e:
|
except:
|
||||||
raise
|
raise
|
||||||
else:
|
|
||||||
return res
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_charset_info(cls, name, collation=None):
|
def get_default_collation(cls, charset):
|
||||||
"""Returns information about the charset and optional collation."""
|
"""Retrieves the default collation for given character set
|
||||||
cls._init_desc()
|
|
||||||
l = len(cls.desc)
|
Raises ProgrammingError when character set is not supported.
|
||||||
errmsg = "Character set '%s' unsupported." % (name)
|
|
||||||
|
Returns list (collation, charset, index)
|
||||||
|
"""
|
||||||
|
if isinstance(charset, int):
|
||||||
|
try:
|
||||||
|
c = cls.desc[charset]
|
||||||
|
return c[1], c[0], charset
|
||||||
|
except:
|
||||||
|
ProgrammingError("Character set ID '%s' unsupported." % (
|
||||||
|
charset))
|
||||||
|
|
||||||
|
for cid, c in enumerate(cls.desc):
|
||||||
|
if c is None:
|
||||||
|
continue
|
||||||
|
if c[0] == charset and c[2] is True:
|
||||||
|
return c[1], c[0], cid
|
||||||
|
|
||||||
|
raise ProgrammingError("Character set '%s' unsupported." % (charset))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_charset_info(cls, charset, collation=None):
|
||||||
|
"""Retrieves character set information as tuple using a name
|
||||||
|
|
||||||
|
Retrieves character set and collation information based on the
|
||||||
|
given a valid name. If charset is an integer, it will look up
|
||||||
|
the character set based on the MySQL's ID.
|
||||||
|
|
||||||
|
Raises ProgrammingError when character set is not supported.
|
||||||
|
|
||||||
|
Returns a tuple.
|
||||||
|
"""
|
||||||
|
idx = None
|
||||||
|
|
||||||
|
if isinstance(charset, int):
|
||||||
|
try:
|
||||||
|
c = cls.desc[charset]
|
||||||
|
return charset, c[0], c[1]
|
||||||
|
except:
|
||||||
|
ProgrammingError("Character set ID '%s' unsupported." % (
|
||||||
|
charset))
|
||||||
|
|
||||||
if collation is None:
|
if collation is None:
|
||||||
collation = '%s_general_ci' % (name)
|
collation, charset, idx = cls.get_default_collation(charset)
|
||||||
|
else:
|
||||||
|
for cid, c in enumerate(cls.desc):
|
||||||
|
if c is None:
|
||||||
|
continue
|
||||||
|
if c[0] == charset and c[1] == collation:
|
||||||
|
idx = cid
|
||||||
|
break
|
||||||
|
|
||||||
# Search the list and return when found
|
if idx is not None:
|
||||||
idx = 0
|
return (idx,charset,collation)
|
||||||
for info in cls.desc:
|
else:
|
||||||
if info and info[0] == name and info[1] == collation:
|
raise ProgrammingError("Character set '%s' unsupported." % (
|
||||||
return (idx,info[0],info[1])
|
charset))
|
||||||
idx += 1
|
|
||||||
|
|
||||||
# If we got here, we didn't find the charset
|
|
||||||
raise ProgrammingError, errmsg
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_supported(cls):
|
def get_supported(cls):
|
||||||
"""Returns a list with names of all supproted character sets."""
|
"""Retrieves a list with names of all supproted character sets
|
||||||
|
|
||||||
|
Returns a tuple.
|
||||||
|
"""
|
||||||
res = []
|
res = []
|
||||||
for info in cls.desc:
|
for info in cls.desc:
|
||||||
if info and info[0] not in res:
|
if info and info[0] not in res:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# MySQL Connector/Python - MySQL driver written in Python.
|
# MySQL Connector/Python - MySQL driver written in Python.
|
||||||
# Copyright 2009 Sun Microsystems, Inc. All rights reserved
|
# Copyright (c) 2009,2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
# Use is subject to license terms. (See COPYING)
|
# Use is subject to license terms. (See COPYING)
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -24,8 +24,7 @@
|
||||||
"""Converting MySQL and Python types
|
"""Converting MySQL and Python types
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from types import NoneType
|
import struct
|
||||||
import re
|
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
@ -80,7 +79,6 @@ class MySQLConverter(ConverterBase):
|
||||||
def __init__(self, charset=None, use_unicode=True):
|
def __init__(self, charset=None, use_unicode=True):
|
||||||
ConverterBase.__init__(self, charset, use_unicode)
|
ConverterBase.__init__(self, charset, use_unicode)
|
||||||
|
|
||||||
# Python types
|
|
||||||
self.python_types = {
|
self.python_types = {
|
||||||
int : int,
|
int : int,
|
||||||
str : self._str_to_mysql,
|
str : self._str_to_mysql,
|
||||||
|
@ -88,7 +86,7 @@ class MySQLConverter(ConverterBase):
|
||||||
float : float,
|
float : float,
|
||||||
unicode : self._unicode_to_mysql,
|
unicode : self._unicode_to_mysql,
|
||||||
bool : self._bool_to_mysql,
|
bool : self._bool_to_mysql,
|
||||||
NoneType : self._none_to_mysql,
|
type(None) : self._none_to_mysql,
|
||||||
datetime.datetime : self._datetime_to_mysql,
|
datetime.datetime : self._datetime_to_mysql,
|
||||||
datetime.date : self._date_to_mysql,
|
datetime.date : self._date_to_mysql,
|
||||||
datetime.time : self._time_to_mysql,
|
datetime.time : self._time_to_mysql,
|
||||||
|
@ -97,7 +95,6 @@ class MySQLConverter(ConverterBase):
|
||||||
Decimal : self._decimal_to_mysql,
|
Decimal : self._decimal_to_mysql,
|
||||||
}
|
}
|
||||||
|
|
||||||
# MySQL types
|
|
||||||
self.mysql_types = {
|
self.mysql_types = {
|
||||||
FieldType.TINY : self._int,
|
FieldType.TINY : self._int,
|
||||||
FieldType.SHORT : self._int,
|
FieldType.SHORT : self._int,
|
||||||
|
@ -116,7 +113,9 @@ class MySQLConverter(ConverterBase):
|
||||||
FieldType.NEWDATE : self._DATE_to_python,
|
FieldType.NEWDATE : self._DATE_to_python,
|
||||||
FieldType.DATETIME : self._DATETIME_to_python,
|
FieldType.DATETIME : self._DATETIME_to_python,
|
||||||
FieldType.TIMESTAMP : self._DATETIME_to_python,
|
FieldType.TIMESTAMP : self._DATETIME_to_python,
|
||||||
FieldType.BLOB : self._STRING_to_python,
|
FieldType.BLOB : self._BLOB_to_python,
|
||||||
|
FieldType.YEAR: self._YEAR_to_python,
|
||||||
|
FieldType.BIT: self._BIT_to_python,
|
||||||
}
|
}
|
||||||
|
|
||||||
def escape(self, value):
|
def escape(self, value):
|
||||||
|
@ -131,9 +130,8 @@ class MySQLConverter(ConverterBase):
|
||||||
return value
|
return value
|
||||||
elif isinstance(value, (int,float,long,Decimal)):
|
elif isinstance(value, (int,float,long,Decimal)):
|
||||||
return value
|
return value
|
||||||
backslash = re.compile(r'\134')
|
|
||||||
res = value
|
res = value
|
||||||
res = backslash.sub(r'\\\\', res)
|
res = res.replace('\\','\\\\')
|
||||||
res = res.replace('\n','\\n')
|
res = res.replace('\n','\\n')
|
||||||
res = res.replace('\r','\\r')
|
res = res.replace('\r','\\r')
|
||||||
res = res.replace('\047','\134\047') # single quotes
|
res = res.replace('\047','\134\047') # single quotes
|
||||||
|
@ -152,7 +150,7 @@ class MySQLConverter(ConverterBase):
|
||||||
"""
|
"""
|
||||||
if isinstance(buf, (int,float,long,Decimal)):
|
if isinstance(buf, (int,float,long,Decimal)):
|
||||||
return str(buf)
|
return str(buf)
|
||||||
elif isinstance(buf, NoneType):
|
elif isinstance(buf, type(None)):
|
||||||
return "NULL"
|
return "NULL"
|
||||||
else:
|
else:
|
||||||
# Anything else would be a string
|
# Anything else would be a string
|
||||||
|
@ -195,12 +193,11 @@ class MySQLConverter(ConverterBase):
|
||||||
|
|
||||||
If the instance isn't a datetime.datetime type, it return None.
|
If the instance isn't a datetime.datetime type, it return None.
|
||||||
|
|
||||||
Returns a string or None when not valid.
|
Returns a string.
|
||||||
"""
|
"""
|
||||||
if isinstance(value, datetime.datetime):
|
return '%d-%02d-%02d %02d:%02d:%02d' % (
|
||||||
return value.strftime('%Y-%m-%d %H:%M:%S')
|
value.year, value.month, value.day,
|
||||||
|
value.hour, value.minute, value.second)
|
||||||
return None
|
|
||||||
|
|
||||||
def _date_to_mysql(self, value):
|
def _date_to_mysql(self, value):
|
||||||
"""
|
"""
|
||||||
|
@ -209,13 +206,9 @@ class MySQLConverter(ConverterBase):
|
||||||
|
|
||||||
If the instance isn't a datetime.date type, it return None.
|
If the instance isn't a datetime.date type, it return None.
|
||||||
|
|
||||||
Returns a string or None when not valid.
|
Returns a string.
|
||||||
"""
|
"""
|
||||||
if isinstance(value, datetime.date):
|
return '%d-%02d-%02d' % (value.year, value.month, value.day)
|
||||||
return value.strftime('%Y-%m-%d')
|
|
||||||
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _time_to_mysql(self, value):
|
def _time_to_mysql(self, value):
|
||||||
"""
|
"""
|
||||||
|
@ -226,11 +219,8 @@ class MySQLConverter(ConverterBase):
|
||||||
|
|
||||||
Returns a string or None when not valid.
|
Returns a string or None when not valid.
|
||||||
"""
|
"""
|
||||||
if isinstance(value, datetime.time):
|
|
||||||
return value.strftime('%H:%M:%S')
|
return value.strftime('%H:%M:%S')
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _struct_time_to_mysql(self, value):
|
def _struct_time_to_mysql(self, value):
|
||||||
"""
|
"""
|
||||||
Converts a time.struct_time sequence to a string suitable
|
Converts a time.struct_time sequence to a string suitable
|
||||||
|
@ -239,24 +229,19 @@ class MySQLConverter(ConverterBase):
|
||||||
|
|
||||||
Returns a string or None when not valid.
|
Returns a string or None when not valid.
|
||||||
"""
|
"""
|
||||||
if isinstance(value, time.struct_time):
|
|
||||||
return time.strftime('%Y-%m-%d %H:%M:%S',value)
|
return time.strftime('%Y-%m-%d %H:%M:%S',value)
|
||||||
return None
|
|
||||||
|
|
||||||
def _timedelta_to_mysql(self, value):
|
def _timedelta_to_mysql(self, value):
|
||||||
"""
|
"""
|
||||||
Converts a timedelta instance to a string suitable for MySQL.
|
Converts a timedelta instance to a string suitable for MySQL.
|
||||||
The returned string has format: %H:%M:%S
|
The returned string has format: %H:%M:%S
|
||||||
|
|
||||||
Returns a string or None when not valid.
|
Returns a string.
|
||||||
"""
|
"""
|
||||||
if isinstance(value, datetime.timedelta):
|
(hours, r) = divmod(value.seconds, 3600)
|
||||||
secs = value.seconds%60
|
(mins, secs) = divmod(r, 60)
|
||||||
mins = value.seconds%3600/60
|
hours = hours + (value.days * 24)
|
||||||
hours = value.seconds/3600+(value.days*24)
|
return '%02d:%02d:%02d' % (hours,mins,secs)
|
||||||
return '%d:%02d:%02d' % (hours,mins,secs)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _decimal_to_mysql(self, value):
|
def _decimal_to_mysql(self, value):
|
||||||
"""
|
"""
|
||||||
|
@ -280,7 +265,7 @@ class MySQLConverter(ConverterBase):
|
||||||
"""
|
"""
|
||||||
res = value
|
res = value
|
||||||
|
|
||||||
if value == '\x00':
|
if value == '\x00' and flddsc[1] != FieldType.BIT:
|
||||||
# Don't go further when we hit a NULL value
|
# Don't go further when we hit a NULL value
|
||||||
return None
|
return None
|
||||||
if value is None:
|
if value is None:
|
||||||
|
@ -316,7 +301,7 @@ class MySQLConverter(ConverterBase):
|
||||||
"""
|
"""
|
||||||
Returns v as long type.
|
Returns v as long type.
|
||||||
"""
|
"""
|
||||||
return long(v)
|
return int(v)
|
||||||
|
|
||||||
def _decimal(self, v, desc=None):
|
def _decimal(self, v, desc=None):
|
||||||
"""
|
"""
|
||||||
|
@ -330,6 +315,13 @@ class MySQLConverter(ConverterBase):
|
||||||
"""
|
"""
|
||||||
return str(v)
|
return str(v)
|
||||||
|
|
||||||
|
def _BIT_to_python(self, v, dsc=None):
|
||||||
|
"""Returns BIT columntype as integer"""
|
||||||
|
s = v
|
||||||
|
if len(s) < 8:
|
||||||
|
s = '\x00'*(8-len(s)) + s
|
||||||
|
return struct.unpack('>Q', s)[0]
|
||||||
|
|
||||||
def _DATE_to_python(self, v, dsc=None):
|
def _DATE_to_python(self, v, dsc=None):
|
||||||
"""
|
"""
|
||||||
Returns DATE column type as datetime.date type.
|
Returns DATE column type as datetime.date type.
|
||||||
|
@ -361,24 +353,37 @@ class MySQLConverter(ConverterBase):
|
||||||
"""
|
"""
|
||||||
pv = None
|
pv = None
|
||||||
try:
|
try:
|
||||||
pv = datetime.datetime(*time.strptime(v, "%Y-%m-%d %H:%M:%S")[0:6])
|
(sd,st) = v.split(' ')
|
||||||
|
dt = [ int(v) for v in sd.split('-') ] +\
|
||||||
|
[ int(v) for v in st.split(':') ]
|
||||||
|
pv = datetime.datetime(*dt)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pv = None
|
pv = None
|
||||||
|
|
||||||
return pv
|
return pv
|
||||||
|
|
||||||
|
def _YEAR_to_python(self, v, desc=None):
|
||||||
|
"""Returns YEAR column type as integer"""
|
||||||
|
try:
|
||||||
|
year = int(v)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError("Failed converting YEAR to int (%s)" % v)
|
||||||
|
|
||||||
|
return year
|
||||||
|
|
||||||
def _SET_to_python(self, v, dsc=None):
|
def _SET_to_python(self, v, dsc=None):
|
||||||
"""
|
"""Returns SET column typs as set
|
||||||
|
|
||||||
Actually, MySQL protocol sees a SET as a string type field. So this
|
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.
|
code isn't called directly, but used by STRING_to_python() method.
|
||||||
|
|
||||||
Returns SET column type as string splitted using a comma.
|
Returns SET column type as a set.
|
||||||
"""
|
"""
|
||||||
pv = None
|
pv = None
|
||||||
try:
|
try:
|
||||||
pv = v.split(',')
|
pv = set(v.split(','))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValueError, "Could not convert set %s to a sequence." % v
|
raise ValueError, "Could not convert SET %s to a set." % v
|
||||||
return pv
|
return pv
|
||||||
|
|
||||||
def _STRING_to_python(self, v, dsc=None):
|
def _STRING_to_python(self, v, dsc=None):
|
||||||
|
@ -392,6 +397,8 @@ class MySQLConverter(ConverterBase):
|
||||||
# Check if we deal with a SET
|
# Check if we deal with a SET
|
||||||
if dsc[7] & FieldFlag.SET:
|
if dsc[7] & FieldFlag.SET:
|
||||||
return self._SET_to_python(v, dsc)
|
return self._SET_to_python(v, dsc)
|
||||||
|
if dsc[7] & FieldFlag.BINARY:
|
||||||
|
return v
|
||||||
|
|
||||||
if self.use_unicode:
|
if self.use_unicode:
|
||||||
try:
|
try:
|
||||||
|
@ -399,3 +406,11 @@ class MySQLConverter(ConverterBase):
|
||||||
except:
|
except:
|
||||||
raise
|
raise
|
||||||
return str(v)
|
return str(v)
|
||||||
|
|
||||||
|
def _BLOB_to_python(self, v, dsc=None):
|
||||||
|
if dsc is not None:
|
||||||
|
if dsc[7] & FieldFlag.BINARY:
|
||||||
|
return v
|
||||||
|
|
||||||
|
return self._STRING_to_python(v, dsc)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# MySQL Connector/Python - MySQL driver written in Python.
|
# MySQL Connector/Python - MySQL driver written in Python.
|
||||||
# Copyright 2009 Sun Microsystems, Inc. All rights reserved
|
# Copyright (c) 2009,2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
# Use is subject to license terms. (See COPYING)
|
# Use is subject to license terms. (See COPYING)
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -24,14 +24,20 @@
|
||||||
"""Cursor classes
|
"""Cursor classes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import exceptions
|
import sys
|
||||||
|
from collections import deque
|
||||||
|
import weakref
|
||||||
|
import re
|
||||||
|
|
||||||
import mysql
|
import constants
|
||||||
import connection
|
|
||||||
import protocol
|
import protocol
|
||||||
import errors
|
import errors
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
|
RE_SQL_COMMENT = re.compile("\/\*.*\*\/")
|
||||||
|
RE_SQL_INSERT_VALUES = re.compile(r'\sVALUES\s*(\(.*\))', re.I)
|
||||||
|
RE_SQL_INSERT_STMT = re.compile(r'INSERT\s+INTO', re.I)
|
||||||
|
|
||||||
class CursorBase(object):
|
class CursorBase(object):
|
||||||
"""
|
"""
|
||||||
Base for defining MySQLCursor. This class is a skeleton and defines
|
Base for defining MySQLCursor. This class is a skeleton and defines
|
||||||
|
@ -79,6 +85,9 @@ class CursorBase(object):
|
||||||
def setoutputsize(self, size, column=None):
|
def setoutputsize(self, size, column=None):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
pass
|
||||||
|
|
||||||
class MySQLCursor(CursorBase):
|
class MySQLCursor(CursorBase):
|
||||||
"""
|
"""
|
||||||
Default cursor which fetches all rows and stores it for later
|
Default cursor which fetches all rows and stores it for later
|
||||||
|
@ -100,17 +109,15 @@ class MySQLCursor(CursorBase):
|
||||||
def __init__(self, db=None):
|
def __init__(self, db=None):
|
||||||
CursorBase.__init__(self)
|
CursorBase.__init__(self)
|
||||||
self.db = None
|
self.db = None
|
||||||
self.fields = ()
|
self._more_results = False
|
||||||
self.nrflds = 0
|
self._results = deque()
|
||||||
self._result = []
|
|
||||||
self._nextrow = (None, None)
|
self._nextrow = (None, None)
|
||||||
self.lastrowid = None
|
self.lastrowid = None
|
||||||
self._warnings = None
|
self._warnings = None
|
||||||
self._warning_count = 0
|
self._warning_count = 0
|
||||||
self._executed = None
|
self._executed = None
|
||||||
self._have_result = False
|
self._have_result = False
|
||||||
self._get_warnings = False
|
self._raise_on_warnings = True
|
||||||
|
|
||||||
if db is not None:
|
if db is not None:
|
||||||
self.set_connection(db)
|
self.set_connection(db)
|
||||||
|
|
||||||
|
@ -121,40 +128,27 @@ class MySQLCursor(CursorBase):
|
||||||
"""
|
"""
|
||||||
return iter(self.fetchone, None)
|
return iter(self.fetchone, None)
|
||||||
|
|
||||||
def _valid_protocol(self,db):
|
|
||||||
if not hasattr(db,'conn'):
|
|
||||||
raise errors.InterfaceError(
|
|
||||||
"MySQL connection object connection not valid.")
|
|
||||||
|
|
||||||
try:
|
|
||||||
if not isinstance(db.conn.protocol,protocol.MySQLProtocol):
|
|
||||||
raise errors.InterfaceError(
|
|
||||||
"MySQL connection has no protocol set.")
|
|
||||||
except AttributeError:
|
|
||||||
raise errors.InterfaceError(
|
|
||||||
"MySQL connection object connection not valid.")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def set_connection(self, db):
|
def set_connection(self, db):
|
||||||
if isinstance(db,mysql.MySQLBase):
|
try:
|
||||||
if self._valid_protocol(db):
|
if isinstance(db.protocol,protocol.MySQLProtocol):
|
||||||
self.db = db
|
self.db = weakref.ref(db)
|
||||||
self.protocol = db.conn.protocol
|
if self not in self.db().cursors:
|
||||||
self.db.register_cursor(self)
|
self.db().cursors.append(self)
|
||||||
self._get_warnings = self.db.get_warnings
|
except:
|
||||||
else:
|
raise errors.InterfaceError(errno=2048)
|
||||||
raise errors.InterfaceError(
|
|
||||||
"MySQLCursor db-argument must subclass of mysql.MySQLBase")
|
|
||||||
|
|
||||||
def _reset_result(self):
|
def _reset_result(self):
|
||||||
del self._result[:]
|
|
||||||
self.rowcount = -1
|
self.rowcount = -1
|
||||||
self._nextrow = (None, None)
|
self._nextrow = (None, None)
|
||||||
self._have_result = False
|
self._have_result = False
|
||||||
|
try:
|
||||||
|
self.db().unread_result = False
|
||||||
|
except:
|
||||||
|
pass
|
||||||
self._warnings = None
|
self._warnings = None
|
||||||
self._warning_count = 0
|
self._warning_count = 0
|
||||||
self._fields = ()
|
self.description = ()
|
||||||
|
self.reset()
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
"""
|
"""
|
||||||
|
@ -177,20 +171,21 @@ class MySQLCursor(CursorBase):
|
||||||
"""
|
"""
|
||||||
if self.db is None:
|
if self.db is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.db.remove_cursor(self)
|
self._reset_result()
|
||||||
|
self.db().remove_cursor(self)
|
||||||
self.db = None
|
self.db = None
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
del self._result[:]
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _process_params_dict(self, params):
|
def _process_params_dict(self, params):
|
||||||
try:
|
try:
|
||||||
to_mysql = self.db.converter.to_mysql
|
to_mysql = self.db().converter.to_mysql
|
||||||
escape = self.db.converter.escape
|
escape = self.db().converter.escape
|
||||||
quote = self.db.converter.quote
|
quote = self.db().converter.quote
|
||||||
res = {}
|
res = {}
|
||||||
for k,v in params.items():
|
for k,v in params.items():
|
||||||
c = v
|
c = v
|
||||||
|
@ -222,9 +217,9 @@ class MySQLCursor(CursorBase):
|
||||||
try:
|
try:
|
||||||
res = params
|
res = params
|
||||||
|
|
||||||
to_mysql = self.db.converter.to_mysql
|
to_mysql = self.db().converter.to_mysql
|
||||||
escape = self.db.converter.escape
|
escape = self.db().converter.escape
|
||||||
quote = self.db.converter.quote
|
quote = self.db().converter.quote
|
||||||
|
|
||||||
res = map(to_mysql,res)
|
res = map(to_mysql,res)
|
||||||
res = map(escape,res)
|
res = map(escape,res)
|
||||||
|
@ -236,34 +231,10 @@ class MySQLCursor(CursorBase):
|
||||||
return tuple(res)
|
return tuple(res)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_description(self, res=None):
|
|
||||||
"""
|
|
||||||
Gets the description of the fields out of a result we got from
|
|
||||||
the MySQL Server. If res is None then self.description is
|
|
||||||
returned (which can be None).
|
|
||||||
|
|
||||||
Returns a list or None when no descriptions are available.
|
|
||||||
"""
|
|
||||||
if not res:
|
|
||||||
return self.description
|
|
||||||
|
|
||||||
desc = []
|
|
||||||
try:
|
|
||||||
for fld in res[1]:
|
|
||||||
if not isinstance(fld, protocol.FieldPacket):
|
|
||||||
raise errors.ProgrammingError(
|
|
||||||
"Can only get description from protocol.FieldPacket")
|
|
||||||
desc.append(fld.get_description())
|
|
||||||
except TypeError:
|
|
||||||
raise errors.ProgrammingError(
|
|
||||||
"_get_description needs a list as argument."
|
|
||||||
)
|
|
||||||
return desc
|
|
||||||
|
|
||||||
def _row_to_python(self, rowdata, desc=None):
|
def _row_to_python(self, rowdata, desc=None):
|
||||||
res = ()
|
res = ()
|
||||||
try:
|
try:
|
||||||
to_python = self.db.converter.to_python
|
to_python = self.db().converter.to_python
|
||||||
if not desc:
|
if not desc:
|
||||||
desc = self.description
|
desc = self.description
|
||||||
for idx,v in enumerate(rowdata):
|
for idx,v in enumerate(rowdata):
|
||||||
|
@ -278,13 +249,17 @@ class MySQLCursor(CursorBase):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _handle_noresultset(self, res):
|
def _handle_noresultset(self, res):
|
||||||
"""Handles result of execute() when there is no result set."""
|
"""Handles result of execute() when there is no result set
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
self.rowcount = res.affected_rows
|
self.rowcount = res['affected_rows']
|
||||||
self.lastrowid = res.insert_id
|
self.lastrowid = res['insert_id']
|
||||||
self._warning_count = res.warning_count
|
self._warning_count = res['warning_count']
|
||||||
if self._get_warnings is True and self._warning_count:
|
if self.db().get_warnings is True and self._warning_count:
|
||||||
self._warnings = self._fetch_warnings()
|
self._warnings = self._fetch_warnings()
|
||||||
|
self._set_more_results(res['server_status'])
|
||||||
|
except errors.Error:
|
||||||
|
raise
|
||||||
except StandardError, e:
|
except StandardError, e:
|
||||||
raise errors.ProgrammingError(
|
raise errors.ProgrammingError(
|
||||||
"Failed handling non-resultset; %s" % e)
|
"Failed handling non-resultset; %s" % e)
|
||||||
|
@ -292,6 +267,17 @@ class MySQLCursor(CursorBase):
|
||||||
def _handle_resultset(self):
|
def _handle_resultset(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _handle_result(self, res):
|
||||||
|
if isinstance(res, dict):
|
||||||
|
self.db().unread_result = False
|
||||||
|
self._have_result = False
|
||||||
|
self._handle_noresultset(res)
|
||||||
|
else:
|
||||||
|
self.description = res[1]
|
||||||
|
self.db().unread_result = True
|
||||||
|
self._have_result = True
|
||||||
|
self._handle_resultset()
|
||||||
|
|
||||||
def execute(self, operation, params=None):
|
def execute(self, operation, params=None):
|
||||||
"""
|
"""
|
||||||
Executes the given operation. The parameters given through params
|
Executes the given operation. The parameters given through params
|
||||||
|
@ -306,32 +292,34 @@ class MySQLCursor(CursorBase):
|
||||||
"""
|
"""
|
||||||
if not operation:
|
if not operation:
|
||||||
return 0
|
return 0
|
||||||
|
if self.db().unread_result is True:
|
||||||
|
raise errors.InternalError("Unread result found.")
|
||||||
|
|
||||||
self._reset_result()
|
self._reset_result()
|
||||||
stmt = ''
|
stmt = ''
|
||||||
|
|
||||||
# Make sure we send the query in correct character set
|
|
||||||
try:
|
try:
|
||||||
if isinstance(operation, unicode):
|
if isinstance(operation, unicode):
|
||||||
operation.encode(self.db.charset_name)
|
operation = operation.encode(self.db().charset_name)
|
||||||
|
|
||||||
if params is not None:
|
if params is not None:
|
||||||
|
try:
|
||||||
stmt = operation % self._process_params(params)
|
stmt = operation % self._process_params(params)
|
||||||
|
except TypeError:
|
||||||
|
raise errors.ProgrammingError(
|
||||||
|
"Wrong number of arguments during string formatting")
|
||||||
else:
|
else:
|
||||||
stmt = operation
|
stmt = operation
|
||||||
res = self.protocol.cmd_query(stmt)
|
|
||||||
if isinstance(res, protocol.OKResultPacket):
|
res = self.db().protocol.cmd_query(stmt)
|
||||||
self._have_result = False
|
self._handle_result(res)
|
||||||
self._handle_noresultset(res)
|
except (UnicodeDecodeError,UnicodeEncodeError), e:
|
||||||
else:
|
raise errors.ProgrammingError(str(e))
|
||||||
self.description = self._get_description(res)
|
except errors.Error:
|
||||||
self._have_result = True
|
|
||||||
self._handle_resultset()
|
|
||||||
except errors.ProgrammingError:
|
|
||||||
raise
|
|
||||||
except errors.OperationalError:
|
|
||||||
raise
|
raise
|
||||||
except StandardError, e:
|
except StandardError, e:
|
||||||
raise errors.InterfaceError(
|
raise errors.InterfaceError, errors.InterfaceError(
|
||||||
"Failed executing the operation; %s" % e)
|
"Failed executing the operation; %s" % e), sys.exc_info()[2]
|
||||||
else:
|
else:
|
||||||
self._executed = stmt
|
self._executed = stmt
|
||||||
return self.rowcount
|
return self.rowcount
|
||||||
|
@ -339,10 +327,28 @@ class MySQLCursor(CursorBase):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def executemany(self, operation, seq_params):
|
def executemany(self, operation, seq_params):
|
||||||
"""Loops over seq_params and calls excute()"""
|
"""Loops over seq_params and calls execute()
|
||||||
|
|
||||||
|
INSERT statements are optimized by batching the data, that is
|
||||||
|
using the MySQL multiple rows syntax.
|
||||||
|
"""
|
||||||
if not operation:
|
if not operation:
|
||||||
return 0
|
return 0
|
||||||
|
if self.db().unread_result is True:
|
||||||
|
raise errors.InternalError("Unread result found.")
|
||||||
|
|
||||||
|
# Optimize INSERTs by batching them
|
||||||
|
if re.match(RE_SQL_INSERT_STMT,operation):
|
||||||
|
opnocom = re.sub(RE_SQL_COMMENT,'',operation)
|
||||||
|
m = re.search(RE_SQL_INSERT_VALUES,opnocom)
|
||||||
|
fmt = m.group(1)
|
||||||
|
values = []
|
||||||
|
for params in seq_params:
|
||||||
|
values.append(fmt % self._process_params(params))
|
||||||
|
operation = re.sub(re.escape(m.group(1)),
|
||||||
|
','.join(values),operation,count=1)
|
||||||
|
self.execute(operation)
|
||||||
|
else:
|
||||||
rowcnt = 0
|
rowcnt = 0
|
||||||
try:
|
try:
|
||||||
for params in seq_params:
|
for params in seq_params:
|
||||||
|
@ -356,8 +362,44 @@ class MySQLCursor(CursorBase):
|
||||||
except:
|
except:
|
||||||
# Raise whatever execute() raises
|
# Raise whatever execute() raises
|
||||||
raise
|
raise
|
||||||
|
self.rowcount = rowcnt
|
||||||
|
return self.rowcount
|
||||||
|
|
||||||
return rowcnt
|
def _set_more_results(self, flags):
|
||||||
|
flag = constants.ServerFlag.MORE_RESULTS_EXISTS
|
||||||
|
self._more_results = constants.flag_is_set(flag, flags)
|
||||||
|
|
||||||
|
def next_resultset(self):
|
||||||
|
"""Gets next result after executing multiple statements
|
||||||
|
|
||||||
|
When more results are available, this function will reset the
|
||||||
|
current result and advance to the next set.
|
||||||
|
|
||||||
|
This is useful when executing multiple statements. If you need
|
||||||
|
to retrieve multiple results after executing a stored procedure
|
||||||
|
using callproc(), use next_proc_resultset() instead.
|
||||||
|
"""
|
||||||
|
if self._more_results is True:
|
||||||
|
buf = self.db().protocol._recv_packet()
|
||||||
|
res = self.db().protocol.handle_cmd_result(buf)
|
||||||
|
self._reset_result()
|
||||||
|
self._handle_result(res)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def next_proc_resultset(self):
|
||||||
|
"""Get the next result set after calling a stored procedure
|
||||||
|
|
||||||
|
Returns a MySQLCursorBuffered-object"""
|
||||||
|
try:
|
||||||
|
return self._results.popleft()
|
||||||
|
except IndexError:
|
||||||
|
return None
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def callproc(self, procname, args=()):
|
def callproc(self, procname, args=()):
|
||||||
"""Calls a stored procedue with the given arguments
|
"""Calls a stored procedue with the given arguments
|
||||||
|
@ -385,6 +427,7 @@ class MySQLCursor(CursorBase):
|
||||||
Raises exceptions when something is wrong.
|
Raises exceptions when something is wrong.
|
||||||
"""
|
"""
|
||||||
argfmt = "@_%s_arg%d"
|
argfmt = "@_%s_arg%d"
|
||||||
|
self._results = deque()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
procargs = self._process_params(args)
|
procargs = self._process_params(args)
|
||||||
|
@ -397,12 +440,23 @@ class MySQLCursor(CursorBase):
|
||||||
self.execute(setquery, (arg,))
|
self.execute(setquery, (arg,))
|
||||||
|
|
||||||
call = "CALL %s(%s)" % (procname,','.join(argnames))
|
call = "CALL %s(%s)" % (procname,','.join(argnames))
|
||||||
res = self.protocol.cmd_query(call)
|
res = self.db().protocol.cmd_query(call)
|
||||||
|
|
||||||
|
while not isinstance(res, dict):
|
||||||
|
tmp = MySQLCursorBuffered(self.db())
|
||||||
|
tmp.description = res[1]
|
||||||
|
tmp._handle_resultset()
|
||||||
|
self._results.append(tmp)
|
||||||
|
buf = self.db().protocol._recv_packet()
|
||||||
|
res = self.db().protocol.handle_cmd_result(buf)
|
||||||
|
try:
|
||||||
select = "SELECT %s" % ','.join(argnames)
|
select = "SELECT %s" % ','.join(argnames)
|
||||||
self.execute(select)
|
self.execute(select)
|
||||||
|
return self.fetchone()
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
except errors.ProgrammingError:
|
except errors.Error:
|
||||||
raise
|
raise
|
||||||
except StandardError, e:
|
except StandardError, e:
|
||||||
raise errors.InterfaceError(
|
raise errors.InterfaceError(
|
||||||
|
@ -420,13 +474,17 @@ class MySQLCursor(CursorBase):
|
||||||
"""
|
"""
|
||||||
res = []
|
res = []
|
||||||
try:
|
try:
|
||||||
c = self.db.cursor()
|
c = self.db().cursor()
|
||||||
cnt = c.execute("SHOW WARNINGS")
|
cnt = c.execute("SHOW WARNINGS")
|
||||||
res = c.fetchall()
|
res = c.fetchall()
|
||||||
c.close()
|
c.close()
|
||||||
except StandardError, e:
|
except StandardError, e:
|
||||||
raise errors.ProgrammingError(
|
raise errors.InterfaceError(
|
||||||
"Failed getting warnings; %s" % e)
|
"Failed getting warnings; %s" % e)
|
||||||
|
|
||||||
|
if self.db().raise_on_warnings is True:
|
||||||
|
msg = '; '.join([ "(%s) %s" % (r[1],r[2]) for r in res])
|
||||||
|
raise errors.get_mysql_exception(res[0][1],res[0][2])
|
||||||
else:
|
else:
|
||||||
if len(res):
|
if len(res):
|
||||||
return res
|
return res
|
||||||
|
@ -435,10 +493,12 @@ class MySQLCursor(CursorBase):
|
||||||
|
|
||||||
def _handle_eof(self, eof):
|
def _handle_eof(self, eof):
|
||||||
self._have_result = False
|
self._have_result = False
|
||||||
|
self.db().unread_result = False
|
||||||
self._nextrow = (None, None)
|
self._nextrow = (None, None)
|
||||||
self._warning_count = eof.warning_count
|
self._warning_count = eof['warning_count']
|
||||||
if self.db.get_warnings is True and eof.warning_count:
|
if self.db().get_warnings is True and eof['warning_count']:
|
||||||
self._warnings = self._fetch_warnings()
|
self._warnings = self._fetch_warnings()
|
||||||
|
self._set_more_results(eof['status_flag'])
|
||||||
|
|
||||||
def _fetch_row(self):
|
def _fetch_row(self):
|
||||||
if self._have_result is False:
|
if self._have_result is False:
|
||||||
|
@ -446,11 +506,12 @@ class MySQLCursor(CursorBase):
|
||||||
row = None
|
row = None
|
||||||
try:
|
try:
|
||||||
if self._nextrow == (None, None):
|
if self._nextrow == (None, None):
|
||||||
(row, eof) = self.protocol.result_get_row()
|
(row, eof) = self.db().protocol.get_row()
|
||||||
else:
|
else:
|
||||||
(row, eof) = self._nextrow
|
(row, eof) = self._nextrow
|
||||||
if row:
|
if row:
|
||||||
(foo, eof) = self._nextrow = self.protocol.result_get_row()
|
(foo, eof) = self._nextrow = \
|
||||||
|
self.db().protocol.get_row()
|
||||||
if eof is not None:
|
if eof is not None:
|
||||||
self._handle_eof(eof)
|
self._handle_eof(eof)
|
||||||
if self.rowcount == -1:
|
if self.rowcount == -1:
|
||||||
|
@ -491,12 +552,16 @@ class MySQLCursor(CursorBase):
|
||||||
raise errors.InterfaceError("No result set to fetch from.")
|
raise errors.InterfaceError("No result set to fetch from.")
|
||||||
res = []
|
res = []
|
||||||
row = None
|
row = None
|
||||||
while self._have_result:
|
while self.db().unread_result:
|
||||||
row = self.fetchone()
|
row = self.fetchone()
|
||||||
if row:
|
if row:
|
||||||
res.append(row)
|
res.append(row)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
@property
|
||||||
|
def column_names(self):
|
||||||
|
return tuple( [d[0].decode('utf8') for d in self.description] )
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
fmt = "MySQLCursor: %s"
|
fmt = "MySQLCursor: %s"
|
||||||
if self._executed:
|
if self._executed:
|
||||||
|
@ -516,27 +581,71 @@ class MySQLCursorBuffered(MySQLCursor):
|
||||||
|
|
||||||
def __init__(self, db=None):
|
def __init__(self, db=None):
|
||||||
MySQLCursor.__init__(self, db)
|
MySQLCursor.__init__(self, db)
|
||||||
self._rows = []
|
self._rows = None
|
||||||
self._next_row = 0
|
self._next_row = 0
|
||||||
|
|
||||||
def _handle_resultset(self):
|
def _handle_resultset(self):
|
||||||
self._get_all_rows()
|
(self._rows, eof) = self.db().protocol.get_rows()
|
||||||
|
|
||||||
def _get_all_rows(self):
|
|
||||||
(self._rows, eof) = self.protocol.result_get_rows()
|
|
||||||
self.rowcount = len(self._rows)
|
self.rowcount = len(self._rows)
|
||||||
self._handle_eof(eof)
|
self._handle_eof(eof)
|
||||||
self._next_row = 0
|
self._next_row = 0
|
||||||
self._have_result = True
|
try:
|
||||||
|
self.db().unread_result = False
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self._rows = None
|
||||||
|
|
||||||
def _fetch_row(self):
|
def _fetch_row(self):
|
||||||
row = None
|
row = None
|
||||||
try:
|
try:
|
||||||
row = self._rows[self._next_row]
|
row = self._rows[self._next_row]
|
||||||
except:
|
except:
|
||||||
self._have_result = False
|
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
self._next_row += 1
|
self._next_row += 1
|
||||||
return row
|
return row
|
||||||
return None
|
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:
|
||||||
|
res.append(self._row_to_python(row))
|
||||||
|
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
|
||||||
|
|
||||||
|
class MySQLCursorRaw(MySQLCursor):
|
||||||
|
|
||||||
|
def fetchone(self):
|
||||||
|
row = self._fetch_row()
|
||||||
|
if row:
|
||||||
|
return tuple(row)
|
||||||
|
return None
|
||||||
|
|
||||||
|
class MySQLCursorBufferedRaw(MySQLCursorBuffered):
|
||||||
|
|
||||||
|
def fetchone(self):
|
||||||
|
row = self._fetch_row()
|
||||||
|
if row:
|
||||||
|
return tuple(row)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def fetchall(self):
|
||||||
|
if self._rows is None:
|
||||||
|
raise errors.InterfaceError("No result set to fetch from.")
|
||||||
|
return [ tuple(r) for r in self._rows ]
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# MySQL Connector/Python - MySQL driver written in Python.
|
# MySQL Connector/Python - MySQL driver written in Python.
|
||||||
# Copyright 2009 Sun Microsystems, Inc. All rights reserved
|
# Copyright (c) 2009,2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
# Use is subject to license terms. (See COPYING)
|
# Use is subject to license terms. (See COPYING)
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# MySQL Connector/Python - MySQL driver written in Python.
|
# MySQL Connector/Python - MySQL driver written in Python.
|
||||||
# Copyright 2009 Sun Microsystems, Inc. All rights reserved
|
# Copyright (c) 2009,2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
# Use is subject to license terms. (See COPYING)
|
# Use is subject to license terms. (See COPYING)
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -24,32 +24,152 @@
|
||||||
"""Python exceptions
|
"""Python exceptions
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import exceptions
|
import logging
|
||||||
import protocol
|
|
||||||
|
logger = logging.getLogger('myconnpy')
|
||||||
|
|
||||||
|
# see get_mysql_exceptions method for errno ranges and smaller lists
|
||||||
|
__programming_errors = (
|
||||||
|
1083,1084,1090,1091,1093,1096,1097,1101,1102,1103,1107,1108,1110,1111,
|
||||||
|
1113,1120,1124,1125,1128,1136,1366,1139,1140,1146,1149,)
|
||||||
|
__operational_errors = (
|
||||||
|
1028,1029,1030,1053,1077,1078,1079,1080,1081,1095,1104,1106,1114,1116,
|
||||||
|
1117,1119,1122,1123,1126,1133,1135,1137,1145,1147,)
|
||||||
|
|
||||||
|
def get_mysql_exception(errno,msg):
|
||||||
|
|
||||||
|
exception = OperationalError
|
||||||
|
|
||||||
|
if (errno >= 1046 and errno <= 1052) or \
|
||||||
|
(errno >= 1054 and errno <= 1061) or \
|
||||||
|
(errno >= 1063 and errno <= 1075) or \
|
||||||
|
errno in __programming_errors:
|
||||||
|
exception = ProgrammingError
|
||||||
|
elif errno in (1097,1109,1118,1121,1138,1292):
|
||||||
|
exception = DataError
|
||||||
|
elif errno in (1031,1089,1112,1115,1127,1148,1149):
|
||||||
|
exception = NotSupportedError
|
||||||
|
elif errno in (1062,1082,1099,1100):
|
||||||
|
exception = IntegrityError
|
||||||
|
elif errno in (1085,1086,1094,1098):
|
||||||
|
exception = InternalError
|
||||||
|
elif (errno >= 1004 and errno <= 1030) or \
|
||||||
|
(errno >= 1132 and errno <= 1045) or \
|
||||||
|
(errno >= 1141 and errno <= 1145) or \
|
||||||
|
(errno >= 1129 and errno <= 1133) or \
|
||||||
|
errno in __operational_errors:
|
||||||
|
exception = OperationalError
|
||||||
|
|
||||||
|
return exception(msg,errno=errno)
|
||||||
|
|
||||||
|
class ClientError(object):
|
||||||
|
|
||||||
|
client_errors = {
|
||||||
|
2000: "Unknown MySQL error",
|
||||||
|
2001: "Can't create UNIX socket (%(socketaddr)d)",
|
||||||
|
2002: "Can't connect to local MySQL server through socket '%(socketaddr)s' (%(errno)s)",
|
||||||
|
2003: "Can't connect to MySQL server on '%(socketaddr)s' (%(errno)s)",
|
||||||
|
2004: "Can't create TCP/IP socket (%s)",
|
||||||
|
2005: "Unknown MySQL server host '%(socketaddr)s' (%s)",
|
||||||
|
2006: "MySQL server has gone away",
|
||||||
|
2007: "Protocol mismatch; server version = %(server_version)d, client version = %(client_version)d",
|
||||||
|
2008: "MySQL client ran out of memory",
|
||||||
|
2009: "Wrong host info",
|
||||||
|
2010: "Localhost via UNIX socket",
|
||||||
|
2011: "%(misc)s via TCP/IP",
|
||||||
|
2012: "Error in server handshake",
|
||||||
|
2013: "Lost connection to MySQL server during query",
|
||||||
|
2014: "Commands out of sync; you can't run this command now",
|
||||||
|
2015: "Named pipe: %(socketaddr)s",
|
||||||
|
2016: "Can't wait for named pipe to host: %(host)s pipe: %(socketaddr)s (%(errno)d)",
|
||||||
|
2017: "Can't open named pipe to host: %s pipe: %s (%(errno)d)",
|
||||||
|
2018: "Can't set state of named pipe to host: %(host)s pipe: %(socketaddr)s (%(errno)d)",
|
||||||
|
2019: "Can't initialize character set %(charset)s (path: %(misc)s)",
|
||||||
|
2020: "Got packet bigger than 'max_allowed_packet' bytes",
|
||||||
|
2021: "Embedded server",
|
||||||
|
2022: "Error on SHOW SLAVE STATUS:",
|
||||||
|
2023: "Error on SHOW SLAVE HOSTS:",
|
||||||
|
2024: "Error connecting to slave:",
|
||||||
|
2025: "Error connecting to master:",
|
||||||
|
2026: "SSL connection error",
|
||||||
|
2027: "Malformed packet",
|
||||||
|
2028: "This client library is licensed only for use with MySQL servers having '%s' license",
|
||||||
|
2029: "Invalid use of null pointer",
|
||||||
|
2030: "Statement not prepared",
|
||||||
|
2031: "No data supplied for parameters in prepared statement",
|
||||||
|
2032: "Data truncated",
|
||||||
|
2033: "No parameters exist in the statement",
|
||||||
|
2034: "Invalid parameter number",
|
||||||
|
2035: "Can't send long data for non-string/non-binary data types (parameter: %d)",
|
||||||
|
2036: "Using unsupported buffer type: %d (parameter: %d)",
|
||||||
|
2037: "Shared memory: %s",
|
||||||
|
2038: "Can't open shared memory; client could not create request event (%d)",
|
||||||
|
2039: "Can't open shared memory; no answer event received from server (%d)",
|
||||||
|
2040: "Can't open shared memory; server could not allocate file mapping (%d)",
|
||||||
|
2041: "Can't open shared memory; server could not get pointer to file mapping (%d)",
|
||||||
|
2042: "Can't open shared memory; client could not allocate file mapping (%d)",
|
||||||
|
2043: "Can't open shared memory; client could not get pointer to file mapping (%d)",
|
||||||
|
2044: "Can't open shared memory; client could not create %s event (%d)",
|
||||||
|
2045: "Can't open shared memory; no answer from server (%d)",
|
||||||
|
2046: "Can't open shared memory; cannot send request event to server (%d)",
|
||||||
|
2047: "Wrong or unknown protocol",
|
||||||
|
2048: "Invalid connection handle",
|
||||||
|
2049: "Connection using old (pre-4.1.1) authentication protocol refused (client option 'secure_auth' enabled)",
|
||||||
|
2050: "Row retrieval was canceled by mysql_stmt_close() call",
|
||||||
|
2051: "Attempt to read column without prior row fetch",
|
||||||
|
2052: "Prepared statement contains no metadata",
|
||||||
|
2053: "Attempt to read a row while there is no result set associated with the statement",
|
||||||
|
2054: "This feature is not implemented yet",
|
||||||
|
2055: "Lost connection to MySQL server at '%(socketaddr)s', system error: %(errno)d",
|
||||||
|
2056: "Statement closed indirectly because of a preceeding %s() call",
|
||||||
|
2057: "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",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __new__(cls):
|
||||||
|
raise TypeError, "Can not instanciate from %s" % cls.__name__
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_error_msg(cls,errno,values=None):
|
||||||
|
res = None
|
||||||
|
if cls.client_errors.has_key(errno):
|
||||||
|
if values is not None:
|
||||||
|
try:
|
||||||
|
res = cls.client_errors[errno] % values
|
||||||
|
except:
|
||||||
|
logger.debug("Missing values for errno %d" % errno)
|
||||||
|
res = cls.client_errors[errno], "(missing values!)"
|
||||||
|
else:
|
||||||
|
res = cls.client_errors[errno]
|
||||||
|
if res is None:
|
||||||
|
res = "Unknown client error %d" % errno
|
||||||
|
logger.debug(res)
|
||||||
|
return res
|
||||||
|
|
||||||
class Error(StandardError):
|
class Error(StandardError):
|
||||||
|
|
||||||
def __init__(self, m):
|
def __init__(self, m, errno=None, values=None):
|
||||||
if isinstance(m,protocol.ErrorResultPacket):
|
try:
|
||||||
# process MySQL error packet
|
# process MySQL error packet
|
||||||
self._process_packet(m)
|
self._process_packet(m)
|
||||||
else:
|
except:
|
||||||
# else the message should be a string
|
self.errno = errno or -1
|
||||||
self.errno = -1
|
|
||||||
self.errmsg = str(m)
|
|
||||||
self.sqlstate = -1
|
self.sqlstate = -1
|
||||||
self.msg = str(m)
|
if m is None and (errno >= 2000 and errno < 3000):
|
||||||
|
m = ClientError.get_error_msg(errno,values)
|
||||||
|
elif m is None:
|
||||||
|
m = 'Unknown error'
|
||||||
|
if self.errno != -1:
|
||||||
|
self.msg = "%s: %s" % (self.errno,m)
|
||||||
|
else:
|
||||||
|
self.msg = m
|
||||||
|
|
||||||
def _process_packet(self, packet):
|
def _process_packet(self, packet):
|
||||||
self.errno = packet.errno
|
self.errno = packet.errno
|
||||||
self.errmsg = packet.errmsg
|
|
||||||
self.sqlstate = packet.sqlstate
|
self.sqlstate = packet.sqlstate
|
||||||
if self.sqlstate:
|
if self.sqlstate:
|
||||||
m = '%d (%s): %s' % (self.errno, self.sqlstate, self.errmsg)
|
self.msg = '%d (%s): %s' % (self.errno,self.sqlstate,packet.errmsg)
|
||||||
else:
|
else:
|
||||||
m = '%d: %s' % (self.errno, self.errmsg)
|
self.msg = '%d: %s' % (self.errno, packet.errmsg)
|
||||||
self.errmsglong = m
|
|
||||||
self.msg = m
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.msg
|
return self.msg
|
||||||
|
@ -61,12 +181,12 @@ class Warning(StandardError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class InterfaceError(Error):
|
class InterfaceError(Error):
|
||||||
def __init__(self, msg):
|
def __init__(self, m=None, errno=None, values=None):
|
||||||
Error.__init__(self, msg)
|
Error.__init__(self, m, errno, values)
|
||||||
|
|
||||||
class DatabaseError(Error):
|
class DatabaseError(Error):
|
||||||
def __init__(self, msg):
|
def __init__(self, m=None, errno=None, values=None):
|
||||||
Error.__init__(self, msg)
|
Error.__init__(self, m, errno, values)
|
||||||
|
|
||||||
class InternalError(DatabaseError):
|
class InternalError(DatabaseError):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,414 +0,0 @@
|
||||||
# MySQL Connector/Python - MySQL driver written in Python.
|
|
||||||
# Copyright 2009 Sun Microsystems, Inc. All rights reserved
|
|
||||||
# Use is subject to license terms. (See COPYING)
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
# There are special exceptions to the terms and conditions of the GNU
|
|
||||||
# General Public License as it is applied to this software. View the
|
|
||||||
# full text of the exception in file EXCEPTIONS-CLIENT in the directory
|
|
||||||
# of this software distribution or see the FOSS License Exception at
|
|
||||||
# www.mysql.com.
|
|
||||||
#
|
|
||||||
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
"""Main classes for interacting with MySQL
|
|
||||||
"""
|
|
||||||
|
|
||||||
import socket, string, os
|
|
||||||
|
|
||||||
from connection import *
|
|
||||||
import constants
|
|
||||||
import conversion
|
|
||||||
import protocol
|
|
||||||
import errors
|
|
||||||
import utils
|
|
||||||
import cursor
|
|
||||||
|
|
||||||
|
|
||||||
class MySQLBase(object):
|
|
||||||
"""MySQLBase"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""Initializing"""
|
|
||||||
self.conn = None # Holding the connection
|
|
||||||
self.converter = None
|
|
||||||
|
|
||||||
self.client_flags = constants.ClientFlag.get_default()
|
|
||||||
(self.charset,
|
|
||||||
self.charset_name,
|
|
||||||
self.collation_name) = constants.CharacterSet.get_charset_info('utf8')
|
|
||||||
|
|
||||||
self.username = ''
|
|
||||||
self.password = ''
|
|
||||||
self.database = ''
|
|
||||||
self.client_host = ''
|
|
||||||
self.client_port = 0
|
|
||||||
|
|
||||||
self.affected_rows = 0
|
|
||||||
self.server_status = 0
|
|
||||||
self.warning_count = 0
|
|
||||||
self.field_count = 0
|
|
||||||
self.insert_id = 0
|
|
||||||
self.info_msg = ''
|
|
||||||
self.use_unicode = True
|
|
||||||
self.get_warnings = False
|
|
||||||
self.autocommit = False
|
|
||||||
self.connection_timeout = None
|
|
||||||
self.buffered = False
|
|
||||||
|
|
||||||
def connect(self):
|
|
||||||
"""To be implemented while subclassing MySQLBase."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _set_connection(self, prtcls=None):
|
|
||||||
"""Automatically chooses based on configuration which connection type to setup."""
|
|
||||||
if self.unix_socket and os.name != 'nt':
|
|
||||||
self.conn = MySQLUnixConnection(prtcls=prtcls,
|
|
||||||
unix_socket=self.unix_socket)
|
|
||||||
else:
|
|
||||||
self.conn = MySQLTCPConnection(prtcls=prtcls,
|
|
||||||
host=self.server_host, port=self.server_port)
|
|
||||||
self.conn.set_connection_timeout(self.connection_timeout)
|
|
||||||
|
|
||||||
def _open_connection(self):
|
|
||||||
"""Opens the connection and sets the appropriated protocol."""
|
|
||||||
# We don't know yet the MySQL version we connect too
|
|
||||||
self._set_connection()
|
|
||||||
try:
|
|
||||||
self.conn.open_connection()
|
|
||||||
version = self.conn.protocol.server_version
|
|
||||||
if version < (4,1):
|
|
||||||
raise InterfaceError("MySQL Version %s is not supported." % version)
|
|
||||||
else:
|
|
||||||
self.conn.set_protocol(protocol.MySQLProtocol)
|
|
||||||
self.protocol = self.conn.protocol
|
|
||||||
self.protocol.do_auth(username=self.username, password=self.password,
|
|
||||||
database=self.database)
|
|
||||||
except:
|
|
||||||
raise
|
|
||||||
|
|
||||||
def _post_connection(self):
|
|
||||||
"""Should be called after a connection was established"""
|
|
||||||
self.get_characterset_info()
|
|
||||||
self.set_converter_class(conversion.MySQLConverter)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.set_charset(self.charset_name)
|
|
||||||
self.set_autocommit(self.autocommit)
|
|
||||||
except:
|
|
||||||
raise
|
|
||||||
|
|
||||||
def is_connected(self):
|
|
||||||
"""
|
|
||||||
Check whether we are connected to the MySQL server.
|
|
||||||
"""
|
|
||||||
return self.protocol.cmd_ping()
|
|
||||||
ping = is_connected
|
|
||||||
|
|
||||||
def disconnect(self):
|
|
||||||
"""
|
|
||||||
Disconnect from the MySQL server.
|
|
||||||
"""
|
|
||||||
if not self.conn:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.conn.sock is not None:
|
|
||||||
self.protocol.cmd_quit()
|
|
||||||
try:
|
|
||||||
self.conn.close_connection()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
self.protocol = None
|
|
||||||
self.conn = None
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
self.converter_class = convclass
|
|
||||||
self.converter = self.converter_class(self.charset_name, self.use_unicode)
|
|
||||||
|
|
||||||
def get_characterset_info(self):
|
|
||||||
try:
|
|
||||||
(self.charset_name, self.collation_name) = constants.CharacterSet.get_info(self.charset)
|
|
||||||
except:
|
|
||||||
raise ProgrammingError, "Illegal character set information (id=%d)" % self.charset
|
|
||||||
return (self.charset_name, self.collation_name)
|
|
||||||
|
|
||||||
def get_server_version(self):
|
|
||||||
"""Returns the server version as a tuple"""
|
|
||||||
try:
|
|
||||||
return self.protocol.server_version
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_server_info(self):
|
|
||||||
"""Returns the server version as a string"""
|
|
||||||
return self.protocol.server_version_original
|
|
||||||
|
|
||||||
def get_server_threadid(self):
|
|
||||||
"""Returns the MySQL threadid of the connection."""
|
|
||||||
threadid = None
|
|
||||||
try:
|
|
||||||
threadid = self.protocol.server_threadid
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return threadid
|
|
||||||
|
|
||||||
def set_host(self, host):
|
|
||||||
"""
|
|
||||||
Set the host for connection to the MySQL server.
|
|
||||||
"""
|
|
||||||
self.server_host = host
|
|
||||||
|
|
||||||
def set_port(self, port):
|
|
||||||
"""
|
|
||||||
Set the TCP port to be used when connecting to the server, usually 3306.
|
|
||||||
"""
|
|
||||||
self.server_port = port
|
|
||||||
|
|
||||||
def set_login(self, username=None, password=None):
|
|
||||||
"""
|
|
||||||
Set the username and/or password for the user connecting to the MySQL Server.
|
|
||||||
"""
|
|
||||||
self.username = username
|
|
||||||
self.password = password
|
|
||||||
|
|
||||||
def set_unicode(self, value=True):
|
|
||||||
"""
|
|
||||||
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_database(self, database):
|
|
||||||
"""
|
|
||||||
Set the database to be used after connection succeeded.
|
|
||||||
"""
|
|
||||||
self.database = database
|
|
||||||
|
|
||||||
def set_charset(self, name):
|
|
||||||
"""
|
|
||||||
Set the character set used for the connection. This is the recommended
|
|
||||||
way of change it per connection basis. It does execute SET NAMES
|
|
||||||
internally, but it's good not to use this command directly, since we
|
|
||||||
are setting some other members accordingly.
|
|
||||||
"""
|
|
||||||
if name not in constants.CharacterSet.get_supported():
|
|
||||||
raise errors.ProgrammingError, "Character set '%s' not supported." % name
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
info = constants.CharacterSet.get_charset_info(name)
|
|
||||||
except errors.ProgrammingError, e:
|
|
||||||
raise
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.protocol.cmd_query("SET NAMES '%s'" % name)
|
|
||||||
except:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
(self.charset, self.charset_name, self.collation_name) = info
|
|
||||||
self.converter.set_charset(self.charset_name)
|
|
||||||
|
|
||||||
def set_getwarnings(self, bool):
|
|
||||||
"""
|
|
||||||
Set wheter we should get warnings whenever an operation produced some.
|
|
||||||
"""
|
|
||||||
self.get_warnings = bool
|
|
||||||
|
|
||||||
def set_autocommit(self, switch):
|
|
||||||
"""
|
|
||||||
Set auto commit on or off. The argument 'switch' must be a boolean type.
|
|
||||||
"""
|
|
||||||
if not isinstance(switch, bool):
|
|
||||||
raise ValueError, "The switch argument must be boolean."
|
|
||||||
|
|
||||||
s = 'OFF'
|
|
||||||
if switch:
|
|
||||||
s = 'ON'
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.protocol.cmd_query("SET AUTOCOMMIT = %s" % s)
|
|
||||||
except:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
self.autocommit = switch
|
|
||||||
|
|
||||||
def set_unixsocket(self, loc):
|
|
||||||
"""Set the UNIX Socket location. Does not check if it exists."""
|
|
||||||
self.unix_socket = loc
|
|
||||||
|
|
||||||
def set_connection_timeout(self, timeout):
|
|
||||||
self.connection_timeout = timeout
|
|
||||||
|
|
||||||
def set_client_flags(self, flags):
|
|
||||||
self.client_flags = flags
|
|
||||||
|
|
||||||
def set_buffered(self, val=False):
|
|
||||||
"""Sets whether cursor .execute() fetches rows"""
|
|
||||||
self.buffered = val
|
|
||||||
|
|
||||||
class MySQL(MySQLBase):
|
|
||||||
"""
|
|
||||||
Class implementing Python DB API v2.0.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Initializes the MySQL object. Calls connect() to open the connection
|
|
||||||
when an instance is created.
|
|
||||||
"""
|
|
||||||
MySQLBase.__init__(self)
|
|
||||||
self.cursors = []
|
|
||||||
self.affected_rows = 0
|
|
||||||
self.server_status = 0
|
|
||||||
self.warning_count = 0
|
|
||||||
self.field_count = 0
|
|
||||||
self.insert_id = 0
|
|
||||||
self.info_msg = ''
|
|
||||||
|
|
||||||
self.connect(*args, **kwargs)
|
|
||||||
|
|
||||||
def connect(self, dsn='', user='', password='', host='127.0.0.1',
|
|
||||||
port=3306, db=None, database=None, use_unicode=True, charset='utf8', get_warnings=False,
|
|
||||||
autocommit=False, unix_socket=None,
|
|
||||||
connection_timeout=None, client_flags=None, buffered=False):
|
|
||||||
"""
|
|
||||||
Establishes a connection to the MySQL Server. Called also when instansiating
|
|
||||||
a new MySQLConnection object through the __init__ method.
|
|
||||||
|
|
||||||
Possible parameters are:
|
|
||||||
|
|
||||||
dsn
|
|
||||||
(not used)
|
|
||||||
user
|
|
||||||
The username used to authenticate with the MySQL Server.
|
|
||||||
|
|
||||||
password
|
|
||||||
The password to authenticate the user with the MySQL Server.
|
|
||||||
|
|
||||||
host
|
|
||||||
The hostname or the IP address of the MySQL Server we are connecting with.
|
|
||||||
(default 127.0.0.1)
|
|
||||||
|
|
||||||
port
|
|
||||||
TCP port to use for connecting to the MySQL Server.
|
|
||||||
(default 3306)
|
|
||||||
|
|
||||||
database
|
|
||||||
db
|
|
||||||
Initial database to use once we are connected with the MySQL Server.
|
|
||||||
The db argument is synonym, but database takes precedence.
|
|
||||||
|
|
||||||
use_unicode
|
|
||||||
If set to true, string values received from MySQL will be returned
|
|
||||||
as Unicode strings.
|
|
||||||
Default: True
|
|
||||||
|
|
||||||
charset
|
|
||||||
Which character shall we use for sending data to MySQL. One can still
|
|
||||||
override this by using the SET NAMES command directly, but this is
|
|
||||||
discouraged. Instead, use the set_charset() method if you
|
|
||||||
want to change it.
|
|
||||||
Default: Whatever the MySQL server has default.
|
|
||||||
|
|
||||||
get_warnings
|
|
||||||
If set to true, whenever a query gives a warning, a SHOW WARNINGS will
|
|
||||||
be done to fetch them. They will be available as MySQLCursor.warnings.
|
|
||||||
The default is to ignore these warnings, for debugging it's good to
|
|
||||||
enable it though, or use strict mode in MySQL to make most of these
|
|
||||||
warnings errors.
|
|
||||||
Default: False
|
|
||||||
|
|
||||||
autocommit
|
|
||||||
Auto commit is OFF by default, which is required by the Python Db API
|
|
||||||
2.0 specification.
|
|
||||||
Default: False
|
|
||||||
|
|
||||||
unix_socket
|
|
||||||
Full path to the MySQL Server UNIX socket. By default TCP connection will
|
|
||||||
be used using the address specified by the host argument.
|
|
||||||
|
|
||||||
connection_timeout
|
|
||||||
Timeout for the TCP and UNIX socket connection.
|
|
||||||
|
|
||||||
client_flags
|
|
||||||
Allows to set flags for the connection. Check following for possible flags:
|
|
||||||
>>> from mysql.connector.constants import ClientFlag
|
|
||||||
>>> print '\n'.join(ClientFlag.get_full_info())
|
|
||||||
|
|
||||||
buffered
|
|
||||||
When set to True .execute() will fetch the rows immediatly.
|
|
||||||
|
|
||||||
"""
|
|
||||||
# db is not part of Db API v2.0, but MySQLdb supports it.
|
|
||||||
if db and not database:
|
|
||||||
database = db
|
|
||||||
|
|
||||||
self.set_host(host)
|
|
||||||
self.set_port(port)
|
|
||||||
self.set_database(database)
|
|
||||||
self.set_getwarnings(get_warnings)
|
|
||||||
self.set_unixsocket(unix_socket)
|
|
||||||
self.set_connection_timeout(connection_timeout)
|
|
||||||
self.set_client_flags(client_flags)
|
|
||||||
self.set_buffered(buffered)
|
|
||||||
|
|
||||||
if user or password:
|
|
||||||
self.set_login(user, password)
|
|
||||||
|
|
||||||
self.disconnect()
|
|
||||||
self._open_connection()
|
|
||||||
self._post_connection()
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
del self.cursors[:]
|
|
||||||
self.disconnect()
|
|
||||||
|
|
||||||
def remove_cursor(self, c):
|
|
||||||
try:
|
|
||||||
self.cursors.remove(c)
|
|
||||||
except ValueError:
|
|
||||||
raise errors.ProgrammingError(
|
|
||||||
"Cursor could not be removed.")
|
|
||||||
|
|
||||||
def register_cursor(self, c):
|
|
||||||
try:
|
|
||||||
self.cursors.append(c)
|
|
||||||
except:
|
|
||||||
raise
|
|
||||||
|
|
||||||
def cursor(self):
|
|
||||||
if self.buffered:
|
|
||||||
c = (cursor.MySQLCursorBuffered)(self)
|
|
||||||
else:
|
|
||||||
c = (cursor.MySQLCursor)(self)
|
|
||||||
|
|
||||||
self.register_cursor(c)
|
|
||||||
return c
|
|
||||||
|
|
||||||
def commit(self):
|
|
||||||
"""Shortcut for executing COMMIT."""
|
|
||||||
self.protocol.cmd_query("COMMIT")
|
|
||||||
|
|
||||||
def rollback(self):
|
|
||||||
"""Shortcut for executing ROLLBACK"""
|
|
||||||
self.protocol.cmd_query("ROLLBACK")
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
||||||
# MySQL Connector/Python - MySQL driver written in Python.
|
# MySQL Connector/Python - MySQL driver written in Python.
|
||||||
# Copyright 2009 Sun Microsystems, Inc. All rights reserved
|
# Copyright (c) 2009,2010, Oracle and/or its affiliates. All rights reserved.
|
||||||
# Use is subject to license terms. (See COPYING)
|
# Use is subject to license terms. (See COPYING)
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -28,91 +28,22 @@ __MYSQL_DEBUG__ = False
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
def int1read(c):
|
def intread(b):
|
||||||
"""
|
"""Unpacks the given buffer to an integer"""
|
||||||
Takes a bytes and returns it was an integer.
|
try:
|
||||||
|
if isinstance(b,int):
|
||||||
Returns integer.
|
return b
|
||||||
"""
|
l = len(b)
|
||||||
if isinstance(c,int):
|
if l == 1:
|
||||||
if c < 0 or c > 254:
|
return int(ord(b))
|
||||||
raise ValueError('excepts int 0 <= x <= 254')
|
if l <= 4:
|
||||||
return c
|
tmp = b + '\x00'*(4-l)
|
||||||
elif len(c) > 1:
|
return struct.unpack('<I', tmp)[0]
|
||||||
raise ValueError('excepts 1 byte long bytes-object or int')
|
else:
|
||||||
|
tmp = b + '\x00'*(8-l)
|
||||||
return int('%02x' % ord(c),16)
|
return struct.unpack('<Q', tmp)[0]
|
||||||
|
except:
|
||||||
def int2read(s):
|
raise
|
||||||
"""
|
|
||||||
Takes a string of 2 bytes and unpacks it as unsigned integer.
|
|
||||||
|
|
||||||
Returns integer.
|
|
||||||
"""
|
|
||||||
if len(s) > 2:
|
|
||||||
raise ValueError('int2read require s length of maximum 3 bytes')
|
|
||||||
elif len(s) < 2:
|
|
||||||
s = s + '\x00'
|
|
||||||
return struct.unpack('<H', s)[0]
|
|
||||||
|
|
||||||
def int3read(s):
|
|
||||||
"""
|
|
||||||
Takes a string of 3 bytes and unpacks it as integer.
|
|
||||||
|
|
||||||
Returns integer.
|
|
||||||
"""
|
|
||||||
if len(s) > 3:
|
|
||||||
raise ValueError('int3read require s length of maximum 3 bytes')
|
|
||||||
elif len(s) < 4:
|
|
||||||
s = s + '\x00'*(4-len(s))
|
|
||||||
return struct.unpack('<I', s)[0]
|
|
||||||
|
|
||||||
def int4read(s):
|
|
||||||
"""
|
|
||||||
Takes a string of 4 bytes and unpacks it as integer.
|
|
||||||
|
|
||||||
Returns integer.
|
|
||||||
"""
|
|
||||||
if len(s) > 4:
|
|
||||||
raise ValueError('int4read require s length of maximum 4 bytes')
|
|
||||||
elif len(s) < 4:
|
|
||||||
s = s + '\x00'*(4-len(s))
|
|
||||||
return struct.unpack('<I', s)[0]
|
|
||||||
|
|
||||||
def int8read(s):
|
|
||||||
"""
|
|
||||||
Takes a string of 8 bytes and unpacks it as integer.
|
|
||||||
|
|
||||||
Returns integer.
|
|
||||||
"""
|
|
||||||
if len(s) > 8:
|
|
||||||
raise ValueError('int4read require s length of maximum 8 bytes')
|
|
||||||
elif len(s) < 8:
|
|
||||||
s = s + '\x00'*(8-len(s))
|
|
||||||
return struct.unpack('<Q', s)[0]
|
|
||||||
|
|
||||||
def intread(s):
|
|
||||||
"""
|
|
||||||
Takes a string and unpacks it as an integer.
|
|
||||||
|
|
||||||
This function uses int1read, int2read, int3read and int4read by
|
|
||||||
checking the length of the given string.
|
|
||||||
|
|
||||||
Returns integer.
|
|
||||||
"""
|
|
||||||
l = len(s)
|
|
||||||
if l < 1 or l > 4:
|
|
||||||
raise ValueError('intread expects a string not longer than 4 bytes')
|
|
||||||
if not isinstance(s, str):
|
|
||||||
raise ValueError('intread expects a string')
|
|
||||||
fs = {
|
|
||||||
1 : int1read,
|
|
||||||
2 : int2read,
|
|
||||||
3 : int3read,
|
|
||||||
4 : int4read,
|
|
||||||
8 : int8read,
|
|
||||||
}
|
|
||||||
return fs[l](s)
|
|
||||||
|
|
||||||
def int1store(i):
|
def int1store(i):
|
||||||
"""
|
"""
|
||||||
|
@ -226,76 +157,27 @@ def read_lc_string(buf):
|
||||||
# NULL value
|
# NULL value
|
||||||
return (buf[1:], None)
|
return (buf[1:], None)
|
||||||
|
|
||||||
l = lsize = start = 0
|
l = lsize = 0
|
||||||
fst = buf[0]
|
fst = ord(buf[0])
|
||||||
# Remove the type byte, we got the length information.
|
|
||||||
buf = buf[1:]
|
|
||||||
|
|
||||||
if fst <= '\xFA':
|
if fst <= 250:
|
||||||
# Returns result right away.
|
l = fst
|
||||||
l = ord(fst)
|
return (buf[1+l:], buf[1:l+1])
|
||||||
s = buf[:l]
|
|
||||||
return (buf[l:], s)
|
|
||||||
elif fst == '\xFC':
|
|
||||||
lsize = 2
|
|
||||||
elif fst == '\xFD':
|
|
||||||
lsize = 3
|
|
||||||
elif fst == '\xFE':
|
|
||||||
lsize = 4
|
|
||||||
|
|
||||||
l = intread(buf[0:lsize])
|
lsize = fst - 250
|
||||||
# Chop of the bytes which hold the length
|
l = intread(buf[1:lsize+1])
|
||||||
buf = buf[lsize:]
|
return (buf[lsize+l+1:], buf[lsize+1:l+lsize+1])
|
||||||
# Get the actual string
|
|
||||||
s = buf[0:l]
|
|
||||||
# Set the buffer so we can return it
|
|
||||||
buf = buf[l:]
|
|
||||||
|
|
||||||
return (buf, s)
|
|
||||||
|
|
||||||
def read_lc_string_list(buf):
|
def read_lc_string_list(buf):
|
||||||
"""
|
"""Reads all length encoded strings from the given buffer
|
||||||
Reads all length encoded strings from the given buffer.
|
|
||||||
|
|
||||||
This is exact same function as read_lc_string() but duplicated
|
Returns a list of strings
|
||||||
in hopes for performance gain when reading results.
|
|
||||||
"""
|
"""
|
||||||
strlst = []
|
strlst = []
|
||||||
|
|
||||||
while buf:
|
while buf:
|
||||||
if buf[0] == '\xfb':
|
(buf, b) = read_lc_string(buf)
|
||||||
# NULL value
|
strlst.append(b)
|
||||||
buf = buf[1:]
|
|
||||||
strlst.append(None)
|
|
||||||
continue
|
|
||||||
|
|
||||||
l = lsize = start = 0
|
|
||||||
fst = buf[0]
|
|
||||||
# Remove the type byte, we got the length information.
|
|
||||||
buf = buf[1:]
|
|
||||||
|
|
||||||
if fst <= '\xFA':
|
|
||||||
# Returns result right away.
|
|
||||||
l = ord(fst)
|
|
||||||
strlst.append(buf[:l])
|
|
||||||
buf = buf[l:]
|
|
||||||
continue
|
|
||||||
elif fst == '\xFC':
|
|
||||||
lsize = 2
|
|
||||||
elif fst == '\xFD':
|
|
||||||
lsize = 3
|
|
||||||
elif fst == '\xFE':
|
|
||||||
lsize = 4
|
|
||||||
|
|
||||||
l = intread(buf[0:lsize])
|
|
||||||
# Chop of the bytes which hold the length
|
|
||||||
buf = buf[lsize:]
|
|
||||||
# Get the actual string
|
|
||||||
s = buf[0:l]
|
|
||||||
# Set the buffer so we can return it
|
|
||||||
buf = buf[l:]
|
|
||||||
|
|
||||||
strlst.append(s)
|
|
||||||
|
|
||||||
return strlst
|
return strlst
|
||||||
|
|
||||||
|
@ -320,31 +202,17 @@ def read_string(buf, end=None, size=None):
|
||||||
raise ValueError('read_string() needs either end or size (weird)')
|
raise ValueError('read_string() needs either end or size (weird)')
|
||||||
|
|
||||||
def read_int(buf, size):
|
def read_int(buf, size):
|
||||||
"""
|
"""Read an integer from buffer
|
||||||
Take a buffer and reads an integer of a certain size (1 <= size <= 4).
|
|
||||||
|
|
||||||
Returns a tuple (truncated buffer, int)
|
Returns a tuple (truncated buffer, int)
|
||||||
"""
|
"""
|
||||||
if len(buf) == 0:
|
|
||||||
raise ValueError("Empty buffer.")
|
|
||||||
if not isinstance(size,int) or (size not in [1,2,3,4,8]):
|
|
||||||
raise ValueError('size should be int in range of 1..4 or 8')
|
|
||||||
|
|
||||||
i = None
|
try:
|
||||||
if size == 1:
|
res = intread(buf[0:size])
|
||||||
i = int1read(buf[0])
|
except:
|
||||||
elif size == 2:
|
raise
|
||||||
i = int2read(buf[0:2])
|
|
||||||
elif size == 3:
|
|
||||||
i = int3read(buf[0:3])
|
|
||||||
elif size == 4:
|
|
||||||
i = int4read(buf[0:4])
|
|
||||||
elif size == 8:
|
|
||||||
i = int8read(buf[0:8])
|
|
||||||
else:
|
|
||||||
raise ValueError('size should be int in range of 1..4 or 8 (weird)')
|
|
||||||
|
|
||||||
return (buf[size:], int(i))
|
return (buf[size:], res)
|
||||||
|
|
||||||
def read_lc_int(buf):
|
def read_lc_int(buf):
|
||||||
"""
|
"""
|
||||||
|
@ -373,20 +241,5 @@ def read_lc_int(buf):
|
||||||
#
|
#
|
||||||
# For debugging
|
# For debugging
|
||||||
#
|
#
|
||||||
def _dump_buffer(buf, label=None):
|
def _digest_buffer(buf):
|
||||||
import __main__
|
return ''.join([ "\\x%02x" % ord(c) for c in buf ])
|
||||||
if not __main__.__dict__.has_key('__MYSQL_DEBUG__'):
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
debug = __main__.__dict__['__MYSQL_DEBUG__']
|
|
||||||
|
|
||||||
try:
|
|
||||||
if debug:
|
|
||||||
if len(buf) == 0:
|
|
||||||
print "%s : EMPTY BUFFER" % label
|
|
||||||
import string
|
|
||||||
print "%s: %s" % (label,string.join( [ "%02x" % ord(c) for c in buf ], ' '))
|
|
||||||
if debug > 1:
|
|
||||||
print "%s: %s" % (label,string.join( [ "%s" % chr(ord(c)) for c in buf ], ''))
|
|
||||||
except:
|
|
||||||
raise
|
|
||||||
|
|
Loading…
Reference in New Issue