updating the mysql library to 0.3pre

This commit is contained in:
Erik Kristensen 2010-10-21 20:48:38 -04:00
parent 51fbf3eef8
commit d812487d23
11 changed files with 1861 additions and 1816 deletions

View File

@ -1,5 +1,5 @@
# 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)
# This program is free software; you can redistribute it and/or modify
@ -25,15 +25,6 @@
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
apilevel = '2.0'
threadsafety = 1
@ -43,21 +34,22 @@ paramstyle = 'pyformat'
import _version
__version__ = _version.version
from mysql import MySQL
from connection import MySQLConnection
from errors import *
from constants import FieldFlag, FieldType, CharacterSet, RefreshOption
from constants import FieldFlag, FieldType, CharacterSet,\
RefreshOption, ClientFlag
from dbapi import *
def Connect(*args, **kwargs):
"""Shortcut for creating a mysql.MySQL object."""
return MySQL(*args, **kwargs)
"""Shortcut for creating a connection.MySQLConnection object."""
return MySQLConnection(*args, **kwargs)
connect = Connect
__all__ = [
'MySQL', 'Connect',
'MySQLConnection', 'Connect',
# Some useful constants
'FieldType','FieldFlag','CharacterSet','RefreshOption',
'FieldType','FieldFlag','ClientFlag','CharacterSet','RefreshOption',
# Error handling
'Error','Warning',

View File

@ -1,5 +1,5 @@
# 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)
# This program is free software; you can redistribute it and/or modify
@ -25,4 +25,4 @@
"""
# Next line is generated
version = (0, 1, 0, 'devel', '')
version = (0, 3, 0, 'devel', '')

View File

@ -1,5 +1,5 @@
# 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)
# This program is free software; you can redistribute it and/or modify
@ -24,31 +24,35 @@
"""Implementing communication to MySQL servers
"""
import sys
import socket
import logging
import os
import weakref
from collections import deque
import constants
import conversion
import protocol
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.
Should not be used directly but overloaded, changing the
open_connection part. Examples over subclasses are
MySQLTCPConnection
MySQLUNIXConnection
open_connection part. Examples of subclasses are
MySQLTCPSocket
MySQLUnixSocket
"""
def __init__(self, prtcls=None):
def __init__(self):
self.sock = None # holds the socket connection
self.connection_timeout = None
self.protocol = None
self.socket_flags = 0
try:
self.protocol = prtcls(self)
except:
self.protocol = protocol.MySQLProtocol(self)
self._set_socket_flags()
self.buffer = deque()
self.recvsize = 1024*8
def open_connection(self):
pass
@ -58,10 +62,12 @@ class MySQLBaseConnection(object):
self.sock.close()
except:
pass
def get_address(self):
pass
def send(self, buf):
"""
Send packets using the socket to the server.
"""Send packets over the socket
"""
pktlen = len(buf)
try:
@ -69,49 +75,57 @@ class MySQLBaseConnection(object):
pktlen -= self.sock.send(buf)
except Exception, e:
raise errors.OperationalError('%s' % e)
def recv(self):
"""
Receive packets using the socket from the server.
"""Receive packets from the socket
"""
try:
header = self.sock.recv(4, self.socket_flags)
(pktsize, pktnr) = self.protocol.handle_header(header)
buf = header + self.sock.recv(pktsize, self.socket_flags)
self.protocol.is_error(buf)
return self.buffer.popleft()
except IndexError:
pass
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:
raise
return (buf, pktsize, pktnr)
def set_protocol(self, prtcls):
try:
self.protocol = prtcls(self, self.protocol.handshake)
except:
self.protocol = protocol.MySQLProtocol(self)
return self.buffer.popleft()
except IndexError, e:
pass
def set_connection_timeout(self, timeout):
self.connection_timeout = timeout
def _set_socket_flags(self, flags=None):
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):
class MySQLUnixSocket(MySQLBaseSocket):
"""Opens a connection through the UNIX socket of the MySQL Server."""
def __init__(self, prtcls=None,unix_socket='/tmp/mysql.sock'):
MySQLBaseConnection.__init__(self, prtcls=prtcls)
def __init__(self, unix_socket='/tmp/mysql.sock'):
MySQLBaseSocket.__init__(self)
self.unix_socket = unix_socket
self.socket_flags = socket.MSG_WAITALL
def get_address(self):
return self.unix_socket
def open_connection(self):
"""Opens a UNIX socket and checks the MySQL handshake."""
@ -119,19 +133,26 @@ class MySQLUnixConnection(MySQLBaseConnection):
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.sock.settimeout(self.connection_timeout)
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:
raise errors.OperationalError('%s' % e)
raise errors.InterfaceError('%s' % e)
buf = self.recv()[0]
self.protocol.handle_handshake(buf)
class MySQLTCPConnection(MySQLBaseConnection):
class MySQLTCPSocket(MySQLBaseSocket):
"""Opens a TCP connection to the MySQL Server."""
def __init__(self, prtcls=None, host='127.0.0.1', port=3306):
MySQLBaseConnection.__init__(self, prtcls=prtcls)
def __init__(self, host='127.0.0.1', port=3306):
MySQLBaseSocket.__init__(self)
self.server_host = host
self.server_port = port
def get_address(self):
return "%s:%s" % (self.server_host,self.server_port)
def open_connection(self):
"""Opens a TCP Connection and checks the MySQL handshake."""
@ -139,10 +160,453 @@ class MySQLTCPConnection(MySQLBaseConnection):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(self.connection_timeout)
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:
raise errors.OperationalError('%s' % e)
buf = self.recv()[0]
self.protocol.handle_handshake(buf)
raise errors.InterfaceError('%s' % e)
except:
raise
class MySQLConnection(object):
"""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

View File

@ -1,5 +1,5 @@
# 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)
# This program is free software; you can redistribute it and/or modify
@ -26,6 +26,14 @@
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):
prefix = ''
@ -36,21 +44,20 @@ class _constants(object):
@classmethod
def get_desc(cls,name):
res = ''
try:
res = cls.desc[name][1]
except KeyError, e:
raise KeyError, e
else:
return res
return cls.desc[name][1]
except:
return None
@classmethod
def get_info(cls,n):
res = ()
for k,v in cls.desc.items():
if v[0] == n:
return v[1]
raise KeyError, e
try:
res = {}
for v in cls.desc.items():
res[v[1][0]] = v[0]
return res[n]
except:
return None
@classmethod
def get_full_info(cls):
@ -61,7 +68,20 @@ class _constants(object):
res = ('No information found in constant class.%s' % e)
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):
prefix = 'FIELD_TYPE_'
@ -155,7 +175,7 @@ class FieldType(_constants):
cls.DATETIME, cls.TIMESTAMP,
]
class FieldFlag(_constants):
class FieldFlag(_constantflags):
"""
Field flags as found in MySQL sources mysql-src/include/mysql_com.h
"""
@ -213,8 +233,7 @@ class FieldFlag(_constants):
'FIELD_IN_ADD_INDEX': (1 << 20, "Intern: Field used in ADD INDEX"),
'FIELD_IS_RENAMED': (1 << 21, "Intern: Field is being renamed"),
}
class ServerCmd(_constants):
_prefix = 'COM_'
SLEEP = 0
@ -248,7 +267,7 @@ class ServerCmd(_constants):
STMT_FETCH = 28
DAEMON = 29
class ClientFlag(_constants):
class ClientFlag(_constantflags):
"""
Client Options as found in the MySQL sources mysql-src/include/mysql_com.h
"""
@ -314,7 +333,7 @@ class ClientFlag(_constants):
flags |= f
return flags
class ServerFlag(_constants):
class ServerFlag(_constantflags):
"""
Server flags as found in the MySQL sources mysql-src/include/mysql_com.h
"""
@ -364,204 +383,330 @@ class RefreshOption(_constants):
}
class CharacterSet(_constants):
"""
List of supported character sets with their collations. This maps to the
character set we get from the server within the handshake packet.
"""MySQL supported character sets and collations
To update this list, use the following query:
SELECT ID,CHARACTER_SET_NAME, COLLATION_NAME
FROM INFORMATION_SCHEMA.COLLATIONS
ORDER BY ID
List of character sets with their collations supported by MySQL. This
maps to the character set we get from the server within the handshake
packet.
This list is hardcoded because we want to avoid doing each time the above
query to get the name of the character set used.
The list is hardcode so we avoid a database query when getting the
name of the used character set or collation.
"""
_max_id = 211 # SELECT MAX(ID)+1 FROM INFORMATION_SCHEMA.COLLATIONS
@classmethod
def _init_desc(cls):
if not cls.__dict__.has_key('desc'):
# Do not forget to update the tests in test_constants!
cls.desc = [ None for i in range(cls._max_id)]
cls.desc[1] = ('big5','big5_chinese_ci')
cls.desc[2] = ('latin2','latin2_czech_cs')
cls.desc[3] = ('dec8','dec8_swedish_ci')
cls.desc[4] = ('cp850','cp850_general_ci')
cls.desc[5] = ('latin1','latin1_german1_ci')
cls.desc[6] = ('hp8','hp8_english_ci')
cls.desc[7] = ('koi8r','koi8r_general_ci')
cls.desc[8] = ('latin1','latin1_swedish_ci')
cls.desc[9] = ('latin2','latin2_general_ci')
cls.desc[10] = ('swe7','swe7_swedish_ci')
cls.desc[11] = ('ascii','ascii_general_ci')
cls.desc[12] = ('ujis','ujis_japanese_ci')
cls.desc[13] = ('sjis','sjis_japanese_ci')
cls.desc[14] = ('cp1251','cp1251_bulgarian_ci')
cls.desc[15] = ('latin1','latin1_danish_ci')
cls.desc[16] = ('hebrew','hebrew_general_ci')
cls.desc[18] = ('tis620','tis620_thai_ci')
cls.desc[19] = ('euckr','euckr_korean_ci')
cls.desc[20] = ('latin7','latin7_estonian_cs')
cls.desc[21] = ('latin2','latin2_hungarian_ci')
cls.desc[22] = ('koi8u','koi8u_general_ci')
cls.desc[23] = ('cp1251','cp1251_ukrainian_ci')
cls.desc[24] = ('gb2312','gb2312_chinese_ci')
cls.desc[25] = ('greek','greek_general_ci')
cls.desc[26] = ('cp1250','cp1250_general_ci')
cls.desc[27] = ('latin2','latin2_croatian_ci')
cls.desc[28] = ('gbk','gbk_chinese_ci')
cls.desc[29] = ('cp1257','cp1257_lithuanian_ci')
cls.desc[30] = ('latin5','latin5_turkish_ci')
cls.desc[31] = ('latin1','latin1_german2_ci')
cls.desc[32] = ('armscii8','armscii8_general_ci')
cls.desc[33] = ('utf8','utf8_general_ci')
cls.desc[34] = ('cp1250','cp1250_czech_cs')
cls.desc[35] = ('ucs2','ucs2_general_ci')
cls.desc[36] = ('cp866','cp866_general_ci')
cls.desc[37] = ('keybcs2','keybcs2_general_ci')
cls.desc[38] = ('macce','macce_general_ci')
cls.desc[39] = ('macroman','macroman_general_ci')
cls.desc[40] = ('cp852','cp852_general_ci')
cls.desc[41] = ('latin7','latin7_general_ci')
cls.desc[42] = ('latin7','latin7_general_cs')
cls.desc[43] = ('macce','macce_bin')
cls.desc[44] = ('cp1250','cp1250_croatian_ci')
cls.desc[47] = ('latin1','latin1_bin')
cls.desc[48] = ('latin1','latin1_general_ci')
cls.desc[49] = ('latin1','latin1_general_cs')
cls.desc[50] = ('cp1251','cp1251_bin')
cls.desc[51] = ('cp1251','cp1251_general_ci')
cls.desc[52] = ('cp1251','cp1251_general_cs')
cls.desc[53] = ('macroman','macroman_bin')
cls.desc[57] = ('cp1256','cp1256_general_ci')
cls.desc[58] = ('cp1257','cp1257_bin')
cls.desc[59] = ('cp1257','cp1257_general_ci')
cls.desc[63] = ('binary','binary')
cls.desc[64] = ('armscii8','armscii8_bin')
cls.desc[65] = ('ascii','ascii_bin')
cls.desc[66] = ('cp1250','cp1250_bin')
cls.desc[67] = ('cp1256','cp1256_bin')
cls.desc[68] = ('cp866','cp866_bin')
cls.desc[69] = ('dec8','dec8_bin')
cls.desc[70] = ('greek','greek_bin')
cls.desc[71] = ('hebrew','hebrew_bin')
cls.desc[72] = ('hp8','hp8_bin')
cls.desc[73] = ('keybcs2','keybcs2_bin')
cls.desc[74] = ('koi8r','koi8r_bin')
cls.desc[75] = ('koi8u','koi8u_bin')
cls.desc[77] = ('latin2','latin2_bin')
cls.desc[78] = ('latin5','latin5_bin')
cls.desc[79] = ('latin7','latin7_bin')
cls.desc[80] = ('cp850','cp850_bin')
cls.desc[81] = ('cp852','cp852_bin')
cls.desc[82] = ('swe7','swe7_bin')
cls.desc[83] = ('utf8','utf8_bin')
cls.desc[84] = ('big5','big5_bin')
cls.desc[85] = ('euckr','euckr_bin')
cls.desc[86] = ('gb2312','gb2312_bin')
cls.desc[87] = ('gbk','gbk_bin')
cls.desc[88] = ('sjis','sjis_bin')
cls.desc[89] = ('tis620','tis620_bin')
cls.desc[90] = ('ucs2','ucs2_bin')
cls.desc[91] = ('ujis','ujis_bin')
cls.desc[92] = ('geostd8','geostd8_general_ci')
cls.desc[93] = ('geostd8','geostd8_bin')
cls.desc[94] = ('latin1','latin1_spanish_ci')
cls.desc[95] = ('cp932','cp932_japanese_ci')
cls.desc[96] = ('cp932','cp932_bin')
cls.desc[97] = ('eucjpms','eucjpms_japanese_ci')
cls.desc[98] = ('eucjpms','eucjpms_bin')
cls.desc[128] = ('ucs2','ucs2_unicode_ci')
cls.desc[129] = ('ucs2','ucs2_icelandic_ci')
cls.desc[130] = ('ucs2','ucs2_latvian_ci')
cls.desc[131] = ('ucs2','ucs2_romanian_ci')
cls.desc[132] = ('ucs2','ucs2_slovenian_ci')
cls.desc[133] = ('ucs2','ucs2_polish_ci')
cls.desc[134] = ('ucs2','ucs2_estonian_ci')
cls.desc[135] = ('ucs2','ucs2_spanish_ci')
cls.desc[136] = ('ucs2','ucs2_swedish_ci')
cls.desc[137] = ('ucs2','ucs2_turkish_ci')
cls.desc[138] = ('ucs2','ucs2_czech_ci')
cls.desc[139] = ('ucs2','ucs2_danish_ci')
cls.desc[140] = ('ucs2','ucs2_lithuanian_ci')
cls.desc[141] = ('ucs2','ucs2_slovak_ci')
cls.desc[142] = ('ucs2','ucs2_spanish2_ci')
cls.desc[143] = ('ucs2','ucs2_roman_ci')
cls.desc[144] = ('ucs2','ucs2_persian_ci')
cls.desc[145] = ('ucs2','ucs2_esperanto_ci')
cls.desc[146] = ('ucs2','ucs2_hungarian_ci')
cls.desc[192] = ('utf8','utf8_unicode_ci')
cls.desc[193] = ('utf8','utf8_icelandic_ci')
cls.desc[194] = ('utf8','utf8_latvian_ci')
cls.desc[195] = ('utf8','utf8_romanian_ci')
cls.desc[196] = ('utf8','utf8_slovenian_ci')
cls.desc[197] = ('utf8','utf8_polish_ci')
cls.desc[198] = ('utf8','utf8_estonian_ci')
cls.desc[199] = ('utf8','utf8_spanish_ci')
cls.desc[200] = ('utf8','utf8_swedish_ci')
cls.desc[201] = ('utf8','utf8_turkish_ci')
cls.desc[202] = ('utf8','utf8_czech_ci')
cls.desc[203] = ('utf8','utf8_danish_ci')
cls.desc[204] = ('utf8','utf8_lithuanian_ci')
cls.desc[205] = ('utf8','utf8_slovak_ci')
cls.desc[206] = ('utf8','utf8_spanish2_ci')
cls.desc[207] = ('utf8','utf8_roman_ci')
cls.desc[208] = ('utf8','utf8_persian_ci')
cls.desc[209] = ('utf8','utf8_esperanto_ci')
cls.desc[210] = ('utf8','utf8_hungarian_ci')
desc = [
# (character set name, collation, default)
None,
("big5","big5_chinese_ci",True), # 1
("latin2","latin2_czech_cs",False), # 2
("dec8","dec8_swedish_ci",True), # 3
("cp850","cp850_general_ci",True), # 4
("latin1","latin1_german1_ci",False), # 5
("hp8","hp8_english_ci",True), # 6
("koi8r","koi8r_general_ci",True), # 7
("latin1","latin1_swedish_ci",True), # 8
("latin2","latin2_general_ci",True), # 9
("swe7","swe7_swedish_ci",True), # 10
("ascii","ascii_general_ci",True), # 11
("ujis","ujis_japanese_ci",True), # 12
("sjis","sjis_japanese_ci",True), # 13
("cp1251","cp1251_bulgarian_ci",False), # 14
("latin1","latin1_danish_ci",False), # 15
("hebrew","hebrew_general_ci",True), # 16
None,
("tis620","tis620_thai_ci",True), # 18
("euckr","euckr_korean_ci",True), # 19
("latin7","latin7_estonian_cs",False), # 20
("latin2","latin2_hungarian_ci",False), # 21
("koi8u","koi8u_general_ci",True), # 22
("cp1251","cp1251_ukrainian_ci",False), # 23
("gb2312","gb2312_chinese_ci",True), # 24
("greek","greek_general_ci",True), # 25
("cp1250","cp1250_general_ci",True), # 26
("latin2","latin2_croatian_ci",False), # 27
("gbk","gbk_chinese_ci",True), # 28
("cp1257","cp1257_lithuanian_ci",False), # 29
("latin5","latin5_turkish_ci",True), # 30
("latin1","latin1_german2_ci",False), # 31
("armscii8","armscii8_general_ci",True), # 32
("utf8","utf8_general_ci",True), # 33
("cp1250","cp1250_czech_cs",False), # 34
("ucs2","ucs2_general_ci",True), # 35
("cp866","cp866_general_ci",True), # 36
("keybcs2","keybcs2_general_ci",True), # 37
("macce","macce_general_ci",True), # 38
("macroman","macroman_general_ci",True), # 39
("cp852","cp852_general_ci",True), # 40
("latin7","latin7_general_ci",True), # 41
("latin7","latin7_general_cs",False), # 42
("macce","macce_bin",False), # 43
("cp1250","cp1250_croatian_ci",False), # 44
None,
None,
("latin1","latin1_bin",False), # 47
("latin1","latin1_general_ci",False), # 48
("latin1","latin1_general_cs",False), # 49
("cp1251","cp1251_bin",False), # 50
("cp1251","cp1251_general_ci",True), # 51
("cp1251","cp1251_general_cs",False), # 52
("macroman","macroman_bin",False), # 53
None,
None,
None,
("cp1256","cp1256_general_ci",True), # 57
("cp1257","cp1257_bin",False), # 58
("cp1257","cp1257_general_ci",True), # 59
None,
None,
None,
("binary","binary",True), # 63
("armscii8","armscii8_bin",False), # 64
("ascii","ascii_bin",False), # 65
("cp1250","cp1250_bin",False), # 66
("cp1256","cp1256_bin",False), # 67
("cp866","cp866_bin",False), # 68
("dec8","dec8_bin",False), # 69
("greek","greek_bin",False), # 70
("hebrew","hebrew_bin",False), # 71
("hp8","hp8_bin",False), # 72
("keybcs2","keybcs2_bin",False), # 73
("koi8r","koi8r_bin",False), # 74
("koi8u","koi8u_bin",False), # 75
None,
("latin2","latin2_bin",False), # 77
("latin5","latin5_bin",False), # 78
("latin7","latin7_bin",False), # 79
("cp850","cp850_bin",False), # 80
("cp852","cp852_bin",False), # 81
("swe7","swe7_bin",False), # 82
("utf8","utf8_bin",False), # 83
("big5","big5_bin",False), # 84
("euckr","euckr_bin",False), # 85
("gb2312","gb2312_bin",False), # 86
("gbk","gbk_bin",False), # 87
("sjis","sjis_bin",False), # 88
("tis620","tis620_bin",False), # 89
("ucs2","ucs2_bin",False), # 90
("ujis","ujis_bin",False), # 91
("geostd8","geostd8_general_ci",True), # 92
("geostd8","geostd8_bin",False), # 93
("latin1","latin1_spanish_ci",False), # 94
("cp932","cp932_japanese_ci",True), # 95
("cp932","cp932_bin",False), # 96
("eucjpms","eucjpms_japanese_ci",True), # 97
("eucjpms","eucjpms_bin",False), # 98
("cp1250","cp1250_polish_ci",False), # 99
None,
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,
("ucs2","ucs2_unicode_ci",False), # 128
("ucs2","ucs2_icelandic_ci",False), # 129
("ucs2","ucs2_latvian_ci",False), # 130
("ucs2","ucs2_romanian_ci",False), # 131
("ucs2","ucs2_slovenian_ci",False), # 132
("ucs2","ucs2_polish_ci",False), # 133
("ucs2","ucs2_estonian_ci",False), # 134
("ucs2","ucs2_spanish_ci",False), # 135
("ucs2","ucs2_swedish_ci",False), # 136
("ucs2","ucs2_turkish_ci",False), # 137
("ucs2","ucs2_czech_ci",False), # 138
("ucs2","ucs2_danish_ci",False), # 139
("ucs2","ucs2_lithuanian_ci",False), # 140
("ucs2","ucs2_slovak_ci",False), # 141
("ucs2","ucs2_spanish2_ci",False), # 142
("ucs2","ucs2_roman_ci",False), # 143
("ucs2","ucs2_persian_ci",False), # 144
("ucs2","ucs2_esperanto_ci",False), # 145
("ucs2","ucs2_hungarian_ci",False), # 146
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
def get_info(cls,setid):
"""Returns information about the charset for given MySQL ID."""
cls._init_desc()
res = ()
errmsg = "Character set with id '%d' unsupported." % (setid)
"""Retrieves character set information as tuple using an ID
Retrieves character set and collation information based on the
given MySQL ID.
Returns a tuple.
"""
try:
res = cls.desc[setid]
r = cls.desc[setid]
if r is None:
raise
return r[0:2]
except:
raise ProgrammingError, errmsg
if res is None:
raise ProgrammingError, errmsg
return res
raise ProgrammingError("Character set '%d' unsupported" % (setid))
@classmethod
def get_desc(cls,setid):
"""Returns info string about the charset for given MySQL ID."""
res = ()
"""Retrieves character set information as string using an ID
Retrieves character set and collation information based on the
given MySQL ID.
Returns a tuple.
"""
try:
res = "%s/%s" % self.get_info(setid)
except ProgrammingError, e:
return "%s/%s" % cls.get_info(setid)
except:
raise
else:
return res
@classmethod
def get_charset_info(cls, name, collation=None):
"""Returns information about the charset and optional collation."""
cls._init_desc()
l = len(cls.desc)
errmsg = "Character set '%s' unsupported." % (name)
def get_default_collation(cls, charset):
"""Retrieves the default collation for given character set
Raises ProgrammingError when character set is not supported.
Returns list (collation, charset, index)
"""
if isinstance(charset, int):
try:
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:
collation = '%s_general_ci' % (name)
# Search the list and return when found
idx = 0
for info in cls.desc:
if info and info[0] == name and info[1] == collation:
return (idx,info[0],info[1])
idx += 1
# If we got here, we didn't find the charset
raise ProgrammingError, errmsg
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
if idx is not None:
return (idx,charset,collation)
else:
raise ProgrammingError("Character set '%s' unsupported." % (
charset))
@classmethod
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 = []
for info in cls.desc:
if info and info[0] not in res:

View File

@ -1,5 +1,5 @@
# 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)
# This program is free software; you can redistribute it and/or modify
@ -24,8 +24,7 @@
"""Converting MySQL and Python types
"""
from types import NoneType
import re
import struct
import datetime
import time
from decimal import Decimal
@ -79,8 +78,7 @@ class MySQLConverter(ConverterBase):
"""
def __init__(self, charset=None, use_unicode=True):
ConverterBase.__init__(self, charset, use_unicode)
# Python types
self.python_types = {
int : int,
str : self._str_to_mysql,
@ -88,7 +86,7 @@ class MySQLConverter(ConverterBase):
float : float,
unicode : self._unicode_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.date : self._date_to_mysql,
datetime.time : self._time_to_mysql,
@ -97,7 +95,6 @@ class MySQLConverter(ConverterBase):
Decimal : self._decimal_to_mysql,
}
# MySQL types
self.mysql_types = {
FieldType.TINY : self._int,
FieldType.SHORT : self._int,
@ -116,7 +113,9 @@ class MySQLConverter(ConverterBase):
FieldType.NEWDATE : self._DATE_to_python,
FieldType.DATETIME : 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):
@ -131,9 +130,8 @@ class MySQLConverter(ConverterBase):
return value
elif isinstance(value, (int,float,long,Decimal)):
return value
backslash = re.compile(r'\134')
res = value
res = backslash.sub(r'\\\\', res)
res = res.replace('\\','\\\\')
res = res.replace('\n','\\n')
res = res.replace('\r','\\r')
res = res.replace('\047','\134\047') # single quotes
@ -152,7 +150,7 @@ class MySQLConverter(ConverterBase):
"""
if isinstance(buf, (int,float,long,Decimal)):
return str(buf)
elif isinstance(buf, NoneType):
elif isinstance(buf, type(None)):
return "NULL"
else:
# 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.
Returns a string or None when not valid.
Returns a string.
"""
if isinstance(value, datetime.datetime):
return value.strftime('%Y-%m-%d %H:%M:%S')
return None
return '%d-%02d-%02d %02d:%02d:%02d' % (
value.year, value.month, value.day,
value.hour, value.minute, value.second)
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.
Returns a string or None when not valid.
Returns a string.
"""
if isinstance(value, datetime.date):
return value.strftime('%Y-%m-%d')
return None
return '%d-%02d-%02d' % (value.year, value.month, value.day)
def _time_to_mysql(self, value):
"""
@ -226,10 +219,7 @@ class MySQLConverter(ConverterBase):
Returns a string or None when not valid.
"""
if isinstance(value, datetime.time):
return value.strftime('%H:%M:%S')
return None
return value.strftime('%H:%M:%S')
def _struct_time_to_mysql(self, value):
"""
@ -239,24 +229,19 @@ class MySQLConverter(ConverterBase):
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 None
return time.strftime('%Y-%m-%d %H:%M:%S',value)
def _timedelta_to_mysql(self, value):
"""
Converts a timedelta instance to a string suitable for MySQL.
The returned string has format: %H:%M:%S
Returns a string or None when not valid.
Returns a string.
"""
if isinstance(value, datetime.timedelta):
secs = value.seconds%60
mins = value.seconds%3600/60
hours = value.seconds/3600+(value.days*24)
return '%d:%02d:%02d' % (hours,mins,secs)
return None
(hours, r) = divmod(value.seconds, 3600)
(mins, secs) = divmod(r, 60)
hours = hours + (value.days * 24)
return '%02d:%02d:%02d' % (hours,mins,secs)
def _decimal_to_mysql(self, value):
"""
@ -280,7 +265,7 @@ class MySQLConverter(ConverterBase):
"""
res = value
if value == '\x00':
if value == '\x00' and flddsc[1] != FieldType.BIT:
# Don't go further when we hit a NULL value
return None
if value is None:
@ -316,7 +301,7 @@ class MySQLConverter(ConverterBase):
"""
Returns v as long type.
"""
return long(v)
return int(v)
def _decimal(self, v, desc=None):
"""
@ -330,6 +315,13 @@ class MySQLConverter(ConverterBase):
"""
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):
"""
Returns DATE column type as datetime.date type.
@ -361,24 +353,37 @@ class MySQLConverter(ConverterBase):
"""
pv = None
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:
pv = None
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):
"""
"""Returns SET column typs as set
Actually, MySQL protocol sees a SET as a string type field. So this
code isn't called directly, but used by STRING_to_python() method.
Returns SET column type as string splitted using a comma.
Returns SET column type as a set.
"""
pv = None
try:
pv = v.split(',')
pv = set(v.split(','))
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
def _STRING_to_python(self, v, dsc=None):
@ -392,6 +397,8 @@ class MySQLConverter(ConverterBase):
# Check if we deal with a SET
if dsc[7] & FieldFlag.SET:
return self._SET_to_python(v, dsc)
if dsc[7] & FieldFlag.BINARY:
return v
if self.use_unicode:
try:
@ -399,3 +406,11 @@ class MySQLConverter(ConverterBase):
except:
raise
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)

