#
# Copyright (C) 2008, 2009, 2010 Bozhin Zafirov
#
#
# 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, either version 3 of the License, or
# (at your option) any later version.
#
# 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, see <http://www.gnu.org/licenses/>.
#
from datetime import date as Date
from datetime import time as Time
from datetime import datetime as Timestamp
import time
from mysql import _mysql
# global variables
apilevel = '1.0'
threadsafety = 1
paramstyle = 'pyformat'
# date and time
def DateFromTicks(ticks):
return Date(*time.localtime(ticks)[:3])
def TimeFromTicks(ticks):
return Time(*time.localtime(ticks)[3:6])
def TimestampFromTicks(ticks):
return Time(*time.localtime(ticks)[:6])
# exception types
class Warning(Exception): pass
class Error(Exception): pass
class InterfaceError(Error): pass
class DatabaseError(Error): pass
class DataError(DatabaseError): pass
class OperationalError(DatabaseError): pass
class IntegrityError(DatabaseError): pass
class InternalError(DatabaseError): pass
class ProgrammingError(DatabaseError): pass
class NotSupportedError(DatabaseError): pass
# data types
class DBAPITypeObject(object):
def __init__(self, *values):
self.__values = values
def __eq__(self, other):
return other in self.__values
# type objects
STRING = DBAPITypeObject(
_mysql.MYSQL_TYPE_VARCHAR,
_mysql.MYSQL_TYPE_ENUM,
_mysql.MYSQL_TYPE_STRING,
_mysql.MYSQL_TYPE_VAR_STRING,
)
BINARY = DBAPITypeObject(
_mysql.MYSQL_TYPE_TINY_BLOB,
_mysql.MYSQL_TYPE_MEDIUM_BLOB,
_mysql.MYSQL_TYPE_LONG_BLOB,
_mysql.MYSQL_TYPE_BLOB,
)
NUMBER = DBAPITypeObject(
_mysql.MYSQL_TYPE_DECIMAL,
_mysql.MYSQL_TYPE_TINY,
_mysql.MYSQL_TYPE_SHORT,
_mysql.MYSQL_TYPE_LONG,
_mysql.MYSQL_TYPE_FLOAT,
_mysql.MYSQL_TYPE_DOUBLE,
_mysql.MYSQL_TYPE_LONGLONG,
_mysql.MYSQL_TYPE_INT24,
_mysql.MYSQL_TYPE_BIT,
)
DATETIME = DBAPITypeObject(
_mysql.MYSQL_TYPE_TIMESTAMP,
_mysql.MYSQL_TYPE_DATE,
_mysql.MYSQL_TYPE_TIME,
_mysql.MYSQL_TYPE_DATETIME,
_mysql.MYSQL_TYPE_YEAR,
_mysql.MYSQL_TYPE_NEWDATE,
)
# does this have a type for MySQL?
ROWID = DBAPITypeObject()
# cursor object definition
class CursorObject(object):
# class constructor
def __init__(self, m, conn):
self.__mysql = m
# array size
self.arraysize = 1
self.__res = []
self.__connection = conn
self.__rownumber = 0
self.__rowcount = 0
self.__lastrowid = None
def callproc(self, *_):
raise NotSupportedError('callproc is not supported.')
# close cursor
def close(self):
self.__mysql = None
# set result descriptions
def __setDescription(self):
description = []
if len(self.__res) and self.__res[0] is not None:
while True:
f = _mysql.fetch_field(self.__res[0])
if f is None:
break
description.append(
(f.name, f.type, f.max_length, f.length, f.length, f.decimals, None)
)
self.__description = tuple(description)
# execute query
def __execute(self, operation, parameters):
query = operation % parameters
if not _mysql.real_query(self.__mysql, query):
raise OperationalError(_mysql.error(self.__mysql))
# get some useful data
self.__res.append(_mysql.store_result(self.__mysql))
self.__rowcount = _mysql.affected_rows(self.__mysql)
# execute query
def execute(self, operation, **parameters):
self.__res = []
self.__execute(operation, parameters)
# set description
self.__setDescription()
self.__rownumber = 0
self.__lastrowid = _mysql.insert_id(self.__mysql)
# execute many queries
def executemany(self, operation, seq_of_parameters):
if not type(seq_of_parameters) in (tuple, list):
raise InterfaceError()
self.__res = []
for p in seq_of_parameters:
# execute all
self.__execute(operation, p)
self.__setDescription()
self.__rownumber = 0
self.__lastrowid = _mysql.insert_id(self.__mysql)
# fetch one row
def fetchone(self):
if not len(self.__res) or self.__res[0] is None:
raise Error('No resultset available')
eerr = None
try:
_row = _mysql.fetch_row(self.__res[0])
except UnicodeDecodeError as err:
eerr = err
if eerr:
raise DataError(eerr)
if _row is None:
return None
row = []
for i in range(0, len(_row)):
item = _row[i]
itype = self.description[i][1]
if item is None:
row.append(None)
elif itype == BINARY:
row.append(item.encode())
elif itype == NUMBER:
if float(item) == int(float(item)):
row.append(int(item))
else:
row.append(float(item))
else:
row.append(item)
self.__rownumber = self.__rownumber + 1
return tuple(row)
# fetch many rows
def fetchmany(self, size=None):
if size is None:
size = self.arraysize
data = []
for i in range(0, size):
item = self.fetchone()
if item is None:
break
data.append(item)
return data
# fetch all remaining results
def fetchall(self):
rows = []
while True:
row = self.fetchone()
if row is None:
break
rows.append(row)
return rows
# set next result set
def nextset(self):
if len(self.__res):
# get next result set
self.__res.pop(0)
# set descriptions
self.__setDescription()
self.__rownumber = 0
return True
# no more result sets
return None
# scroll data result set
def scroll(self, value, mode='relative'):
if mode == 'relative':
value = self.rownumber + int(value)
if value < 0 or value > self.rowcount:
raise IndexError('scroll value is outside allowed range.')
_mysql.data_seek(self.__res[0], value)
self.__rownumber = value
# iteration protocol
# dbapi 2.0
def __next__(self):
data = self.fetchone()
if data is None:
raise StopIteration
return data
def __iter__(self):
return self
# returns number of rows changed/deleted/inserted
@property
def rowcount(self):
return self.__rowcount
# do nothing
def setinputsizes(self, *_):
pass
# do nothing
def setoutputsize(self, *_):
pass
# get query rows description
@property
def description(self):
return self.__description
@property
def connection(self):
return self.__connection
@property
def rownumber(self):
return self.__rownumber
@property
def lastrowid(self):
return self.__lastrowid
# Connection object definition
class ConnectionObject(object):
# class constructor
def __init__(self, m):
self.__mysql = m
# exceptions
self.Warning = Warning
self.Error = Error
self.InterfaceError = InterfaceError
self.DatabaseError = DatabaseError
self.DataError = DataError
self.OperationalError = OperationalError
self.IntegrityError = IntegrityError
self.InternalError = InternalError
self.ProgrammingError = ProgrammingError
self.NotSupportedError = NotSupportedError
# class destructor
def __del__(self):
self.close()
# close connection to database
def close(self):
_mysql.close(self.__mysql)
# commit database transaction
def commit(self):
if _mysql.commit(self.__mysql):
raise DatabaseError(_mysql.error(self.__mysql))
# rollback database transaction
def rollback(self):
if _mysql.rollback(self.__mysql):
raise DatabaseError(_mysql.error(self.__mysql))
# return cursor object
def cursor(self):
return CursorObject(self.__mysql, self)
# connect to database server
def connect(dsn=None, host=None, user=None, passwd=None, db=None, port=0, unixsocket=None):
m = _mysql.init()
if not _mysql.real_connect(m, host, user, passwd, db, port, unixsocket):
raise DatabaseError(_mysql.error(m))
return ConnectionObject(m)