1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
Rules for Migrating TypeEngine classes to 0.6
---------------------------------------------
 
1. the TypeEngine classes are used for:
 
    a. Specifying behavior which needs to occur for bind parameters
    or result row columns.
 
    b. Specifying types that are entirely specific to the database
    in use and have no analogue in the sqlalchemy.types package.
 
    c. Specifying types where there is an analogue in sqlalchemy.types,
    but the database in use takes vendor-specific flags for those
    types.
 
    d. If a TypeEngine class doesn't provide any of this, it should be
    *removed* from the dialect.
 
2. the TypeEngine classes are *no longer* used for generating DDL.  Dialects
now have a TypeCompiler subclass which uses the same visit_XXX model as
other compilers.
 
3. the "ischema_names" and "colspecs" dictionaries are now required members on
the Dialect class.
 
4. The names of types within dialects are now important.   If a dialect-specific type
is a subclass of an existing generic type and is only provided for bind/result behavior, 
the current mixed case naming can remain, i.e. _PGNumeric for Numeric - in this case, 
end users would never need to use _PGNumeric directly.   However, if a dialect-specific 
type is specifying a type *or* arguments that are not present generically, it should
match the real name of the type on that backend, in uppercase.  E.g. postgresql.INET,
mysql.ENUM, postgresql.ARRAY.
 
Or follow this handy flowchart:
 
    is the type meant to provide bind/result                  is the type the same name as an
    behavior to a generic type (i.e. MixedCase)  ---- no ---> UPPERCASE type in types.py ?
    type in types.py ?                                          |                     |
                    |                                           no                    yes
                   yes                                          |                     |
                    |                                           |             does your type need special
                    |                                           +<--- yes --- behavior or arguments ?
                    |                                           |                               |
                    |                                           |                              no
           name the type using                                  |                               |
           _MixedCase, i.e.                                     v                               V
           _OracleBoolean. it                          name the type                        don't make a
           stays private to the dialect                identically as that                  type, make sure the dialect's
           and is invoked *only* via                   within the DB,                       base.py imports the types.py
           the colspecs dict.                          using UPPERCASE                      UPPERCASE name into its namespace
                    |                                  (i.e. BIT, NCHAR, INTERVAL).
                    |                                  Users can import it.
                    |                                       |
                    v                                       v
           subclass the closest                        is the name of this type
           MixedCase type types.py,                    identical to an UPPERCASE
           i.e.                        <--- no ------- name in types.py ?
           class _DateTime(types.DateTime),
           class DATETIME2(types.DateTime),                   |
           class BIT(types.TypeEngine).                      yes
                                                              |
                                                              v
                                                        the type should
                                                        subclass the
                                                        UPPERCASE
                                                        type in types.py
                                                        (i.e. class BLOB(types.BLOB))
 
 
Example 1.   pysqlite needs bind/result processing for the DateTime type in types.py, 
which applies to all DateTimes and subclasses.   It's named _SLDateTime and 
subclasses types.DateTime.
 
Example 2.  MS-SQL has a TIME type which takes a non-standard "precision" argument
that is rendered within DDL.   So it's named TIME in the MS-SQL dialect's base.py, 
and subclasses types.TIME.  Users can then say mssql.TIME(precision=10).
 
Example 3.  MS-SQL dialects also need special bind/result processing for date 
But its DATE type doesn't render DDL differently than that of a plain 
DATE, i.e. it takes no special arguments.  Therefore we are just adding behavior
to types.Date, so it's named _MSDate in the MS-SQL dialect's base.py, and subclasses
types.Date.
 
Example 4.  MySQL has a SET type, there's no analogue for this in types.py. So
MySQL names it SET in the dialect's base.py, and it subclasses types.String, since 
it ultimately deals with strings.
 
Example 5.  PostgreSQL has a DATETIME type.  The DBAPIs handle dates correctly,
and no special arguments are used in PG's DDL beyond what types.py provides.
PostgreSQL dialect therefore imports types.DATETIME into its base.py.
 
Ideally one should be able to specify a schema using names imported completely from a 
dialect, all matching the real name on that backend:
 
   from sqlalchemy.dialects.postgresql import base as pg
 
   t = Table('mytable', metadata,
              Column('id', pg.INTEGER, primary_key=True),
              Column('name', pg.VARCHAR(300)),
              Column('inetaddr', pg.INET)
   )
 
where above, the INTEGER and VARCHAR types are ultimately from sqlalchemy.types, 
but the PG dialect makes them available in its own namespace.
 
5. "colspecs" now is a dictionary of generic or uppercased types from sqlalchemy.types
linked to types specified in the dialect.   Again, if a type in the dialect does not
specify any special behavior for bind_processor() or result_processor() and does not
indicate a special type only available in this database, it must be *removed* from the 
module and from this dictionary.
 
6. "ischema_names" indicates string descriptions of types as returned from the database
linked to TypeEngine classes.
 
    a. The string name should be matched to the most specific type possible within
    sqlalchemy.types, unless there is no matching type within sqlalchemy.types in which
    case it points to a dialect type.   *It doesn't matter* if the dialect has its 
    own subclass of that type with special bind/result behavior - reflect to the types.py
    UPPERCASE type as much as possible.   With very few exceptions, all types
    should reflect to an UPPERCASE type.
 
    b. If the dialect contains a matching dialect-specific type that takes extra arguments 
    which the generic one does not, then point to the dialect-specific type.  E.g.
    mssql.VARCHAR takes a "collation" parameter which should be preserved.
 
5. DDL, or what was formerly issued by "get_col_spec()", is now handled exclusively by
a subclass of compiler.GenericTypeCompiler.
 
    a. your TypeCompiler class will receive generic and uppercase types from 
    sqlalchemy.types.  Do not assume the presence of dialect-specific attributes on
    these types. 
 
    b. the visit_UPPERCASE methods on GenericTypeCompiler should *not* be overridden with
    methods that produce a different DDL name.   Uppercase types don't do any kind of 
    "guessing" - if visit_TIMESTAMP is called, the DDL should render as TIMESTAMP in
    all cases, regardless of whether or not that type is legal on the backend database.
 
    c. the visit_UPPERCASE methods *should* be overridden with methods that add additional
    arguments and flags to those types.
 
    d. the visit_lowercase methods are overridden to provide an interpretation of a generic 
    type.  E.g.  visit_large_binary() might be overridden to say "return self.visit_BIT(type_)".
 
    e. visit_lowercase methods should *never* render strings directly - it should always
    be via calling a visit_UPPERCASE() method.