From 8fe38c7e956675eb9b6c859848200d8dcc7a9589 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sat, 5 Jan 2008 22:59:18 +0000 Subject: [PATCH] - changed name of TEXT to Text since its a "generic" type; TEXT name is deprecated until 0.5. The "upgrading" behavior of String to Text when no length is present is also deprecated until 0.5; will issue a warning when used for CREATE TABLE statements (String with no length for SQL expression purposes is still fine) [ticket:912] --- CHANGES | 6 ++++ lib/sqlalchemy/databases/access.py | 4 +-- lib/sqlalchemy/databases/firebird.py | 4 +-- lib/sqlalchemy/databases/informix.py | 2 +- lib/sqlalchemy/databases/maxdb.py | 2 +- lib/sqlalchemy/databases/mssql.py | 4 +-- lib/sqlalchemy/databases/mysql.py | 6 ++-- lib/sqlalchemy/databases/oracle.py | 4 +-- lib/sqlalchemy/databases/postgres.py | 4 +-- lib/sqlalchemy/databases/sqlite.py | 4 +-- lib/sqlalchemy/databases/sybase.py | 4 +-- lib/sqlalchemy/sql/compiler.py | 2 +- lib/sqlalchemy/sql/expression.py | 17 +-------- lib/sqlalchemy/types.py | 52 +++++++++++++++++++++++----- 14 files changed, 71 insertions(+), 44 deletions(-) diff --git a/CHANGES b/CHANGES index f63c50a424..fa9ea42d75 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,12 @@ CHANGES 0.4.2b ------ +- sql + - changed name of TEXT to Text since its a "generic" type; TEXT name is + deprecated until 0.5. The "upgrading" behavior of String to Text + when no length is present is also deprecated until 0.5; will issue a + warning when used for CREATE TABLE statements (String with no length + for SQL expression purposes is still fine) [ticket:912] - orm - Fixed cascades on a += assignment to a list-based relation. diff --git a/lib/sqlalchemy/databases/access.py b/lib/sqlalchemy/databases/access.py index 354a8c3322..78e3a99970 100644 --- a/lib/sqlalchemy/databases/access.py +++ b/lib/sqlalchemy/databases/access.py @@ -64,7 +64,7 @@ class AcDate(types.Date): def get_col_spec(self): return "DATETIME" -class AcText(types.TEXT): +class AcText(types.Text): def get_col_spec(self): return "MEMO" @@ -172,7 +172,7 @@ class AccessDialect(default.DefaultDialect): types.String : AcString, types.Binary : AcBinary, types.Boolean : AcBoolean, - types.TEXT : AcText, + types.Text : AcText, types.CHAR: AcChar, types.TIMESTAMP: AcTimeStamp, } diff --git a/lib/sqlalchemy/databases/firebird.py b/lib/sqlalchemy/databases/firebird.py index 9dca32a336..2474001794 100644 --- a/lib/sqlalchemy/databases/firebird.py +++ b/lib/sqlalchemy/databases/firebird.py @@ -177,7 +177,7 @@ class FBTime(sqltypes.Time): return "TIME" -class FBText(sqltypes.TEXT): +class FBText(sqltypes.Text): """Handle ``BLOB SUB_TYPE 1`` datatype (aka *textual* blob).""" def get_col_spec(self): @@ -223,7 +223,7 @@ colspecs = { sqltypes.String : FBString, sqltypes.Binary : FBBinary, sqltypes.Boolean : FBBoolean, - sqltypes.TEXT : FBText, + sqltypes.Text : FBText, sqltypes.CHAR: FBChar, } diff --git a/lib/sqlalchemy/databases/informix.py b/lib/sqlalchemy/databases/informix.py index f04d53f0f3..3ad490f44b 100644 --- a/lib/sqlalchemy/databases/informix.py +++ b/lib/sqlalchemy/databases/informix.py @@ -150,7 +150,7 @@ colspecs = { sqltypes.String : InfoString, sqltypes.Binary : InfoBinary, sqltypes.Boolean : InfoBoolean, - sqltypes.TEXT : InfoText, + sqltypes.Text : InfoText, sqltypes.CHAR: InfoChar, } diff --git a/lib/sqlalchemy/databases/maxdb.py b/lib/sqlalchemy/databases/maxdb.py index 3ca1bd61cc..b5d397d545 100644 --- a/lib/sqlalchemy/databases/maxdb.py +++ b/lib/sqlalchemy/databases/maxdb.py @@ -353,7 +353,7 @@ colspecs = { sqltypes.String: MaxString, sqltypes.Binary: MaxBlob, sqltypes.Boolean: MaxBoolean, - sqltypes.TEXT: MaxText, + sqltypes.Text: MaxText, sqltypes.CHAR: MaxChar, sqltypes.TIMESTAMP: MaxTimestamp, sqltypes.BLOB: MaxBlob, diff --git a/lib/sqlalchemy/databases/mssql.py b/lib/sqlalchemy/databases/mssql.py index 8d43e7616f..69cf1bcc60 100644 --- a/lib/sqlalchemy/databases/mssql.py +++ b/lib/sqlalchemy/databases/mssql.py @@ -192,7 +192,7 @@ class MSDate_pymssql(MSDate): return value return process -class MSText(sqltypes.TEXT): +class MSText(sqltypes.Text): def get_col_spec(self): if self.dialect.text_as_varchar: return "VARCHAR(max)" @@ -380,7 +380,7 @@ class MSSQLDialect(default.DefaultDialect): sqltypes.String : MSString, sqltypes.Binary : MSBinary, sqltypes.Boolean : MSBoolean, - sqltypes.TEXT : MSText, + sqltypes.Text : MSText, sqltypes.CHAR: MSChar, sqltypes.NCHAR: MSNChar, sqltypes.TIMESTAMP: MSTimeStamp, diff --git a/lib/sqlalchemy/databases/mysql.py b/lib/sqlalchemy/databases/mysql.py index d89b6f5dba..4f7f10a78a 100644 --- a/lib/sqlalchemy/databases/mysql.py +++ b/lib/sqlalchemy/databases/mysql.py @@ -685,7 +685,7 @@ class MSYear(sqltypes.TypeEngine): else: return "YEAR(%s)" % self.length -class MSText(_StringType, sqltypes.TEXT): +class MSText(_StringType, sqltypes.Text): """MySQL TEXT type, for text up to 2^16 characters.""" def __init__(self, length=None, **kwargs): @@ -724,7 +724,7 @@ class MSText(_StringType, sqltypes.TEXT): """ _StringType.__init__(self, **kwargs) - sqltypes.TEXT.__init__(self, length, + sqltypes.Text.__init__(self, length, kwargs.get('convert_unicode', False), kwargs.get('assert_unicode', None)) def get_col_spec(self): @@ -1318,7 +1318,7 @@ colspecs = { sqltypes.String: MSString, sqltypes.Binary: MSBlob, sqltypes.Boolean: MSBoolean, - sqltypes.TEXT: MSText, + sqltypes.Text: MSText, sqltypes.CHAR: MSChar, sqltypes.NCHAR: MSNChar, sqltypes.TIMESTAMP: MSTimeStamp, diff --git a/lib/sqlalchemy/databases/oracle.py b/lib/sqlalchemy/databases/oracle.py index 163b4f58b8..79a276d62d 100644 --- a/lib/sqlalchemy/databases/oracle.py +++ b/lib/sqlalchemy/databases/oracle.py @@ -86,7 +86,7 @@ class OracleString(sqltypes.String): def get_col_spec(self): return "VARCHAR(%(length)s)" % {'length' : self.length} -class OracleText(sqltypes.TEXT): +class OracleText(sqltypes.Text): def get_dbapi_type(self, dbapi): return dbapi.CLOB @@ -169,7 +169,7 @@ colspecs = { sqltypes.String : OracleString, sqltypes.Binary : OracleBinary, sqltypes.Boolean : OracleBoolean, - sqltypes.TEXT : OracleText, + sqltypes.Text : OracleText, sqltypes.TIMESTAMP : OracleTimestamp, sqltypes.CHAR: OracleChar, } diff --git a/lib/sqlalchemy/databases/postgres.py b/lib/sqlalchemy/databases/postgres.py index 01b1b10d32..eacd0c7e11 100644 --- a/lib/sqlalchemy/databases/postgres.py +++ b/lib/sqlalchemy/databases/postgres.py @@ -89,7 +89,7 @@ class PGInterval(sqltypes.TypeEngine): def get_col_spec(self): return "INTERVAL" -class PGText(sqltypes.TEXT): +class PGText(sqltypes.Text): def get_col_spec(self): return "TEXT" @@ -166,7 +166,7 @@ colspecs = { sqltypes.String : PGString, sqltypes.Binary : PGBinary, sqltypes.Boolean : PGBoolean, - sqltypes.TEXT : PGText, + sqltypes.Text : PGText, sqltypes.CHAR: PGChar, } diff --git a/lib/sqlalchemy/databases/sqlite.py b/lib/sqlalchemy/databases/sqlite.py index fa4466688e..b357f48dec 100644 --- a/lib/sqlalchemy/databases/sqlite.py +++ b/lib/sqlalchemy/databases/sqlite.py @@ -107,7 +107,7 @@ class SLTime(DateTimeMixin, sqltypes.Time): return tup and datetime.time(*tup[3:7]) return process -class SLText(sqltypes.TEXT): +class SLText(sqltypes.Text): def get_col_spec(self): return "TEXT" @@ -152,7 +152,7 @@ colspecs = { sqltypes.String : SLString, sqltypes.Binary : SLBinary, sqltypes.Boolean : SLBoolean, - sqltypes.TEXT : SLText, + sqltypes.Text : SLText, sqltypes.CHAR: SLChar, } diff --git a/lib/sqlalchemy/databases/sybase.py b/lib/sqlalchemy/databases/sybase.py index 2209594ed7..c170a9723c 100644 --- a/lib/sqlalchemy/databases/sybase.py +++ b/lib/sqlalchemy/databases/sybase.py @@ -295,7 +295,7 @@ class SybaseTime_pyodbc(sqltypes.Time): return datetime.datetime(1970, 1, 1, value.hour, value.minute, value.second, value.microsecond) return process -class SybaseText(sqltypes.TEXT): +class SybaseText(sqltypes.Text): def get_col_spec(self): return "TEXT" @@ -425,7 +425,7 @@ class SybaseSQLDialect(default.DefaultDialect): sqltypes.String : SybaseString, sqltypes.Binary : SybaseBinary, sqltypes.Boolean : SybaseBoolean, - sqltypes.TEXT : SybaseText, + sqltypes.Text : SybaseText, sqltypes.CHAR : SybaseChar, sqltypes.TIMESTAMP : SybaseTimeStamp, sqltypes.FLOAT : SybaseFloat, diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 35a12efe39..70343a7b4d 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -286,7 +286,7 @@ class DefaultCompiler(engine.Compiled): return index.name def visit_typeclause(self, typeclause, **kwargs): - return typeclause.type.dialect_impl(self.dialect).get_col_spec() + return typeclause.type.dialect_impl(self.dialect, _for_ddl=True).get_col_spec() def visit_textclause(self, textclause, **kwargs): if textclause.typemap is not None: diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index 8ad537e74b..b3b04b678c 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -1729,27 +1729,12 @@ class _BindParamClause(ClauseElement, _CompareMixin): self.shortname = shortname if type_ is None: - self.type = self.type_map.get(type(value), sqltypes.NullType)() + self.type = sqltypes.type_map.get(type(value), sqltypes.NullType)() elif isinstance(type_, type): self.type = type_() else: self.type = type_ - # TODO: move to types module, obviously - # using VARCHAR/NCHAR so that we dont get the genericized "String" - # type which usually resolves to TEXT/CLOB - type_map = { - str : sqltypes.VARCHAR, - unicode : sqltypes.NCHAR, - int : sqltypes.Integer, - float : sqltypes.Numeric, - datetime.date : sqltypes.Date, - datetime.datetime : sqltypes.DateTime, - datetime.time : sqltypes.Time, - datetime.timedelta : sqltypes.Interval, - type(None):sqltypes.NullType - } - def _clone(self): c = ClauseElement._clone(self) if self.unique: diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py index 5ab9ad4509..22a89ccc2c 100644 --- a/lib/sqlalchemy/types.py +++ b/lib/sqlalchemy/types.py @@ -12,12 +12,13 @@ For more information see the SQLAlchemy documentation on types. """ __all__ = [ 'TypeEngine', 'TypeDecorator', 'AbstractType', - 'INT', 'CHAR', 'VARCHAR', 'NCHAR', 'TEXT', 'FLOAT', + 'INT', 'CHAR', 'VARCHAR', 'NCHAR', 'TEXT', 'Text', 'FLOAT', 'NUMERIC', 'DECIMAL', 'TIMESTAMP', 'DATETIME', 'CLOB', 'BLOB', 'BOOLEAN', 'SMALLINT', 'DATE', 'TIME', 'String', 'Integer', 'SmallInteger','Smallinteger', 'Numeric', 'Float', 'DateTime', 'Date', 'Time', 'Binary', 'Boolean', 'Unicode', 'PickleType', 'Interval', + 'type_map' ] import inspect @@ -25,7 +26,7 @@ import datetime as dt import warnings from sqlalchemy import exceptions -from sqlalchemy.util import pickle, Decimal as _python_Decimal +from sqlalchemy.util import pickle, Decimal as _python_Decimal, warn_deprecated NoneType = type(None) class _UserTypeAdapter(type): @@ -155,7 +156,7 @@ class AbstractType(object): for k in inspect.getargspec(self.__init__)[0][1:]])) class TypeEngine(AbstractType): - def dialect_impl(self, dialect): + def dialect_impl(self, dialect, **kwargs): try: return self._impl_dict[dialect] except AttributeError: @@ -196,7 +197,7 @@ class TypeDecorator(AbstractType): raise exceptions.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): + def dialect_impl(self, dialect, **kwargs): try: return self._impl_dict[dialect] except AttributeError: @@ -340,6 +341,15 @@ class Concatenable(object): return op class String(Concatenable, TypeEngine): + """A sized string type. + + Usually 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.) + + a String with no length will adapt itself automatically to a Text + object at the dialect level (this behavior is deprecated in 0.4). + """ def __init__(self, length=None, convert_unicode=False, assert_unicode=None): self.length = length self.convert_unicode = convert_unicode @@ -380,13 +390,19 @@ class String(Concatenable, TypeEngine): else: return None + def dialect_impl(self, dialect, **kwargs): + _for_ddl = kwargs.pop('_for_ddl', False) + if self.length is None: + warn_deprecated("Using String type with no length for CREATE TABLE is deprecated; use the Text type explicitly") + return TypeEngine.dialect_impl(self, dialect, **kwargs) + def get_search_list(self): l = super(String, self).get_search_list() # if we are String or Unicode with no length, - # return TEXT as the highest-priority type + # return Text as the highest-priority type # to be adapted by the dialect if self.length is None and l[0] in (String, Unicode): - return (TEXT,) + l + return (Text,) + l else: return l @@ -394,6 +410,8 @@ class String(Concatenable, TypeEngine): return dbapi.STRING class Unicode(String): + """A synonym for String(length, convert_unicode=True, assert_unicode='warn').""" + def __init__(self, length=None, **kwargs): kwargs['convert_unicode'] = True kwargs['assert_unicode'] = 'warn' @@ -413,6 +431,8 @@ class SmallInteger(Integer): Smallinteger = SmallInteger class Numeric(TypeEngine): + """Numeric datatype, usually resolves to DECIMAL or NUMERIC.""" + def __init__(self, precision=10, length=2, asdecimal=True): self.precision = precision self.length = length @@ -634,7 +654,8 @@ class Interval(TypeDecorator): return process class FLOAT(Float): pass -class TEXT(String): pass +class Text(String): pass +TEXT = Text class NUMERIC(Numeric): pass class DECIMAL(Numeric): pass class INT(Integer): pass @@ -644,7 +665,7 @@ class TIMESTAMP(DateTime): pass class DATETIME(DateTime): pass class DATE(Date): pass class TIME(Time): pass -class CLOB(TEXT): pass +class CLOB(Text): pass class VARCHAR(String): pass class CHAR(String): pass class NCHAR(Unicode): pass @@ -652,3 +673,18 @@ class BLOB(Binary): pass class BOOLEAN(Boolean): pass NULLTYPE = NullType() + +# using VARCHAR/NCHAR so that we dont get the genericized "String" +# type which usually resolves to TEXT/CLOB +type_map = { + str : VARCHAR, + unicode : NCHAR, + int : Integer, + float : Numeric, + dt.date : Date, + dt.datetime : DateTime, + dt.time : Time, + dt.timedelta : Interval, + type(None): NullType +} + -- 2.47.3