View File

@ -1,5 +1,5 @@
# 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)
# This program is free software; you can redistribute it and/or modify
@ -24,14 +24,20 @@
"""Cursor classes
"""
import exceptions
import sys
from collections import deque
import weakref
import re
import mysql
import connection
import constants
import protocol
import errors
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):
"""
Base for defining MySQLCursor. This class is a skeleton and defines
@ -78,6 +84,9 @@ class CursorBase(object):
def setoutputsize(self, size, column=None):
pass
def reset(self):
pass
class MySQLCursor(CursorBase):
"""
@ -100,17 +109,15 @@ class MySQLCursor(CursorBase):
def __init__(self, db=None):
CursorBase.__init__(self)
self.db = None
self.fields = ()
self.nrflds = 0
self._result = []
self._more_results = False
self._results = deque()
self._nextrow = (None, None)
self.lastrowid = None
self._warnings = None
self._warning_count = 0
self._executed = None
self._have_result = False
self._get_warnings = False
self._raise_on_warnings = True
if db is not None:
self.set_connection(db)
@ -120,41 +127,28 @@ class MySQLCursor(CursorBase):
and returns the next row.
"""
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):
if isinstance(db,mysql.MySQLBase):
if self._valid_protocol(db):
self.db = db
self.protocol = db.conn.protocol
self.db.register_cursor(self)
self._get_warnings = self.db.get_warnings
else:
raise errors.InterfaceError(
"MySQLCursor db-argument must subclass of mysql.MySQLBase")
try:
if isinstance(db.protocol,protocol.MySQLProtocol):
self.db = weakref.ref(db)
if self not in self.db().cursors:
self.db().cursors.append(self)
except:
raise errors.InterfaceError(errno=2048)
def _reset_result(self):
del self._result[:]
self.rowcount = -1
self._nextrow = (None, None)
self._have_result = False
try:
self.db().unread_result = False
except:
pass
self._warnings = None
self._warning_count = 0
self._fields = ()
self.description = ()
self.reset()
def next(self):
"""
@ -177,20 +171,21 @@ class MySQLCursor(CursorBase):
"""
if self.db is None:
return False
try:
self.db.remove_cursor(self)
self._reset_result()
self.db().remove_cursor(self)
self.db = None
except:
return False
del self._result[:]
return True
def _process_params_dict(self, params):
try:
to_mysql = self.db.converter.to_mysql
escape = self.db.converter.escape
quote = self.db.converter.quote
to_mysql = self.db().converter.to_mysql
escape = self.db().converter.escape
quote = self.db().converter.quote
res = {}
for k,v in params.items():
c = v
@ -222,9 +217,9 @@ class MySQLCursor(CursorBase):
try:
res = params
to_mysql = self.db.converter.to_mysql
escape = self.db.converter.escape
quote = self.db.converter.quote
to_mysql = self.db().converter.to_mysql
escape = self.db().converter.escape
quote = self.db().converter.quote
res = map(to_mysql,res)
res = map(escape,res)
@ -235,35 +230,11 @@ class MySQLCursor(CursorBase):
else:
return tuple(res)
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):
res = ()
try:
to_python = self.db.converter.to_python
to_python = self.db().converter.to_python
if not desc:
desc = self.description
for idx,v in enumerate(rowdata):
@ -278,19 +249,34 @@ class MySQLCursor(CursorBase):
return None
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:
self.rowcount = res.affected_rows
self.lastrowid = res.insert_id
self._warning_count = res.warning_count
if self._get_warnings is True and self._warning_count:
self.rowcount = res['affected_rows']
self.lastrowid = res['insert_id']
self._warning_count = res['warning_count']
if self.db().get_warnings is True and self._warning_count:
self._warnings = self._fetch_warnings()
self._set_more_results(res['server_status'])
except errors.Error:
raise
except StandardError, e:
raise errors.ProgrammingError(
"Failed handling non-resultset; %s" % e)
def _handle_resultset(self):
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):
"""
@ -306,32 +292,34 @@ class MySQLCursor(CursorBase):
"""
if not operation:
return 0
if self.db().unread_result is True:
raise errors.InternalError("Unread result found.")
self._reset_result()
stmt = ''
# Make sure we send the query in correct character set
try:
if isinstance(operation, unicode):
operation.encode(self.db.charset_name)
operation = operation.encode(self.db().charset_name)
if params is not None:
stmt = operation % self._process_params(params)
try:
stmt = operation % self._process_params(params)
except TypeError:
raise errors.ProgrammingError(
"Wrong number of arguments during string formatting")
else:
stmt = operation
res = self.protocol.cmd_query(stmt)
if isinstance(res, protocol.OKResultPacket):
self._have_result = False
self._handle_noresultset(res)
else:
self.description = self._get_description(res)
self._have_result = True
self._handle_resultset()
except errors.ProgrammingError:
raise
except errors.OperationalError:
res = self.db().protocol.cmd_query(stmt)
self._handle_result(res)
except (UnicodeDecodeError,UnicodeEncodeError), e:
raise errors.ProgrammingError(str(e))
except errors.Error:
raise
except StandardError, e:
raise errors.InterfaceError(
"Failed executing the operation; %s" % e)
raise errors.InterfaceError, errors.InterfaceError(
"Failed executing the operation; %s" % e), sys.exc_info()[2]
else:
self._executed = stmt
return self.rowcount
@ -339,45 +327,99 @@ class MySQLCursor(CursorBase):
return 0
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:
return 0
rowcnt = 0
try:
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:
self.execute(operation, params)
if self._have_result:
self.fetchall()
rowcnt += self.rowcount
except (ValueError,TypeError), e:
raise errors.InterfaceError(
"Failed executing the operation; %s" % e)
except:
# Raise whatever execute() raises
raise
return rowcnt
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
try:
for params in seq_params:
self.execute(operation, params)
if self._have_result:
self.fetchall()
rowcnt += self.rowcount
except (ValueError,TypeError), e:
raise errors.InterfaceError(
"Failed executing the operation; %s" % e)
except:
# Raise whatever execute() raises
raise
self.rowcount = rowcnt
return self.rowcount
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=()):
"""Calls a stored procedue with the given arguments
The arguments will be set during this session, meaning
they will be called like _<procname>__arg<nr> where
<nr> is an enumeration (+1) of the arguments.
Coding Example:
1) Definining the Stored Routine in MySQL:
CREATE PROCEDURE multiply(IN pFac1 INT, IN pFac2 INT, OUT pProd INT)
BEGIN
SET pProd := pFac1 * pFac2;
END
2) Executing in Python:
args = (5,5,0) # 0 is to hold pprod
cursor.callproc(multiply, args)
print cursor.fetchone()
The last print should output ('5', '5', 25L)
Does not return a value, but a result set will be
@ -385,24 +427,36 @@ class MySQLCursor(CursorBase):
Raises exceptions when something is wrong.
"""
argfmt = "@_%s_arg%d"
self._results = deque()
try:
procargs = self._process_params(args)
argnames = []
for idx,arg in enumerate(procargs):
argname = argfmt % (procname, idx+1)
argnames.append(argname)
setquery = "SET %s=%%s" % argname
self.execute(setquery, (arg,))
call = "CALL %s(%s)" % (procname,','.join(argnames))
res = self.protocol.cmd_query(call)
res = self.db().protocol.cmd_query(call)
select = "SELECT %s" % ','.join(argnames)
self.execute(select)
except errors.ProgrammingError:
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)
self.execute(select)
return self.fetchone()
except:
raise
except errors.Error:
raise
except StandardError, e:
raise errors.InterfaceError(
@ -420,13 +474,17 @@ class MySQLCursor(CursorBase):
"""
res = []
try:
c = self.db.cursor()
c = self.db().cursor()
cnt = c.execute("SHOW WARNINGS")
res = c.fetchall()
c.close()
except StandardError, e:
raise errors.ProgrammingError(
raise errors.InterfaceError(
"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:
if len(res):
return res
@ -435,22 +493,25 @@ class MySQLCursor(CursorBase):
def _handle_eof(self, eof):
self._have_result = False
self.db().unread_result = False
self._nextrow = (None, None)
self._warning_count = eof.warning_count
if self.db.get_warnings is True and eof.warning_count:
self._warning_count = eof['warning_count']
if self.db().get_warnings is True and eof['warning_count']:
self._warnings = self._fetch_warnings()
self._set_more_results(eof['status_flag'])
def _fetch_row(self):
if self._have_result is False:
return None
row = None
try:
if self._nextrow == (None, None):
(row, eof) = self.protocol.result_get_row()
(row, eof) = self.db().protocol.get_row()
else:
(row, eof) = self._nextrow
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:
self._handle_eof(eof)
if self.rowcount == -1:
@ -491,11 +552,15 @@ class MySQLCursor(CursorBase):
raise errors.InterfaceError("No result set to fetch from.")
res = []
row = None
while self._have_result:
while self.db().unread_result:
row = self.fetchone()
if row:
res.append(row)
return res
@property
def column_names(self):
return tuple( [d[0].decode('utf8') for d in self.description] )
def __unicode__(self):
fmt = "MySQLCursor: %s"
@ -516,27 +581,71 @@ class MySQLCursorBuffered(MySQLCursor):
def __init__(self, db=None):
MySQLCursor.__init__(self, db)
self._rows = []
self._rows = None
self._next_row = 0
def _handle_resultset(self):
self._get_all_rows()
def _get_all_rows(self):
(self._rows, eof) = self.protocol.result_get_rows()
(self._rows, eof) = self.db().protocol.get_rows()
self.rowcount = len(self._rows)
self._handle_eof(eof)
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):
row = None
try:
row = self._rows[self._next_row]
except:
self._have_result = False
return None
else:
self._next_row += 1
return row
return None
def fetchall(self):
if self._rows is None:
raise errors.InterfaceError("No result set to fetch from.")
res = []
for row in self._rows:
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 ]

