also customizable via the "precedence" argument
on the ``op()`` method. [ticket:2537]
+ - [feature] Added "collation" parameter to all
+ String types. When present, renders as
+ COLLATE <collation>. This to support the
+ COLLATE keyword now supported by several
+ databases including MySQL, SQLite, and Postgresql.
+ [ticket:2276]
+
+ - [change] The Text() type renders the length
+ given to it, if a length was specified.
+
- [feature] Custom unary operators can now be
used by combining operators.custom_op() with
UnaryExpression().
ascii=False, binary=False,
national=False, **kw):
self.charset = charset
+
# allow collate= or collation=
- self.collation = kw.pop('collate', collation)
+ kw.setdefault('collation', kw.pop('collate', collation))
+
self.ascii = ascii
# We have to munge the 'unicode' param strictly as a dict
# otherwise 2to3 will turn it into str.
self.national = national
super(_StringType, self).__init__(**kw)
- def __repr__(self):
- attributes = inspect.getargspec(self.__init__)[0][1:]
- attributes.extend(inspect.getargspec(_StringType.__init__)[0][1:])
-
- params = {}
- for attr in attributes:
- val = getattr(self, attr)
- if val is not None and val is not False:
- params[attr] = val
-
- return "%s(%s)" % (self.__class__.__name__,
- ', '.join(['%s=%r' % (k, params[k]) for k in params]))
-
class NUMERIC(_NumericType, sqltypes.NUMERIC):
"""MySQL NUMERIC type."""
opts = dict(
(
- k[len(self.dialect.name)+1:].upper(),
+ k[len(self.dialect.name) + 1:].upper(),
v
)
for k, v in table.kwargs.items()
def visit_CHAR(self, type_):
if type_.length:
- return self._extend_string(type_, {}, "CHAR(%(length)s)" % {'length' : type_.length})
+ return self._extend_string(type_, {}, "CHAR(%(length)s)" %
+ {'length': type_.length})
else:
return self._extend_string(type_, {}, "CHAR")
# We'll actually generate the equiv. "NATIONAL VARCHAR" instead
# of "NVARCHAR".
if type_.length:
- return self._extend_string(type_, {'national':True}, "VARCHAR(%(length)s)" % {'length': type_.length})
+ return self._extend_string(type_, {'national': True},
+ "VARCHAR(%(length)s)" % {'length': type_.length})
else:
raise exc.CompileError(
"NVARCHAR requires a length on dialect %s" %
def visit_NCHAR(self, type_):
# We'll actually generate the equiv. "NATIONAL CHAR" instead of "NCHAR".
if type_.length:
- return self._extend_string(type_, {'national':True}, "CHAR(%(length)s)" % {'length': type_.length})
+ return self._extend_string(type_, {'national': True},
+ "CHAR(%(length)s)" % {'length': type_.length})
else:
- return self._extend_string(type_, {'national':True}, "CHAR")
+ return self._extend_string(type_, {'national': True}, "CHAR")
def visit_VARBINARY(self, type_):
return "VARBINARY(%d)" % type_.length
class GenericTypeCompiler(engine.TypeCompiler):
- def visit_CHAR(self, type_):
- return "CHAR" + (type_.length and "(%d)" % type_.length or "")
-
- def visit_NCHAR(self, type_):
- return "NCHAR" + (type_.length and "(%d)" % type_.length or "")
def visit_FLOAT(self, type_):
return "FLOAT"
def visit_NCLOB(self, type_):
return "NCLOB"
+ def _render_string_type(self, type_, name):
+
+ text = name
+ if type_.length:
+ text += "(%d)" % type_.length
+ if type_.collation:
+ text += ' COLLATE "%s"' % type_.collation
+ return text
+
+ def visit_CHAR(self, type_):
+ return self._render_string_type(type_, "CHAR")
+
+ def visit_NCHAR(self, type_):
+ return self._render_string_type(type_, "NCHAR")
+
def visit_VARCHAR(self, type_):
- return "VARCHAR" + (type_.length and "(%d)" % type_.length or "")
+ return self._render_string_type(type_, "VARCHAR")
def visit_NVARCHAR(self, type_):
- return "NVARCHAR" + (type_.length and "(%d)" % type_.length or "")
+ return self._render_string_type(type_, "NVARCHAR")
+
+ def visit_TEXT(self, type_):
+ return self._render_string_type(type_, "TEXT")
def visit_BLOB(self, type_):
return "BLOB"
def visit_BOOLEAN(self, type_):
return "BOOLEAN"
- def visit_TEXT(self, type_):
- return "TEXT"
def visit_large_binary(self, type_):
return self.visit_BLOB(type_)
@property
def index_reflection(self):
return exclusions.open()
+
+ @property
+ def unbounded_varchar(self):
+ """Target database must support VARCHAR with no length"""
+
+ return exclusions.open()
__visit_name__ = 'string'
- def __init__(self, length=None, convert_unicode=False,
+ def __init__(self, length=None, collation=None,
+ convert_unicode=False,
assert_unicode=None, unicode_error=None,
_warn_on_bytestring=False
):
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
+ DDL and CAST expressions. 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 if a ``VARCHAR``
with no length is included. Whether the value is
interpreted as bytes or characters is database specific.
+ :param collation: Optional, a column-level collation for
+ use in DDL and CAST expressions. Renders using the
+ COLLATE keyword supported by SQLite, MySQL, and Postgresql.
+ E.g.::
+
+ >>> from sqlalchemy import cast, select, String
+ >>> print select([cast('some string', String(collation='utf8'))])
+ SELECT CAST(:param_1 AS VARCHAR COLLATE utf8) AS anon_1
+
+ .. versionadded:: 0.8 Added support for COLLATE to all
+ string types.
+
:param convert_unicode: When set to ``True``, the
:class:`.String` type will assume that
input is to be passed as Python ``unicode`` objects,
'*not* apply to DBAPIs that coerce '
'Unicode natively.')
self.length = length
+ self.collation = collation
self.convert_unicode = convert_unicode
self.unicode_error = unicode_error
self._warn_on_bytestring = _warn_on_bytestring
'LONGTEXT ASCII'),
(mysql.ENUM, ["foo", "bar"], {'unicode':True},
- '''ENUM('foo','bar') UNICODE''')
+ '''ENUM('foo','bar') UNICODE'''),
+
+ (String, [20], {"collation":"utf8"}, 'VARCHAR(20) COLLATE utf8')
+
+
]
for type_, args, kw, res in columns:
Column('Lar', LargeBinary()),
Column('Pic', PickleType()),
Column('Int', Interval()),
- Column('Enu', Enum('x','y','z', name="somename")),
+ Column('Enu', Enum('x', 'y', 'z', name="somename")),
]
for column_type in column_types:
- #print column_type
meta = MetaData()
Table('foo', meta, column_type)
- ct = loads(dumps(column_type))
- mt = loads(dumps(meta))
+ loads(dumps(column_type))
+ loads(dumps(meta))
class UserDefinedTest(fixtures.TablesTest, AssertsCompiledSQL):
raw_dialect_impl = raw_impl.dialect_impl(dialect_)
dec_dialect_impl = dec_type.dialect_impl(dialect_)
eq_(dec_dialect_impl.__class__, MyType)
- eq_(raw_dialect_impl.__class__ , dec_dialect_impl.impl.__class__)
+ eq_(raw_dialect_impl.__class__, dec_dialect_impl.impl.__class__)
self.assert_compile(
MyType(**kw),
assert test_table.c.data.distinct().type == test_table.c.data.type
class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
- def test_default_compile(self):
- """test that the base dialect of the type object is used
- for default compilation.
- """
+ @testing.requires.unbounded_varchar
+ def test_string_plain(self):
+ self.assert_compile(String(), "VARCHAR")
+
+ def test_string_length(self):
+ self.assert_compile(String(50), "VARCHAR(50)")
+
+ def test_string_collation(self):
+ self.assert_compile(String(50, collation="FOO"),
+ 'VARCHAR(50) COLLATE "FOO"')
+
+ def test_char_plain(self):
+ self.assert_compile(CHAR(), "CHAR")
+
+ def test_char_length(self):
+ self.assert_compile(CHAR(50), "CHAR(50)")
+
+ def test_char_collation(self):
+ self.assert_compile(CHAR(50, collation="FOO"),
+ 'CHAR(50) COLLATE "FOO"')
+
+ def test_text_plain(self):
+ self.assert_compile(Text(), "TEXT")
+
+ def test_text_length(self):
+ self.assert_compile(Text(50), "TEXT(50)")
+
+ def test_text_collation(self):
+ self.assert_compile(Text(collation="FOO"),
+ 'TEXT COLLATE "FOO"')
+
+ def test_default_compile_pg_inet(self):
+ self.assert_compile(dialects.postgresql.INET(), "INET",
+ allow_dialect_select=True)
+
+ def test_default_compile_pg_float(self):
+ self.assert_compile(dialects.postgresql.FLOAT(), "FLOAT",
+ allow_dialect_select=True)
+
+ def test_default_compile_mysql_integer(self):
+ self.assert_compile(
+ dialects.mysql.INTEGER(display_width=5), "INTEGER(5)",
+ allow_dialect_select=True)
- for type_, expected in (
- (String(), "VARCHAR"),
- (Integer(), "INTEGER"),
- (dialects.postgresql.INET(), "INET"),
- (dialects.postgresql.FLOAT(), "FLOAT"),
- (dialects.mysql.REAL(precision=8, scale=2), "REAL(8, 2)"),
- (dialects.postgresql.REAL(), "REAL"),
- (INTEGER(), "INTEGER"),
- (dialects.mysql.INTEGER(display_width=5), "INTEGER(5)")
- ):
- self.assert_compile(type_, expected,
- allow_dialect_select=True)
class DateTest(fixtures.TestBase, AssertsExecutionResults):
@classmethod