mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
349 lines
10 KiB
Python
349 lines
10 KiB
Python
import re
|
|
import datetime
|
|
import time
|
|
import sys
|
|
|
|
from constants import FIELD_TYPE, FLAG
|
|
from charset import charset_by_id
|
|
|
|
PYTHON3 = sys.version_info[0] > 2
|
|
|
|
try:
|
|
set
|
|
except NameError:
|
|
try:
|
|
from sets import BaseSet as set
|
|
except ImportError:
|
|
from sets import Set as set
|
|
|
|
ESCAPE_REGEX = re.compile(r"[\0\n\r\032\'\"\\]")
|
|
ESCAPE_MAP = {'\0': '\\0', '\n': '\\n', '\r': '\\r', '\032': '\\Z',
|
|
'\'': '\\\'', '"': '\\"', '\\': '\\\\'}
|
|
|
|
def escape_item(val, charset):
|
|
if type(val) in [tuple, list, set]:
|
|
return escape_sequence(val, charset)
|
|
if type(val) is dict:
|
|
return escape_dict(val, charset)
|
|
if PYTHON3 and hasattr(val, "decode") and not isinstance(val, unicode):
|
|
# deal with py3k bytes
|
|
val = val.decode(charset)
|
|
encoder = encoders[type(val)]
|
|
val = encoder(val)
|
|
if type(val) is str:
|
|
return val
|
|
val = val.encode(charset)
|
|
return val
|
|
|
|
def escape_dict(val, charset):
|
|
n = {}
|
|
for k, v in val.items():
|
|
quoted = escape_item(v, charset)
|
|
n[k] = quoted
|
|
return n
|
|
|
|
def escape_sequence(val, charset):
|
|
n = []
|
|
for item in val:
|
|
quoted = escape_item(item, charset)
|
|
n.append(quoted)
|
|
return "(" + ",".join(n) + ")"
|
|
|
|
def escape_set(val, charset):
|
|
val = map(lambda x: escape_item(x, charset), val)
|
|
return ','.join(val)
|
|
|
|
def escape_bool(value):
|
|
return str(int(value))
|
|
|
|
def escape_object(value):
|
|
return str(value)
|
|
|
|
escape_int = escape_long = escape_object
|
|
|
|
def escape_float(value):
|
|
return ('%.15g' % value)
|
|
|
|
def escape_string(value):
|
|
return ("'%s'" % ESCAPE_REGEX.sub(
|
|
lambda match: ESCAPE_MAP.get(match.group(0)), value))
|
|
|
|
def escape_unicode(value):
|
|
return escape_string(value)
|
|
|
|
def escape_None(value):
|
|
return 'NULL'
|
|
|
|
def escape_timedelta(obj):
|
|
seconds = int(obj.seconds) % 60
|
|
minutes = int(obj.seconds // 60) % 60
|
|
hours = int(obj.seconds // 3600) % 24 + int(obj.days) * 24
|
|
return escape_string('%02d:%02d:%02d' % (hours, minutes, seconds))
|
|
|
|
def escape_time(obj):
|
|
s = "%02d:%02d:%02d" % (int(obj.hour), int(obj.minute),
|
|
int(obj.second))
|
|
if obj.microsecond:
|
|
s += ".%f" % obj.microsecond
|
|
|
|
return escape_string(s)
|
|
|
|
def escape_datetime(obj):
|
|
return escape_string(obj.strftime("%Y-%m-%d %H:%M:%S"))
|
|
|
|
def escape_date(obj):
|
|
return escape_string(obj.strftime("%Y-%m-%d"))
|
|
|
|
def escape_struct_time(obj):
|
|
return escape_datetime(datetime.datetime(*obj[:6]))
|
|
|
|
def convert_datetime(connection, field, obj):
|
|
"""Returns a DATETIME or TIMESTAMP column value as a datetime object:
|
|
|
|
>>> datetime_or_None('2007-02-25 23:06:20')
|
|
datetime.datetime(2007, 2, 25, 23, 6, 20)
|
|
>>> datetime_or_None('2007-02-25T23:06:20')
|
|
datetime.datetime(2007, 2, 25, 23, 6, 20)
|
|
|
|
Illegal values are returned as None:
|
|
|
|
>>> datetime_or_None('2007-02-31T23:06:20') is None
|
|
True
|
|
>>> datetime_or_None('0000-00-00 00:00:00') is None
|
|
True
|
|
|
|
"""
|
|
if not isinstance(obj, unicode):
|
|
obj = obj.decode(connection.charset)
|
|
if ' ' in obj:
|
|
sep = ' '
|
|
elif 'T' in obj:
|
|
sep = 'T'
|
|
else:
|
|
return convert_date(connection, field, obj)
|
|
|
|
try:
|
|
ymd, hms = obj.split(sep, 1)
|
|
return datetime.datetime(*[ int(x) for x in ymd.split('-')+hms.split(':') ])
|
|
except ValueError:
|
|
return convert_date(connection, field, obj)
|
|
|
|
def convert_timedelta(connection, field, obj):
|
|
"""Returns a TIME column as a timedelta object:
|
|
|
|
>>> timedelta_or_None('25:06:17')
|
|
datetime.timedelta(1, 3977)
|
|
>>> timedelta_or_None('-25:06:17')
|
|
datetime.timedelta(-2, 83177)
|
|
|
|
Illegal values are returned as None:
|
|
|
|
>>> timedelta_or_None('random crap') is None
|
|
True
|
|
|
|
Note that MySQL always returns TIME columns as (+|-)HH:MM:SS, but
|
|
can accept values as (+|-)DD HH:MM:SS. The latter format will not
|
|
be parsed correctly by this function.
|
|
"""
|
|
from math import modf
|
|
try:
|
|
if not isinstance(obj, unicode):
|
|
obj = obj.decode(connection.charset)
|
|
hours, minutes, seconds = tuple([int(x) for x in obj.split(':')])
|
|
tdelta = datetime.timedelta(
|
|
hours = int(hours),
|
|
minutes = int(minutes),
|
|
seconds = int(seconds),
|
|
microseconds = int(modf(float(seconds))[0]*1000000),
|
|
)
|
|
return tdelta
|
|
except ValueError:
|
|
return None
|
|
|
|
def convert_time(connection, field, obj):
|
|
"""Returns a TIME column as a time object:
|
|
|
|
>>> time_or_None('15:06:17')
|
|
datetime.time(15, 6, 17)
|
|
|
|
Illegal values are returned as None:
|
|
|
|
>>> time_or_None('-25:06:17') is None
|
|
True
|
|
>>> time_or_None('random crap') is None
|
|
True
|
|
|
|
Note that MySQL always returns TIME columns as (+|-)HH:MM:SS, but
|
|
can accept values as (+|-)DD HH:MM:SS. The latter format will not
|
|
be parsed correctly by this function.
|
|
|
|
Also note that MySQL's TIME column corresponds more closely to
|
|
Python's timedelta and not time. However if you want TIME columns
|
|
to be treated as time-of-day and not a time offset, then you can
|
|
use set this function as the converter for FIELD_TYPE.TIME.
|
|
"""
|
|
from math import modf
|
|
try:
|
|
hour, minute, second = obj.split(':')
|
|
return datetime.time(hour=int(hour), minute=int(minute),
|
|
second=int(second),
|
|
microsecond=int(modf(float(second))[0]*1000000))
|
|
except ValueError:
|
|
return None
|
|
|
|
def convert_date(connection, field, obj):
|
|
"""Returns a DATE column as a date object:
|
|
|
|
>>> date_or_None('2007-02-26')
|
|
datetime.date(2007, 2, 26)
|
|
|
|
Illegal values are returned as None:
|
|
|
|
>>> date_or_None('2007-02-31') is None
|
|
True
|
|
>>> date_or_None('0000-00-00') is None
|
|
True
|
|
|
|
"""
|
|
try:
|
|
if not isinstance(obj, unicode):
|
|
obj = obj.decode(connection.charset)
|
|
return datetime.date(*[ int(x) for x in obj.split('-', 2) ])
|
|
except ValueError:
|
|
return None
|
|
|
|
def convert_mysql_timestamp(connection, field, timestamp):
|
|
"""Convert a MySQL TIMESTAMP to a Timestamp object.
|
|
|
|
MySQL >= 4.1 returns TIMESTAMP in the same format as DATETIME:
|
|
|
|
>>> mysql_timestamp_converter('2007-02-25 22:32:17')
|
|
datetime.datetime(2007, 2, 25, 22, 32, 17)
|
|
|
|
MySQL < 4.1 uses a big string of numbers:
|
|
|
|
>>> mysql_timestamp_converter('20070225223217')
|
|
datetime.datetime(2007, 2, 25, 22, 32, 17)
|
|
|
|
Illegal values are returned as None:
|
|
|
|
>>> mysql_timestamp_converter('2007-02-31 22:32:17') is None
|
|
True
|
|
>>> mysql_timestamp_converter('00000000000000') is None
|
|
True
|
|
|
|
"""
|
|
if not isinstance(timestamp, unicode):
|
|
timestamp = timestamp.decode(connection.charset)
|
|
|
|
if timestamp[4] == '-':
|
|
return convert_datetime(connection, field, timestamp)
|
|
timestamp += "0"*(14-len(timestamp)) # padding
|
|
year, month, day, hour, minute, second = \
|
|
int(timestamp[:4]), int(timestamp[4:6]), int(timestamp[6:8]), \
|
|
int(timestamp[8:10]), int(timestamp[10:12]), int(timestamp[12:14])
|
|
try:
|
|
return datetime.datetime(year, month, day, hour, minute, second)
|
|
except ValueError:
|
|
return None
|
|
|
|
def convert_set(s):
|
|
return set(s.split(","))
|
|
|
|
def convert_bit(connection, field, b):
|
|
#b = "\x00" * (8 - len(b)) + b # pad w/ zeroes
|
|
#return struct.unpack(">Q", b)[0]
|
|
#
|
|
# the snippet above is right, but MySQLdb doesn't process bits,
|
|
# so we shouldn't either
|
|
return b
|
|
|
|
def convert_characters(connection, field, data):
|
|
field_charset = charset_by_id(field.charsetnr).name
|
|
if field.flags & FLAG.SET:
|
|
return convert_set(data.decode(field_charset))
|
|
if field.flags & FLAG.BINARY:
|
|
return data
|
|
|
|
if connection.use_unicode:
|
|
data = data.decode(field_charset)
|
|
elif connection.charset != field_charset:
|
|
data = data.decode(field_charset)
|
|
data = data.encode(connection.charset)
|
|
return data
|
|
|
|
def convert_int(connection, field, data):
|
|
return int(data)
|
|
|
|
def convert_long(connection, field, data):
|
|
return long(data)
|
|
|
|
def convert_float(connection, field, data):
|
|
return float(data)
|
|
|
|
encoders = {
|
|
bool: escape_bool,
|
|
int: escape_int,
|
|
long: escape_long,
|
|
float: escape_float,
|
|
str: escape_string,
|
|
unicode: escape_unicode,
|
|
tuple: escape_sequence,
|
|
list:escape_sequence,
|
|
set:escape_sequence,
|
|
dict:escape_dict,
|
|
type(None):escape_None,
|
|
datetime.date: escape_date,
|
|
datetime.datetime : escape_datetime,
|
|
datetime.timedelta : escape_timedelta,
|
|
datetime.time : escape_time,
|
|
time.struct_time : escape_struct_time,
|
|
}
|
|
|
|
decoders = {
|
|
FIELD_TYPE.BIT: convert_bit,
|
|
FIELD_TYPE.TINY: convert_int,
|
|
FIELD_TYPE.SHORT: convert_int,
|
|
FIELD_TYPE.LONG: convert_long,
|
|
FIELD_TYPE.FLOAT: convert_float,
|
|
FIELD_TYPE.DOUBLE: convert_float,
|
|
FIELD_TYPE.DECIMAL: convert_float,
|
|
FIELD_TYPE.NEWDECIMAL: convert_float,
|
|
FIELD_TYPE.LONGLONG: convert_long,
|
|
FIELD_TYPE.INT24: convert_int,
|
|
FIELD_TYPE.YEAR: convert_int,
|
|
FIELD_TYPE.TIMESTAMP: convert_mysql_timestamp,
|
|
FIELD_TYPE.DATETIME: convert_datetime,
|
|
FIELD_TYPE.TIME: convert_timedelta,
|
|
FIELD_TYPE.DATE: convert_date,
|
|
FIELD_TYPE.SET: convert_set,
|
|
FIELD_TYPE.BLOB: convert_characters,
|
|
FIELD_TYPE.TINY_BLOB: convert_characters,
|
|
FIELD_TYPE.MEDIUM_BLOB: convert_characters,
|
|
FIELD_TYPE.LONG_BLOB: convert_characters,
|
|
FIELD_TYPE.STRING: convert_characters,
|
|
FIELD_TYPE.VAR_STRING: convert_characters,
|
|
FIELD_TYPE.VARCHAR: convert_characters,
|
|
#FIELD_TYPE.BLOB: str,
|
|
#FIELD_TYPE.STRING: str,
|
|
#FIELD_TYPE.VAR_STRING: str,
|
|
#FIELD_TYPE.VARCHAR: str
|
|
}
|
|
conversions = decoders # for MySQLdb compatibility
|
|
|
|
try:
|
|
# python version > 2.3
|
|
from decimal import Decimal
|
|
def convert_decimal(connection, field, data):
|
|
data = data.decode(connection.charset)
|
|
return Decimal(data)
|
|
decoders[FIELD_TYPE.DECIMAL] = convert_decimal
|
|
decoders[FIELD_TYPE.NEWDECIMAL] = convert_decimal
|
|
|
|
def escape_decimal(obj):
|
|
return unicode(obj)
|
|
encoders[Decimal] = escape_decimal
|
|
|
|
except ImportError:
|
|
pass
|