View File

@ -1,5 +1,5 @@
# 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)
# This program is free software; you can redistribute it and/or modify

View File

@ -1,5 +1,5 @@
# 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)
# This program is free software; you can redistribute it and/or modify
@ -24,32 +24,152 @@
"""Python exceptions
"""
import exceptions
import protocol
import logging
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):
def __init__(self, m):
if isinstance(m,protocol.ErrorResultPacket):
def __init__(self, m, errno=None, values=None):
try:
# process MySQL error packet
self._process_packet(m)
else:
# else the message should be a string
self.errno = -1
self.errmsg = str(m)
except:
self.errno = errno or -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):
self.errno = packet.errno
self.errmsg = packet.errmsg
self.sqlstate = packet.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:
m = '%d: %s' % (self.errno, self.errmsg)
self.errmsglong = m
self.msg = m
self.msg = '%d: %s' % (self.errno, packet.errmsg)
def __str__(self):
return self.msg
@ -61,12 +181,12 @@ class Warning(StandardError):
pass
class InterfaceError(Error):
def __init__(self, msg):
Error.__init__(self, msg)
def __init__(self, m=None, errno=None, values=None):
Error.__init__(self, m, errno, values)
class DatabaseError(Error):
def __init__(self, msg):
Error.__init__(self, msg)
def __init__(self, m=None, errno=None, values=None):
Error.__init__(self, m, errno, values)
class InternalError(DatabaseError):
pass

