mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-06-04 06:48:27 -04:00
1028 lines
31 KiB
Python
1028 lines
31 KiB
Python
# types.py
|
|
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Michael Bayer mike_mp@zzzcomputing.com
|
|
#
|
|
# This module is part of SQLAlchemy and is released under
|
|
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
|
|
|
"""defines genericized SQL types, each represented by a subclass of
|
|
:class:`~sqlalchemy.types.AbstractType`. Dialects define further subclasses of these
|
|
types.
|
|
|
|
For more information see the SQLAlchemy documentation on types.
|
|
|
|
"""
|
|
__all__ = [ 'TypeEngine', 'TypeDecorator', 'AbstractType', 'UserDefinedType',
|
|
'INT', 'CHAR', 'VARCHAR', 'NCHAR', 'NVARCHAR','TEXT', 'Text', 'FLOAT',
|
|
'NUMERIC', 'DECIMAL', 'TIMESTAMP', 'DATETIME', 'CLOB', 'NCLOB', 'BLOB',
|
|
'BOOLEAN', 'SMALLINT', 'INTEGER','DATE', 'TIME',
|
|
'String', 'Integer', 'SmallInteger',
|
|
'Numeric', 'Float', 'DateTime', 'Date', 'Time', 'Binary',
|
|
'Boolean', 'Unicode', 'MutableType', 'Concatenable', 'UnicodeText', 'PickleType', 'Interval',
|
|
'type_map'
|
|
]
|
|
|
|
import inspect
|
|
import datetime as dt
|
|
from decimal import Decimal as _python_Decimal
|
|
|
|
from sqlalchemy import exc
|
|
from sqlalchemy.util import pickle
|
|
from sqlalchemy.sql.visitors import Visitable
|
|
import sqlalchemy.util as util
|
|
NoneType = type(None)
|
|
if util.jython:
|
|
import array
|
|
|
|
class AbstractType(Visitable):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
pass
|
|
|
|
def compile(self, dialect):
|
|
return dialect.type_compiler.process(self)
|
|
|
|
def copy_value(self, value):
|
|
return value
|
|
|
|
def bind_processor(self, dialect):
|
|
"""Defines a bind parameter processing function."""
|
|
|
|
return None
|
|
|
|
def result_processor(self, dialect):
|
|
"""Defines a result-column processing function."""
|
|
|
|
return None
|
|
|
|
def compare_values(self, x, y):
|
|
"""Compare two values for equality."""
|
|
|
|
return x == y
|
|
|
|
def is_mutable(self):
|
|
"""Return True if the target Python type is 'mutable'.
|
|
|
|
This allows systems like the ORM to know if a column value can
|
|
be considered 'not changed' by comparing the identity of
|
|
objects alone.
|
|
|
|
Use the :class:`MutableType` mixin or override this method to
|
|
return True in custom types that hold mutable values such as
|
|
``dict``, ``list`` and custom objects.
|
|
|
|
"""
|
|
return False
|
|
|
|
def get_dbapi_type(self, dbapi):
|
|
"""Return the corresponding type object from the underlying DB-API, if any.
|
|
|
|
This can be useful for calling ``setinputsizes()``, for example.
|
|
"""
|
|
return None
|
|
|
|
def adapt_operator(self, op):
|
|
"""Given an operator from the sqlalchemy.sql.operators package,
|
|
translate it to a new operator based on the semantics of this type.
|
|
|
|
By default, returns the operator unchanged.
|
|
"""
|
|
return op
|
|
|
|
def __repr__(self):
|
|
return "%s(%s)" % (
|
|
self.__class__.__name__,
|
|
", ".join("%s=%r" % (k, getattr(self, k, None))
|
|
for k in inspect.getargspec(self.__init__)[0][1:]))
|
|
|
|
class TypeEngine(AbstractType):
|
|
"""Base for built-in types."""
|
|
|
|
@util.memoized_property
|
|
def _impl_dict(self):
|
|
return {}
|
|
|
|
def dialect_impl(self, dialect, **kwargs):
|
|
try:
|
|
return self._impl_dict[dialect.__class__]
|
|
except KeyError:
|
|
return self._impl_dict.setdefault(dialect.__class__, dialect.__class__.type_descriptor(self))
|
|
|
|
def __getstate__(self):
|
|
d = self.__dict__.copy()
|
|
d.pop('_impl_dict', None)
|
|
return d
|
|
|
|
def bind_processor(self, dialect):
|
|
"""Return a conversion function for processing bind values.
|
|
|
|
Returns a callable which will receive a bind parameter value
|
|
as the sole positional argument and will return a value to
|
|
send to the DB-API.
|
|
|
|
If processing is not necessary, the method should return ``None``.
|
|
|
|
"""
|
|
return None
|
|
|
|
def result_processor(self, dialect):
|
|
"""Return a conversion function for processing result row values.
|
|
|
|
Returns a callable which will receive a result row column
|
|
value as the sole positional argument and will return a value
|
|
to return to the user.
|
|
|
|
If processing is not necessary, the method should return ``None``.
|
|
|
|
"""
|
|
return None
|
|
|
|
def adapt(self, cls):
|
|
return cls()
|
|
|
|
class UserDefinedType(TypeEngine):
|
|
"""Base for user defined types.
|
|
|
|
This should be the base of new types. Note that
|
|
for most cases, :class:`TypeDecorator` is probably
|
|
more appropriate.
|
|
|
|
import sqlalchemy.types as types
|
|
|
|
class MyType(types.UserDefinedType):
|
|
def __init__(self, precision = 8):
|
|
self.precision = precision
|
|
|
|
def get_col_spec(self):
|
|
return "MYTYPE(%s)" % self.precision
|
|
|
|
def bind_processor(self, dialect):
|
|
def process(value):
|
|
return value
|
|
return process
|
|
|
|
def result_processor(self, dialect):
|
|
def process(value):
|
|
return value
|
|
return process
|
|
|
|
Once the type is made, it's immediately usable::
|
|
|
|
table = Table('foo', meta,
|
|
Column('id', Integer, primary_key=True),
|
|
Column('data', MyType(16))
|
|
)
|
|
|
|
"""
|
|
__visit_name__ = "user_defined"
|
|
|
|
class TypeDecorator(AbstractType):
|
|
"""Allows the creation of types which add additional functionality
|
|
to an existing type.
|
|
|
|
Typical usage::
|
|
|
|
import sqlalchemy.types as types
|
|
|
|
class MyType(types.TypeDecorator):
|
|
# Prefixes Unicode values with "PREFIX:" on the way in and
|
|
# strips it off on the way out.
|
|
|
|
impl = types.Unicode
|
|
|
|
def process_bind_param(self, value, dialect):
|
|
return "PREFIX:" + value
|
|
|
|
def process_result_value(self, value, dialect):
|
|
return value[7:]
|
|
|
|
def copy(self):
|
|
return MyType(self.impl.length)
|
|
|
|
The class-level "impl" variable is required, and can reference any
|
|
TypeEngine class. Alternatively, the load_dialect_impl() method
|
|
can be used to provide different type classes based on the dialect
|
|
given; in this case, the "impl" variable can reference
|
|
``TypeEngine`` as a placeholder.
|
|
|
|
The reason that type behavior is modified using class decoration
|
|
instead of subclassing is due to the way dialect specific types
|
|
are used. Such as with the example above, when using the mysql
|
|
dialect, the actual type in use will be a
|
|
``sqlalchemy.databases.mysql.MSString`` instance.
|
|
``TypeDecorator`` handles the mechanics of passing the values
|
|
between user-defined ``process_`` methods and the current
|
|
dialect-specific type in use.
|
|
|
|
"""
|
|
|
|
__visit_name__ = "type_decorator"
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
if not hasattr(self.__class__, 'impl'):
|
|
raise AssertionError("TypeDecorator implementations require a class-level "
|
|
"variable 'impl' which refers to the class of type being decorated")
|
|
self.impl = self.__class__.impl(*args, **kwargs)
|
|
|
|
def dialect_impl(self, dialect):
|
|
try:
|
|
return self._impl_dict[dialect.__class__]
|
|
except AttributeError:
|
|
self._impl_dict = {}
|
|
except KeyError:
|
|
pass
|
|
|
|
# adapt the TypeDecorator first, in
|
|
# the case that the dialect maps the TD
|
|
# to one of its native types (i.e. PGInterval)
|
|
adapted = dialect.__class__.type_descriptor(self)
|
|
if adapted is not self:
|
|
self._impl_dict[dialect] = adapted
|
|
return adapted
|
|
|
|
# otherwise adapt the impl type, link
|
|
# to a copy of this TypeDecorator and return
|
|
# that.
|
|
typedesc = self.load_dialect_impl(dialect)
|
|
tt = self.copy()
|
|
if not isinstance(tt, self.__class__):
|
|
raise AssertionError("Type object %s does not properly implement the copy() "
|
|
"method, it must return an object of type %s" % (self, self.__class__))
|
|
tt.impl = typedesc
|
|
self._impl_dict[dialect] = tt
|
|
return tt
|
|
|
|
def type_engine(self, dialect):
|
|
impl = self.dialect_impl(dialect)
|
|
if not isinstance(impl, TypeDecorator):
|
|
return impl
|
|
else:
|
|
return impl.impl
|
|
|
|
def load_dialect_impl(self, dialect):
|
|
"""Loads the dialect-specific implementation of this type.
|
|
|
|
by default calls dialect.type_descriptor(self.impl), but
|
|
can be overridden to provide different behavior.
|
|
|
|
"""
|
|
if isinstance(self.impl, TypeDecorator):
|
|
return self.impl.dialect_impl(dialect)
|
|
else:
|
|
return dialect.__class__.type_descriptor(self.impl)
|
|
|
|
def __getattr__(self, key):
|
|
"""Proxy all other undefined accessors to the underlying implementation."""
|
|
|
|
return getattr(self.impl, key)
|
|
|
|
def process_bind_param(self, value, dialect):
|
|
raise NotImplementedError()
|
|
|
|
def process_result_value(self, value, dialect):
|
|
raise NotImplementedError()
|
|
|
|
def bind_processor(self, dialect):
|
|
if self.__class__.process_bind_param.func_code is not TypeDecorator.process_bind_param.func_code:
|
|
impl_processor = self.impl.bind_processor(dialect)
|
|
if impl_processor:
|
|
def process(value):
|
|
return impl_processor(self.process_bind_param(value, dialect))
|
|
return process
|
|
else:
|
|
def process(value):
|
|
return self.process_bind_param(value, dialect)
|
|
return process
|
|
else:
|
|
return self.impl.bind_processor(dialect)
|
|
|
|
def result_processor(self, dialect):
|
|
if self.__class__.process_result_value.func_code is not TypeDecorator.process_result_value.func_code:
|
|
impl_processor = self.impl.result_processor(dialect)
|
|
if impl_processor:
|
|
def process(value):
|
|
return self.process_result_value(impl_processor(value), dialect)
|
|
return process
|
|
else:
|
|
def process(value):
|
|
return self.process_result_value(value, dialect)
|
|
return process
|
|
else:
|
|
return self.impl.result_processor(dialect)
|
|
|
|
def copy(self):
|
|
instance = self.__class__.__new__(self.__class__)
|
|
instance.__dict__.update(self.__dict__)
|
|
return instance
|
|
|
|
def get_dbapi_type(self, dbapi):
|
|
return self.impl.get_dbapi_type(dbapi)
|
|
|
|
def copy_value(self, value):
|
|
return self.impl.copy_value(value)
|
|
|
|
def compare_values(self, x, y):
|
|
return self.impl.compare_values(x, y)
|
|
|
|
def is_mutable(self):
|
|
return self.impl.is_mutable()
|
|
|
|
class MutableType(object):
|
|
"""A mixin that marks a Type as holding a mutable object.
|
|
|
|
:meth:`copy_value` and :meth:`compare_values` should be customized
|
|
as needed to match the needs of the object.
|
|
|
|
"""
|
|
|
|
def is_mutable(self):
|
|
"""Return True, mutable."""
|
|
return True
|
|
|
|
def copy_value(self, value):
|
|
"""Unimplemented."""
|
|
raise NotImplementedError()
|
|
|
|
def compare_values(self, x, y):
|
|
"""Compare *x* == *y*."""
|
|
return x == y
|
|
|
|
def to_instance(typeobj):
|
|
if typeobj is None:
|
|
return NULLTYPE
|
|
|
|
try:
|
|
return typeobj()
|
|
except TypeError:
|
|
return typeobj
|
|
|
|
def adapt_type(typeobj, colspecs):
|
|
if isinstance(typeobj, type):
|
|
typeobj = typeobj()
|
|
for t in typeobj.__class__.__mro__[0:-1]:
|
|
try:
|
|
impltype = colspecs[t]
|
|
break
|
|
except KeyError:
|
|
pass
|
|
else:
|
|
# couldnt adapt - so just return the type itself
|
|
# (it may be a user-defined type)
|
|
return typeobj
|
|
# if we adapted the given generic type to a database-specific type,
|
|
# but it turns out the originally given "generic" type
|
|
# is actually a subclass of our resulting type, then we were already
|
|
# given a more specific type than that required; so use that.
|
|
if (issubclass(typeobj.__class__, impltype)):
|
|
return typeobj
|
|
return typeobj.adapt(impltype)
|
|
|
|
class NullType(TypeEngine):
|
|
"""An unknown type.
|
|
|
|
NullTypes will stand in if :class:`~sqlalchemy.Table` reflection
|
|
encounters a column data type unknown to SQLAlchemy. The
|
|
resulting columns are nearly fully usable: the DB-API adapter will
|
|
handle all translation to and from the database data type.
|
|
|
|
NullType does not have sufficient information to particpate in a
|
|
``CREATE TABLE`` statement and will raise an exception if
|
|
encountered during a :meth:`~sqlalchemy.Table.create` operation.
|
|
|
|
"""
|
|
__visit_name__ = 'null'
|
|
|
|
NullTypeEngine = NullType
|
|
|
|
class Concatenable(object):
|
|
"""A mixin that marks a type as supporting 'concatenation', typically strings."""
|
|
|
|
def adapt_operator(self, op):
|
|
"""Converts an add operator to concat."""
|
|
from sqlalchemy.sql import operators
|
|
if op is operators.add:
|
|
return operators.concat_op
|
|
else:
|
|
return op
|
|
|
|
class String(Concatenable, TypeEngine):
|
|
"""The base for all string and character types.
|
|
|
|
In SQL, corresponds to VARCHAR. Can also take Python unicode objects
|
|
and encode to the database's encoding in bind params (and the reverse for
|
|
result sets.)
|
|
|
|
The `length` field is usually required when the `String` type is
|
|
used within a CREATE TABLE statement, as VARCHAR requires a length
|
|
on most databases.
|
|
|
|
"""
|
|
|
|
__visit_name__ = 'string'
|
|
|
|
def __init__(self, length=None, convert_unicode=False, assert_unicode=None):
|
|
"""
|
|
Create a string-holding type.
|
|
|
|
:param length: optional, a length for the column for use in
|
|
DDL statements. May be safely omitted if no ``CREATE
|
|
TABLE`` will be issued. Certain databases may require a
|
|
*length* for use in DDL, and will raise an exception when
|
|
the ``CREATE TABLE`` DDL is issued. Whether the value is
|
|
interpreted as bytes or characters is database specific.
|
|
|
|
:param convert_unicode: defaults to False. If True, convert
|
|
``unicode`` data sent to the database to a ``str``
|
|
bytestring, and convert bytestrings coming back from the
|
|
database into ``unicode``.
|
|
|
|
Bytestrings are encoded using the dialect's
|
|
:attr:`~sqlalchemy.engine.base.Dialect.encoding`, which
|
|
defaults to `utf-8`.
|
|
|
|
If False, may be overridden by
|
|
:attr:`sqlalchemy.engine.base.Dialect.convert_unicode`.
|
|
|
|
:param assert_unicode:
|
|
|
|
If None (the default), no assertion will take place unless
|
|
overridden by :attr:`sqlalchemy.engine.base.Dialect.assert_unicode`.
|
|
|
|
If 'warn', will issue a runtime warning if a ``str``
|
|
instance is used as a bind value.
|
|
|
|
If true, will raise an :exc:`sqlalchemy.exc.InvalidRequestError`.
|
|
|
|
"""
|
|
self.length = length
|
|
self.convert_unicode = convert_unicode
|
|
self.assert_unicode = assert_unicode
|
|
|
|
def adapt(self, impltype):
|
|
return impltype(
|
|
length=self.length,
|
|
convert_unicode=self.convert_unicode,
|
|
assert_unicode=self.assert_unicode)
|
|
|
|
def bind_processor(self, dialect):
|
|
if self.convert_unicode or dialect.convert_unicode:
|
|
if self.assert_unicode is None:
|
|
assert_unicode = dialect.assert_unicode
|
|
else:
|
|
assert_unicode = self.assert_unicode
|
|
|
|
if dialect.supports_unicode_binds and assert_unicode:
|
|
def process(value):
|
|
if not isinstance(value, (unicode, NoneType)):
|
|
if assert_unicode == 'warn':
|
|
util.warn("Unicode type received non-unicode bind "
|
|
"param value %r" % value)
|
|
return value
|
|
else:
|
|
raise exc.InvalidRequestError("Unicode type received non-unicode bind param value %r" % value)
|
|
else:
|
|
return value
|
|
elif dialect.supports_unicode_binds:
|
|
return None
|
|
else:
|
|
def process(value):
|
|
if isinstance(value, unicode):
|
|
return value.encode(dialect.encoding)
|
|
elif assert_unicode and not isinstance(value, (unicode, NoneType)):
|
|
if assert_unicode == 'warn':
|
|
util.warn("Unicode type received non-unicode bind "
|
|
"param value %r" % value)
|
|
return value
|
|
else:
|
|
raise exc.InvalidRequestError("Unicode type received non-unicode bind param value %r" % value)
|
|
else:
|
|
return value
|
|
return process
|
|
else:
|
|
return None
|
|
|
|
def result_processor(self, dialect):
|
|
if self.convert_unicode or dialect.convert_unicode:
|
|
def process(value):
|
|
if value is not None and not isinstance(value, unicode):
|
|
return value.decode(dialect.encoding)
|
|
else:
|
|
return value
|
|
return process
|
|
else:
|
|
return None
|
|
|
|
def get_dbapi_type(self, dbapi):
|
|
return dbapi.STRING
|
|
|
|
class Text(String):
|
|
"""A variably sized string type.
|
|
|
|
In SQL, usually corresponds to CLOB or TEXT. Can also take Python
|
|
unicode objects and encode to the database's encoding in bind
|
|
params (and the reverse for result sets.)
|
|
|
|
"""
|
|
__visit_name__ = 'text'
|
|
|
|
class Unicode(String):
|
|
"""A variable length Unicode string.
|
|
|
|
The ``Unicode`` type is a :class:`String` which converts Python
|
|
``unicode`` objects (i.e., strings that are defined as
|
|
``u'somevalue'``) into encoded bytestrings when passing the value
|
|
to the database driver, and similarly decodes values from the
|
|
database back into Python ``unicode`` objects.
|
|
|
|
When using the ``Unicode`` type, it is only appropriate to pass
|
|
Python ``unicode`` objects, and not plain ``str``. If a
|
|
bytestring (``str``) is passed, a runtime warning is issued. If
|
|
you notice your application raising these warnings but you're not
|
|
sure where, the Python ``warnings`` filter can be used to turn
|
|
these warnings into exceptions which will illustrate a stack
|
|
trace::
|
|
|
|
import warnings
|
|
warnings.simplefilter('error')
|
|
|
|
Bytestrings sent to and received from the database are encoded
|
|
using the dialect's
|
|
:attr:`~sqlalchemy.engine.base.Dialect.encoding`, which defaults
|
|
to `utf-8`.
|
|
|
|
"""
|
|
|
|
__visit_name__ = 'unicode'
|
|
|
|
def __init__(self, length=None, **kwargs):
|
|
"""
|
|
Create a Unicode-converting String type.
|
|
|
|
:param length: optional, a length for the column for use in
|
|
DDL statements. May be safely omitted if no ``CREATE
|
|
TABLE`` will be issued. Certain databases may require a
|
|
*length* for use in DDL, and will raise an exception when
|
|
the ``CREATE TABLE`` DDL is issued. Whether the value is
|
|
interpreted as bytes or characters is database specific.
|
|
|
|
"""
|
|
kwargs.setdefault('convert_unicode', True)
|
|
kwargs.setdefault('assert_unicode', 'warn')
|
|
super(Unicode, self).__init__(length=length, **kwargs)
|
|
|
|
class UnicodeText(Text):
|
|
"""An unbounded-length Unicode string.
|
|
|
|
See :class:`Unicode` for details on the unicode
|
|
behavior of this object.
|
|
|
|
"""
|
|
|
|
__visit_name__ = 'unicode_text'
|
|
|
|
def __init__(self, length=None, **kwargs):
|
|
"""
|
|
Create a Unicode-converting Text type.
|
|
|
|
:param length: optional, a length for the column for use in
|
|
DDL statements. May be safely omitted if no ``CREATE
|
|
TABLE`` will be issued. Certain databases may require a
|
|
*length* for use in DDL, and will raise an exception when
|
|
the ``CREATE TABLE`` DDL is issued. Whether the value is
|
|
interpreted as bytes or characters is database specific.
|
|
|
|
"""
|
|
kwargs.setdefault('convert_unicode', True)
|
|
kwargs.setdefault('assert_unicode', 'warn')
|
|
super(UnicodeText, self).__init__(length=length, **kwargs)
|
|
|
|
|
|
class Integer(TypeEngine):
|
|
"""A type for ``int`` integers."""
|
|
|
|
__visit_name__ = 'integer'
|
|
|
|
def get_dbapi_type(self, dbapi):
|
|
return dbapi.NUMBER
|
|
|
|
|
|
class SmallInteger(Integer):
|
|
"""A type for smaller ``int`` integers.
|
|
|
|
Typically generates a ``SMALLINT`` in DDL, and otherwise acts like
|
|
a normal :class:`Integer` on the Python side.
|
|
|
|
"""
|
|
|
|
__visit_name__ = 'small_integer'
|
|
|
|
class BigInteger(Integer):
|
|
"""A type for bigger ``int`` integers.
|
|
|
|
Typically generates a ``BIGINT`` in DDL, and otherwise acts like
|
|
a normal :class:`Integer` on the Python side.
|
|
|
|
"""
|
|
|
|
__visit_name__ = 'big_integer'
|
|
|
|
class Numeric(TypeEngine):
|
|
"""A type for fixed precision numbers.
|
|
|
|
Typically generates DECIMAL or NUMERIC. Returns
|
|
``decimal.Decimal`` objects by default.
|
|
|
|
"""
|
|
|
|
__visit_name__ = 'numeric'
|
|
|
|
def __init__(self, precision=None, scale=None, asdecimal=True):
|
|
"""
|
|
Construct a Numeric.
|
|
|
|
:param precision: the numeric precision for use in DDL ``CREATE TABLE``.
|
|
|
|
:param scale: the numeric scale for use in DDL ``CREATE TABLE``.
|
|
|
|
:param asdecimal: default True. If False, values will be
|
|
returned as-is from the DB-API, and may be either
|
|
``Decimal`` or ``float`` types depending on the DB-API in
|
|
use.
|
|
|
|
"""
|
|
self.precision = precision
|
|
self.scale = scale
|
|
self.asdecimal = asdecimal
|
|
|
|
def adapt(self, impltype):
|
|
return impltype(precision=self.precision, scale=self.scale, asdecimal=self.asdecimal)
|
|
|
|
def get_dbapi_type(self, dbapi):
|
|
return dbapi.NUMBER
|
|
|
|
def bind_processor(self, dialect):
|
|
def process(value):
|
|
if value is not None:
|
|
return float(value)
|
|
else:
|
|
return value
|
|
return process
|
|
|
|
def result_processor(self, dialect):
|
|
if self.asdecimal:
|
|
def process(value):
|
|
if value is not None:
|
|
return _python_Decimal(str(value))
|
|
else:
|
|
return value
|
|
return process
|
|
else:
|
|
return None
|
|
|
|
|
|
class Float(Numeric):
|
|
"""A type for ``float`` numbers."""
|
|
|
|
__visit_name__ = 'float'
|
|
|
|
def __init__(self, precision=None, asdecimal=False, **kwargs):
|
|
"""
|
|
Construct a Float.
|
|
|
|
:param precision: the numeric precision for use in DDL ``CREATE TABLE``.
|
|
|
|
"""
|
|
self.precision = precision
|
|
self.asdecimal = asdecimal
|
|
|
|
def adapt(self, impltype):
|
|
return impltype(precision=self.precision, asdecimal=self.asdecimal)
|
|
|
|
|
|
class DateTime(TypeEngine):
|
|
"""A type for ``datetime.datetime()`` objects.
|
|
|
|
Date and time types return objects from the Python ``datetime``
|
|
module. Most DBAPIs have built in support for the datetime
|
|
module, with the noted exception of SQLite. In the case of
|
|
SQLite, date and time types are stored as strings which are then
|
|
converted back to datetime objects when rows are returned.
|
|
|
|
"""
|
|
|
|
__visit_name__ = 'datetime'
|
|
|
|
def __init__(self, timezone=False):
|
|
self.timezone = timezone
|
|
|
|
def adapt(self, impltype):
|
|
return impltype(timezone=self.timezone)
|
|
|
|
def get_dbapi_type(self, dbapi):
|
|
return dbapi.DATETIME
|
|
|
|
|
|
class Date(TypeEngine):
|
|
"""A type for ``datetime.date()`` objects."""
|
|
|
|
__visit_name__ = 'date'
|
|
|
|
def get_dbapi_type(self, dbapi):
|
|
return dbapi.DATETIME
|
|
|
|
|
|
class Time(TypeEngine):
|
|
"""A type for ``datetime.time()`` objects."""
|
|
|
|
__visit_name__ = 'time'
|
|
|
|
def __init__(self, timezone=False):
|
|
self.timezone = timezone
|
|
|
|
def adapt(self, impltype):
|
|
return impltype(timezone=self.timezone)
|
|
|
|
def get_dbapi_type(self, dbapi):
|
|
return dbapi.DATETIME
|
|
|
|
|
|
class Binary(TypeEngine):
|
|
"""A type for binary byte data.
|
|
|
|
The Binary type generates BLOB or BYTEA when tables are created,
|
|
and also converts incoming values using the ``Binary`` callable
|
|
provided by each DB-API.
|
|
|
|
"""
|
|
|
|
__visit_name__ = 'binary'
|
|
|
|
def __init__(self, length=None):
|
|
"""
|
|
Construct a Binary type.
|
|
|
|
:param length: optional, a length for the column for use in
|
|
DDL statements. May be safely omitted if no ``CREATE
|
|
TABLE`` will be issued. Certain databases may require a
|
|
*length* for use in DDL, and will raise an exception when
|
|
the ``CREATE TABLE`` DDL is issued.
|
|
|
|
"""
|
|
self.length = length
|
|
|
|
def bind_processor(self, dialect):
|
|
DBAPIBinary = dialect.dbapi.Binary
|
|
def process(value):
|
|
if value is not None:
|
|
return DBAPIBinary(value)
|
|
else:
|
|
return None
|
|
return process
|
|
|
|
def adapt(self, impltype):
|
|
return impltype(length=self.length)
|
|
|
|
def get_dbapi_type(self, dbapi):
|
|
return dbapi.BINARY
|
|
|
|
|
|
class PickleType(MutableType, TypeDecorator):
|
|
"""Holds Python objects.
|
|
|
|
PickleType builds upon the Binary type to apply Python's
|
|
``pickle.dumps()`` to incoming objects, and ``pickle.loads()`` on
|
|
the way out, allowing any pickleable Python object to be stored as
|
|
a serialized binary field.
|
|
|
|
"""
|
|
|
|
impl = Binary
|
|
|
|
def __init__(self, protocol=pickle.HIGHEST_PROTOCOL, pickler=None, mutable=True, comparator=None):
|
|
"""
|
|
Construct a PickleType.
|
|
|
|
:param protocol: defaults to ``pickle.HIGHEST_PROTOCOL``.
|
|
|
|
:param pickler: defaults to cPickle.pickle or pickle.pickle if
|
|
cPickle is not available. May be any object with
|
|
pickle-compatible ``dumps` and ``loads`` methods.
|
|
|
|
:param mutable: defaults to True; implements
|
|
:meth:`AbstractType.is_mutable`. When ``True``, incoming
|
|
objects *must* provide an ``__eq__()`` method which
|
|
performs the desired deep comparison of members, or the
|
|
``comparator`` argument must be present. Otherwise,
|
|
comparisons are done by comparing pickle strings.
|
|
The pickle form of comparison is a deprecated usage and will
|
|
raise a warning.
|
|
|
|
:param comparator: optional. a 2-arg callable predicate used
|
|
to compare values of this type. Otherwise, either
|
|
the == operator is used to compare values, or if mutable==True
|
|
and the incoming object does not implement __eq__(), the value
|
|
of pickle.dumps(obj) is compared. The last option is a deprecated
|
|
usage and will raise a warning.
|
|
|
|
"""
|
|
self.protocol = protocol
|
|
self.pickler = pickler or pickle
|
|
self.mutable = mutable
|
|
self.comparator = comparator
|
|
super(PickleType, self).__init__()
|
|
|
|
def process_bind_param(self, value, dialect):
|
|
dumps = self.pickler.dumps
|
|
protocol = self.protocol
|
|
if value is None:
|
|
return None
|
|
return dumps(value, protocol)
|
|
|
|
def process_result_value(self, value, dialect):
|
|
loads = self.pickler.loads
|
|
if value is None:
|
|
return None
|
|
# Py3K
|
|
#return loads(value)
|
|
# Py2K
|
|
if util.jython and isinstance(value, array.ArrayType):
|
|
value = value.tostring()
|
|
else:
|
|
value = str(value)
|
|
return loads(value)
|
|
# end Py2K
|
|
|
|
def copy_value(self, value):
|
|
if self.mutable:
|
|
return self.pickler.loads(self.pickler.dumps(value, self.protocol))
|
|
else:
|
|
return value
|
|
|
|
def compare_values(self, x, y):
|
|
if self.comparator:
|
|
return self.comparator(x, y)
|
|
else:
|
|
return x == y
|
|
|
|
def is_mutable(self):
|
|
return self.mutable
|
|
|
|
|
|
class Boolean(TypeEngine):
|
|
"""A bool datatype.
|
|
|
|
Boolean typically uses BOOLEAN or SMALLINT on the DDL side, and on
|
|
the Python side deals in ``True`` or ``False``.
|
|
|
|
"""
|
|
|
|
__visit_name__ = 'boolean'
|
|
|
|
class Interval(TypeDecorator):
|
|
"""A type for ``datetime.timedelta()`` objects.
|
|
|
|
The Interval type deals with ``datetime.timedelta`` objects. In
|
|
PostgreSQL, the native ``INTERVAL`` type is used; for others, the
|
|
value is stored as a date which is relative to the "epoch"
|
|
(Jan. 1, 1970).
|
|
|
|
"""
|
|
|
|
impl = DateTime
|
|
|
|
def process_bind_param(self, value, dialect):
|
|
if value is None:
|
|
return None
|
|
return dt.datetime.utcfromtimestamp(0) + value
|
|
|
|
def process_result_value(self, value, dialect):
|
|
if value is None:
|
|
return None
|
|
return value - dt.datetime.utcfromtimestamp(0)
|
|
|
|
class FLOAT(Float):
|
|
"""The SQL FLOAT type."""
|
|
|
|
__visit_name__ = 'FLOAT'
|
|
|
|
class NUMERIC(Numeric):
|
|
"""The SQL NUMERIC type."""
|
|
|
|
__visit_name__ = 'NUMERIC'
|
|
|
|
|
|
class DECIMAL(Numeric):
|
|
"""The SQL DECIMAL type."""
|
|
|
|
__visit_name__ = 'DECIMAL'
|
|
|
|
|
|
class INTEGER(Integer):
|
|
"""The SQL INT or INTEGER type."""
|
|
|
|
__visit_name__ = 'INTEGER'
|
|
INT = INTEGER
|
|
|
|
|
|
class SMALLINT(SmallInteger):
|
|
"""The SQL SMALLINT type."""
|
|
|
|
__visit_name__ = 'SMALLINT'
|
|
|
|
|
|
class BIGINT(BigInteger):
|
|
"""The SQL BIGINT type."""
|
|
|
|
__visit_name__ = 'BIGINT'
|
|
|
|
class TIMESTAMP(DateTime):
|
|
"""The SQL TIMESTAMP type."""
|
|
|
|
__visit_name__ = 'TIMESTAMP'
|
|
|
|
def get_dbapi_type(self, dbapi):
|
|
return dbapi.TIMESTAMP
|
|
|
|
class DATETIME(DateTime):
|
|
"""The SQL DATETIME type."""
|
|
|
|
__visit_name__ = 'DATETIME'
|
|
|
|
|
|
class DATE(Date):
|
|
"""The SQL DATE type."""
|
|
|
|
__visit_name__ = 'DATE'
|
|
|
|
|
|
class TIME(Time):
|
|
"""The SQL TIME type."""
|
|
|
|
__visit_name__ = 'TIME'
|
|
|
|
class TEXT(Text):
|
|
"""The SQL TEXT type."""
|
|
|
|
__visit_name__ = 'TEXT'
|
|
|
|
class CLOB(Text):
|
|
"""The CLOB type.
|
|
|
|
This type is found in Oracle and Informix.
|
|
"""
|
|
|
|
__visit_name__ = 'CLOB'
|
|
|
|
class VARCHAR(String):
|
|
"""The SQL VARCHAR type."""
|
|
|
|
__visit_name__ = 'VARCHAR'
|
|
|
|
class NVARCHAR(Unicode):
|
|
"""The SQL NVARCHAR type."""
|
|
|
|
__visit_name__ = 'NVARCHAR'
|
|
|
|
class CHAR(String):
|
|
"""The SQL CHAR type."""
|
|
|
|
__visit_name__ = 'CHAR'
|
|
|
|
|
|
class NCHAR(Unicode):
|
|
"""The SQL NCHAR type."""
|
|
|
|
__visit_name__ = 'NCHAR'
|
|
|
|
|
|
class BLOB(Binary):
|
|
"""The SQL BLOB type."""
|
|
|
|
__visit_name__ = 'BLOB'
|
|
|
|
|
|
class BOOLEAN(Boolean):
|
|
"""The SQL BOOLEAN type."""
|
|
|
|
__visit_name__ = 'BOOLEAN'
|
|
|
|
NULLTYPE = NullType()
|
|
|
|
# using VARCHAR/NCHAR so that we dont get the genericized "String"
|
|
# type which usually resolves to TEXT/CLOB
|
|
type_map = {
|
|
str: String,
|
|
# Py2K
|
|
unicode : String,
|
|
# end Py2K
|
|
int : Integer,
|
|
float : Numeric,
|
|
bool: Boolean,
|
|
_python_Decimal : Numeric,
|
|
dt.date : Date,
|
|
dt.datetime : DateTime,
|
|
dt.time : Time,
|
|
dt.timedelta : Interval,
|
|
NoneType: NullType
|
|
}
|
|
|