View File

@ -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

View File

@ -1,5 +1,5 @@
# 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)
# This program is free software; you can redistribute it and/or modify
@ -28,91 +28,22 @@ __MYSQL_DEBUG__ = False
import struct
def int1read(c):
"""
Takes a bytes and returns it was an integer.
Returns integer.
"""
if isinstance(c,int):
if c < 0 or c > 254:
raise ValueError('excepts int 0 <= x <= 254')
return c
elif len(c) > 1:
raise ValueError('excepts 1 byte long bytes-object or int')
return int('%02x' % ord(c),16)
def int2read(s):
"""
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 intread(b):
"""Unpacks the given buffer to an integer"""
try:
if isinstance(b,int):
return b
l = len(b)
if l == 1:
return int(ord(b))
if l <= 4:
tmp = b + '\x00'*(4-l)
return struct.unpack('<I', tmp)[0]
else:
tmp = b + '\x00'*(8-l)
return struct.unpack('<Q', tmp)[0]
except:
raise
def int1store(i):
"""
@ -221,81 +152,32 @@ def read_lc_string(buf):
it's a NULL and we return None as value.
Returns a tuple (trucated buffer, string).
"""
"""
if buf[0] == '\xfb':
# NULL value
return (buf[1:], None)
l = lsize = start = 0
fst = buf[0]
# Remove the type byte, we got the length information.
buf = buf[1:]
l = lsize = 0
fst = ord(buf[0])
if fst <= '\xFA':
# Returns result right away.
l = ord(fst)
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])
# 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:]
return (buf, s)
if fst <= 250:
l = fst
return (buf[1+l:], buf[1:l+1])
def read_lc_string_list(buf):
"""
Reads all length encoded strings from the given buffer.
lsize = fst - 250
l = intread(buf[1:lsize+1])
return (buf[lsize+l+1:], buf[lsize+1:l+lsize+1])
This is exact same function as read_lc_string() but duplicated
in hopes for performance gain when reading results.
def read_lc_string_list(buf):
"""Reads all length encoded strings from the given buffer
Returns a list of strings
"""
strlst = []
while buf:
if buf[0] == '\xfb':
# NULL value
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)
(buf, b) = read_lc_string(buf)
strlst.append(b)
return strlst
@ -320,31 +202,17 @@ def read_string(buf, end=None, size=None):
raise ValueError('read_string() needs either end or size (weird)')
def read_int(buf, size):
"""
Take a buffer and reads an integer of a certain size (1 <= size <= 4).
"""Read an integer from buffer
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
if size == 1:
i = int1read(buf[0])
elif size == 2:
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))
try:
res = intread(buf[0:size])
except:
raise
return (buf[size:], res)
def read_lc_int(buf):
"""
@ -373,20 +241,5 @@ def read_lc_int(buf):
#
# For debugging
#
def _dump_buffer(buf, label=None):
import __main__
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
def _digest_buffer(buf):
return ''.join([ "\\x%02x" % ord(c) for c in buf ])