| 1 | # sybase.py |
|---|
| 2 | # Copyright (C) 2007 Fisch Asset Management AG http://www.fam.ch |
|---|
| 3 | # Coding: Alexander Houben alexander.houben@thor-solutions.ch |
|---|
| 4 | # |
|---|
| 5 | # This module is part of SQLAlchemy and is released under |
|---|
| 6 | # the MIT License: http://www.opensource.org/licenses/mit-license.php |
|---|
| 7 | |
|---|
| 8 | """ |
|---|
| 9 | Sybase database backend. |
|---|
| 10 | |
|---|
| 11 | Known issues / TODO: |
|---|
| 12 | |
|---|
| 13 | * Uses the mx.ODBC driver from egenix (version 2.1.0) |
|---|
| 14 | * The current version of sqlalchemy.databases.sybase only supports |
|---|
| 15 | mx.ODBC.Windows (other platforms such as mx.ODBC.unixODBC still need |
|---|
| 16 | some development) |
|---|
| 17 | * Support for pyodbc has been built in but is not yet complete (needs |
|---|
| 18 | further development) |
|---|
| 19 | * Results of running tests/alltests.py: |
|---|
| 20 | Ran 934 tests in 287.032s |
|---|
| 21 | FAILED (failures=3, errors=1) |
|---|
| 22 | * Tested on 'Adaptive Server Anywhere 9' (version 9.0.1.1751) |
|---|
| 23 | """ |
|---|
| 24 | |
|---|
| 25 | import datetime, operator |
|---|
| 26 | |
|---|
| 27 | from sqlalchemy import util, sql, schema, exc |
|---|
| 28 | from sqlalchemy.sql import compiler, expression |
|---|
| 29 | from sqlalchemy.engine import default, base |
|---|
| 30 | from sqlalchemy import types as sqltypes |
|---|
| 31 | from sqlalchemy.sql import operators as sql_operators |
|---|
| 32 | from sqlalchemy import MetaData, Table, Column |
|---|
| 33 | from sqlalchemy import String, Integer, SMALLINT, CHAR, ForeignKey |
|---|
| 34 | |
|---|
| 35 | |
|---|
| 36 | __all__ = [ |
|---|
| 37 | 'SybaseTypeError' |
|---|
| 38 | 'SybaseNumeric', 'SybaseFloat', 'SybaseInteger', 'SybaseBigInteger', |
|---|
| 39 | 'SybaseTinyInteger', 'SybaseSmallInteger', |
|---|
| 40 | 'SybaseDateTime_mxodbc', 'SybaseDateTime_pyodbc', |
|---|
| 41 | 'SybaseDate_mxodbc', 'SybaseDate_pyodbc', |
|---|
| 42 | 'SybaseTime_mxodbc', 'SybaseTime_pyodbc', |
|---|
| 43 | 'SybaseText', 'SybaseString', 'SybaseChar', 'SybaseBinary', |
|---|
| 44 | 'SybaseBoolean', 'SybaseTimeStamp', 'SybaseMoney', 'SybaseSmallMoney', |
|---|
| 45 | 'SybaseUniqueIdentifier', |
|---|
| 46 | ] |
|---|
| 47 | |
|---|
| 48 | |
|---|
| 49 | RESERVED_WORDS = set([ |
|---|
| 50 | "add", "all", "alter", "and", |
|---|
| 51 | "any", "as", "asc", "backup", |
|---|
| 52 | "begin", "between", "bigint", "binary", |
|---|
| 53 | "bit", "bottom", "break", "by", |
|---|
| 54 | "call", "capability", "cascade", "case", |
|---|
| 55 | "cast", "char", "char_convert", "character", |
|---|
| 56 | "check", "checkpoint", "close", "comment", |
|---|
| 57 | "commit", "connect", "constraint", "contains", |
|---|
| 58 | "continue", "convert", "create", "cross", |
|---|
| 59 | "cube", "current", "current_timestamp", "current_user", |
|---|
| 60 | "cursor", "date", "dbspace", "deallocate", |
|---|
| 61 | "dec", "decimal", "declare", "default", |
|---|
| 62 | "delete", "deleting", "desc", "distinct", |
|---|
| 63 | "do", "double", "drop", "dynamic", |
|---|
| 64 | "else", "elseif", "encrypted", "end", |
|---|
| 65 | "endif", "escape", "except", "exception", |
|---|
| 66 | "exec", "execute", "existing", "exists", |
|---|
| 67 | "externlogin", "fetch", "first", "float", |
|---|
| 68 | "for", "force", "foreign", "forward", |
|---|
| 69 | "from", "full", "goto", "grant", |
|---|
| 70 | "group", "having", "holdlock", "identified", |
|---|
| 71 | "if", "in", "index", "index_lparen", |
|---|
| 72 | "inner", "inout", "insensitive", "insert", |
|---|
| 73 | "inserting", "install", "instead", "int", |
|---|
| 74 | "integer", "integrated", "intersect", "into", |
|---|
| 75 | "iq", "is", "isolation", "join", |
|---|
| 76 | "key", "lateral", "left", "like", |
|---|
| 77 | "lock", "login", "long", "match", |
|---|
| 78 | "membership", "message", "mode", "modify", |
|---|
| 79 | "natural", "new", "no", "noholdlock", |
|---|
| 80 | "not", "notify", "null", "numeric", |
|---|
| 81 | "of", "off", "on", "open", |
|---|
| 82 | "option", "options", "or", "order", |
|---|
| 83 | "others", "out", "outer", "over", |
|---|
| 84 | "passthrough", "precision", "prepare", "primary", |
|---|
| 85 | "print", "privileges", "proc", "procedure", |
|---|
| 86 | "publication", "raiserror", "readtext", "real", |
|---|
| 87 | "reference", "references", "release", "remote", |
|---|
| 88 | "remove", "rename", "reorganize", "resource", |
|---|
| 89 | "restore", "restrict", "return", "revoke", |
|---|
| 90 | "right", "rollback", "rollup", "save", |
|---|
| 91 | "savepoint", "scroll", "select", "sensitive", |
|---|
| 92 | "session", "set", "setuser", "share", |
|---|
| 93 | "smallint", "some", "sqlcode", "sqlstate", |
|---|
| 94 | "start", "stop", "subtrans", "subtransaction", |
|---|
| 95 | "synchronize", "syntax_error", "table", "temporary", |
|---|
| 96 | "then", "time", "timestamp", "tinyint", |
|---|
| 97 | "to", "top", "tran", "trigger", |
|---|
| 98 | "truncate", "tsequal", "unbounded", "union", |
|---|
| 99 | "unique", "unknown", "unsigned", "update", |
|---|
| 100 | "updating", "user", "using", "validate", |
|---|
| 101 | "values", "varbinary", "varchar", "variable", |
|---|
| 102 | "varying", "view", "wait", "waitfor", |
|---|
| 103 | "when", "where", "while", "window", |
|---|
| 104 | "with", "with_cube", "with_lparen", "with_rollup", |
|---|
| 105 | "within", "work", "writetext", |
|---|
| 106 | ]) |
|---|
| 107 | |
|---|
| 108 | ischema = MetaData() |
|---|
| 109 | |
|---|
| 110 | tables = Table("SYSTABLE", ischema, |
|---|
| 111 | Column("table_id", Integer, primary_key=True), |
|---|
| 112 | Column("file_id", SMALLINT), |
|---|
| 113 | Column("table_name", CHAR(128)), |
|---|
| 114 | Column("table_type", CHAR(10)), |
|---|
| 115 | Column("creator", Integer), |
|---|
| 116 | #schema="information_schema" |
|---|
| 117 | ) |
|---|
| 118 | |
|---|
| 119 | domains = Table("SYSDOMAIN", ischema, |
|---|
| 120 | Column("domain_id", Integer, primary_key=True), |
|---|
| 121 | Column("domain_name", CHAR(128)), |
|---|
| 122 | Column("type_id", SMALLINT), |
|---|
| 123 | Column("precision", SMALLINT, quote=True), |
|---|
| 124 | #schema="information_schema" |
|---|
| 125 | ) |
|---|
| 126 | |
|---|
| 127 | columns = Table("SYSCOLUMN", ischema, |
|---|
| 128 | Column("column_id", Integer, primary_key=True), |
|---|
| 129 | Column("table_id", Integer, ForeignKey(tables.c.table_id)), |
|---|
| 130 | Column("pkey", CHAR(1)), |
|---|
| 131 | Column("column_name", CHAR(128)), |
|---|
| 132 | Column("nulls", CHAR(1)), |
|---|
| 133 | Column("width", SMALLINT), |
|---|
| 134 | Column("domain_id", SMALLINT, ForeignKey(domains.c.domain_id)), |
|---|
| 135 | # FIXME: should be mx.BIGINT |
|---|
| 136 | Column("max_identity", Integer), |
|---|
| 137 | # FIXME: should be mx.ODBC.Windows.LONGVARCHAR |
|---|
| 138 | Column("default", String), |
|---|
| 139 | Column("scale", Integer), |
|---|
| 140 | #schema="information_schema" |
|---|
| 141 | ) |
|---|
| 142 | |
|---|
| 143 | foreignkeys = Table("SYSFOREIGNKEY", ischema, |
|---|
| 144 | Column("foreign_table_id", Integer, ForeignKey(tables.c.table_id), primary_key=True), |
|---|
| 145 | Column("foreign_key_id", SMALLINT, primary_key=True), |
|---|
| 146 | Column("primary_table_id", Integer, ForeignKey(tables.c.table_id)), |
|---|
| 147 | #schema="information_schema" |
|---|
| 148 | ) |
|---|
| 149 | fkcols = Table("SYSFKCOL", ischema, |
|---|
| 150 | Column("foreign_table_id", Integer, ForeignKey(columns.c.table_id), primary_key=True), |
|---|
| 151 | Column("foreign_key_id", SMALLINT, ForeignKey(foreignkeys.c.foreign_key_id), primary_key=True), |
|---|
| 152 | Column("foreign_column_id", Integer, ForeignKey(columns.c.column_id), primary_key=True), |
|---|
| 153 | Column("primary_column_id", Integer), |
|---|
| 154 | #schema="information_schema" |
|---|
| 155 | ) |
|---|
| 156 | |
|---|
| 157 | class SybaseTypeError(sqltypes.TypeEngine): |
|---|
| 158 | def result_processor(self, dialect): |
|---|
| 159 | return None |
|---|
| 160 | |
|---|
| 161 | def bind_processor(self, dialect): |
|---|
| 162 | def process(value): |
|---|
| 163 | raise exc.InvalidRequestError("Data type not supported", [value]) |
|---|
| 164 | return process |
|---|
| 165 | |
|---|
| 166 | def get_col_spec(self): |
|---|
| 167 | raise exc.CompileError("Data type not supported") |
|---|
| 168 | |
|---|
| 169 | class SybaseNumeric(sqltypes.Numeric): |
|---|
| 170 | def get_col_spec(self): |
|---|
| 171 | if self.scale is None: |
|---|
| 172 | if self.precision is None: |
|---|
| 173 | return "NUMERIC" |
|---|
| 174 | else: |
|---|
| 175 | return "NUMERIC(%(precision)s)" % {'precision' : self.precision} |
|---|
| 176 | else: |
|---|
| 177 | return "NUMERIC(%(precision)s, %(scale)s)" % {'precision': self.precision, 'scale' : self.scale} |
|---|
| 178 | |
|---|
| 179 | class SybaseFloat(sqltypes.FLOAT, SybaseNumeric): |
|---|
| 180 | def __init__(self, precision = 10, asdecimal = False, scale = 2, **kwargs): |
|---|
| 181 | super(sqltypes.FLOAT, self).__init__(precision, asdecimal, **kwargs) |
|---|
| 182 | self.scale = scale |
|---|
| 183 | |
|---|
| 184 | def get_col_spec(self): |
|---|
| 185 | # if asdecimal is True, handle same way as SybaseNumeric |
|---|
| 186 | if self.asdecimal: |
|---|
| 187 | return SybaseNumeric.get_col_spec(self) |
|---|
| 188 | if self.precision is None: |
|---|
| 189 | return "FLOAT" |
|---|
| 190 | else: |
|---|
| 191 | return "FLOAT(%(precision)s)" % {'precision': self.precision} |
|---|
| 192 | |
|---|
| 193 | def result_processor(self, dialect): |
|---|
| 194 | def process(value): |
|---|
| 195 | if value is None: |
|---|
| 196 | return None |
|---|
| 197 | return float(value) |
|---|
| 198 | if self.asdecimal: |
|---|
| 199 | return SybaseNumeric.result_processor(self, dialect) |
|---|
| 200 | return process |
|---|
| 201 | |
|---|
| 202 | class SybaseInteger(sqltypes.Integer): |
|---|
| 203 | def get_col_spec(self): |
|---|
| 204 | return "INTEGER" |
|---|
| 205 | |
|---|
| 206 | class SybaseBigInteger(SybaseInteger): |
|---|
| 207 | def get_col_spec(self): |
|---|
| 208 | return "BIGINT" |
|---|
| 209 | |
|---|
| 210 | class SybaseTinyInteger(SybaseInteger): |
|---|
| 211 | def get_col_spec(self): |
|---|
| 212 | return "TINYINT" |
|---|
| 213 | |
|---|
| 214 | class SybaseSmallInteger(SybaseInteger): |
|---|
| 215 | def get_col_spec(self): |
|---|
| 216 | return "SMALLINT" |
|---|
| 217 | |
|---|
| 218 | class SybaseDateTime_mxodbc(sqltypes.DateTime): |
|---|
| 219 | def __init__(self, *a, **kw): |
|---|
| 220 | super(SybaseDateTime_mxodbc, self).__init__(False) |
|---|
| 221 | |
|---|
| 222 | def get_col_spec(self): |
|---|
| 223 | return "DATETIME" |
|---|
| 224 | |
|---|
| 225 | class SybaseDateTime_pyodbc(sqltypes.DateTime): |
|---|
| 226 | def __init__(self, *a, **kw): |
|---|
| 227 | super(SybaseDateTime_pyodbc, self).__init__(False) |
|---|
| 228 | |
|---|
| 229 | def get_col_spec(self): |
|---|
| 230 | return "DATETIME" |
|---|
| 231 | |
|---|
| 232 | def result_processor(self, dialect): |
|---|
| 233 | def process(value): |
|---|
| 234 | if value is None: |
|---|
| 235 | return None |
|---|
| 236 | # Convert the datetime.datetime back to datetime.time |
|---|
| 237 | return value |
|---|
| 238 | return process |
|---|
| 239 | |
|---|
| 240 | def bind_processor(self, dialect): |
|---|
| 241 | def process(value): |
|---|
| 242 | if value is None: |
|---|
| 243 | return None |
|---|
| 244 | return value |
|---|
| 245 | return process |
|---|
| 246 | |
|---|
| 247 | class SybaseDate_mxodbc(sqltypes.Date): |
|---|
| 248 | def __init__(self, *a, **kw): |
|---|
| 249 | super(SybaseDate_mxodbc, self).__init__(False) |
|---|
| 250 | |
|---|
| 251 | def get_col_spec(self): |
|---|
| 252 | return "DATE" |
|---|
| 253 | |
|---|
| 254 | class SybaseDate_pyodbc(sqltypes.Date): |
|---|
| 255 | def __init__(self, *a, **kw): |
|---|
| 256 | super(SybaseDate_pyodbc, self).__init__(False) |
|---|
| 257 | |
|---|
| 258 | def get_col_spec(self): |
|---|
| 259 | return "DATE" |
|---|
| 260 | |
|---|
| 261 | class SybaseTime_mxodbc(sqltypes.Time): |
|---|
| 262 | def __init__(self, *a, **kw): |
|---|
| 263 | super(SybaseTime_mxodbc, self).__init__(False) |
|---|
| 264 | |
|---|
| 265 | def get_col_spec(self): |
|---|
| 266 | return "DATETIME" |
|---|
| 267 | |
|---|
| 268 | def result_processor(self, dialect): |
|---|
| 269 | def process(value): |
|---|
| 270 | if value is None: |
|---|
| 271 | return None |
|---|
| 272 | # Convert the datetime.datetime back to datetime.time |
|---|
| 273 | return datetime.time(value.hour, value.minute, value.second, value.microsecond) |
|---|
| 274 | return process |
|---|
| 275 | |
|---|
| 276 | class SybaseTime_pyodbc(sqltypes.Time): |
|---|
| 277 | def __init__(self, *a, **kw): |
|---|
| 278 | super(SybaseTime_pyodbc, self).__init__(False) |
|---|
| 279 | |
|---|
| 280 | def get_col_spec(self): |
|---|
| 281 | return "DATETIME" |
|---|
| 282 | |
|---|
| 283 | def result_processor(self, dialect): |
|---|
| 284 | def process(value): |
|---|
| 285 | if value is None: |
|---|
| 286 | return None |
|---|
| 287 | # Convert the datetime.datetime back to datetime.time |
|---|
| 288 | return datetime.time(value.hour, value.minute, value.second, value.microsecond) |
|---|
| 289 | return process |
|---|
| 290 | |
|---|
| 291 | def bind_processor(self, dialect): |
|---|
| 292 | def process(value): |
|---|
| 293 | if value is None: |
|---|
| 294 | return None |
|---|
| 295 | return datetime.datetime(1970, 1, 1, value.hour, value.minute, value.second, value.microsecond) |
|---|
| 296 | return process |
|---|
| 297 | |
|---|
| 298 | class SybaseText(sqltypes.Text): |
|---|
| 299 | def get_col_spec(self): |
|---|
| 300 | return "TEXT" |
|---|
| 301 | |
|---|
| 302 | class SybaseString(sqltypes.String): |
|---|
| 303 | def get_col_spec(self): |
|---|
| 304 | return "VARCHAR(%(length)s)" % {'length' : self.length} |
|---|
| 305 | |
|---|
| 306 | class SybaseChar(sqltypes.CHAR): |
|---|
| 307 | def get_col_spec(self): |
|---|
| 308 | return "CHAR(%(length)s)" % {'length' : self.length} |
|---|
| 309 | |
|---|
| 310 | class SybaseBinary(sqltypes.Binary): |
|---|
| 311 | def get_col_spec(self): |
|---|
| 312 | return "IMAGE" |
|---|
| 313 | |
|---|
| 314 | class SybaseBoolean(sqltypes.Boolean): |
|---|
| 315 | def get_col_spec(self): |
|---|
| 316 | return "BIT" |
|---|
| 317 | |
|---|
| 318 | def result_processor(self, dialect): |
|---|
| 319 | def process(value): |
|---|
| 320 | if value is None: |
|---|
| 321 | return None |
|---|
| 322 | return value and True or False |
|---|
| 323 | return process |
|---|
| 324 | |
|---|
| 325 | def bind_processor(self, dialect): |
|---|
| 326 | def process(value): |
|---|
| 327 | if value is True: |
|---|
| 328 | return 1 |
|---|
| 329 | elif value is False: |
|---|
| 330 | return 0 |
|---|
| 331 | elif value is None: |
|---|
| 332 | return None |
|---|
| 333 | else: |
|---|
| 334 | return value and True or False |
|---|
| 335 | return process |
|---|
| 336 | |
|---|
| 337 | class SybaseTimeStamp(sqltypes.TIMESTAMP): |
|---|
| 338 | def get_col_spec(self): |
|---|
| 339 | return "TIMESTAMP" |
|---|
| 340 | |
|---|
| 341 | class SybaseMoney(sqltypes.TypeEngine): |
|---|
| 342 | def get_col_spec(self): |
|---|
| 343 | return "MONEY" |
|---|
| 344 | |
|---|
| 345 | class SybaseSmallMoney(SybaseMoney): |
|---|
| 346 | def get_col_spec(self): |
|---|
| 347 | return "SMALLMONEY" |
|---|
| 348 | |
|---|
| 349 | class SybaseUniqueIdentifier(sqltypes.TypeEngine): |
|---|
| 350 | def get_col_spec(self): |
|---|
| 351 | return "UNIQUEIDENTIFIER" |
|---|
| 352 | |
|---|
| 353 | class SybaseSQLExecutionContext(default.DefaultExecutionContext): |
|---|
| 354 | pass |
|---|
| 355 | |
|---|
| 356 | class SybaseSQLExecutionContext_mxodbc(SybaseSQLExecutionContext): |
|---|
| 357 | |
|---|
| 358 | def __init__(self, dialect, connection, compiled=None, statement=None, parameters=None): |
|---|
| 359 | super(SybaseSQLExecutionContext_mxodbc, self).__init__(dialect, connection, compiled, statement, parameters) |
|---|
| 360 | |
|---|
| 361 | def pre_exec(self): |
|---|
| 362 | super(SybaseSQLExecutionContext_mxodbc, self).pre_exec() |
|---|
| 363 | |
|---|
| 364 | def post_exec(self): |
|---|
| 365 | if self.compiled.isinsert: |
|---|
| 366 | table = self.compiled.statement.table |
|---|
| 367 | # get the inserted values of the primary key |
|---|
| 368 | |
|---|
| 369 | # get any sequence IDs first (using @@identity) |
|---|
| 370 | self.cursor.execute("SELECT @@identity AS lastrowid") |
|---|
| 371 | row = self.cursor.fetchone() |
|---|
| 372 | lastrowid = int(row[0]) |
|---|
| 373 | if lastrowid > 0: |
|---|
| 374 | # an IDENTITY was inserted, fetch it |
|---|
| 375 | # FIXME: always insert in front ? This only works if the IDENTITY is the first column, no ?! |
|---|
| 376 | if not hasattr(self, '_last_inserted_ids') or self._last_inserted_ids is None: |
|---|
| 377 | self._last_inserted_ids = [lastrowid] |
|---|
| 378 | else: |
|---|
| 379 | self._last_inserted_ids = [lastrowid] + self._last_inserted_ids[1:] |
|---|
| 380 | super(SybaseSQLExecutionContext_mxodbc, self).post_exec() |
|---|
| 381 | |
|---|
| 382 | class SybaseSQLExecutionContext_pyodbc(SybaseSQLExecutionContext): |
|---|
| 383 | def __init__(self, dialect, connection, compiled=None, statement=None, parameters=None): |
|---|
| 384 | super(SybaseSQLExecutionContext_pyodbc, self).__init__(dialect, connection, compiled, statement, parameters) |
|---|
| 385 | |
|---|
| 386 | def pre_exec(self): |
|---|
| 387 | super(SybaseSQLExecutionContext_pyodbc, self).pre_exec() |
|---|
| 388 | |
|---|
| 389 | def post_exec(self): |
|---|
| 390 | if self.compiled.isinsert: |
|---|
| 391 | table = self.compiled.statement.table |
|---|
| 392 | # get the inserted values of the primary key |
|---|
| 393 | |
|---|
| 394 | # get any sequence IDs first (using @@identity) |
|---|
| 395 | self.cursor.execute("SELECT @@identity AS lastrowid") |
|---|
| 396 | row = self.cursor.fetchone() |
|---|
| 397 | lastrowid = int(row[0]) |
|---|
| 398 | if lastrowid > 0: |
|---|
| 399 | # an IDENTITY was inserted, fetch it |
|---|
| 400 | # FIXME: always insert in front ? This only works if the IDENTITY is the first column, no ?! |
|---|
| 401 | if not hasattr(self, '_last_inserted_ids') or self._last_inserted_ids is None: |
|---|
| 402 | self._last_inserted_ids = [lastrowid] |
|---|
| 403 | else: |
|---|
| 404 | self._last_inserted_ids = [lastrowid] + self._last_inserted_ids[1:] |
|---|
| 405 | super(SybaseSQLExecutionContext_pyodbc, self).post_exec() |
|---|
| 406 | |
|---|
| 407 | class SybaseSQLDialect(default.DefaultDialect): |
|---|
| 408 | colspecs = { |
|---|
| 409 | # FIXME: unicode support |
|---|
| 410 | #sqltypes.Unicode : SybaseUnicode, |
|---|
| 411 | sqltypes.Integer : SybaseInteger, |
|---|
| 412 | sqltypes.SmallInteger : SybaseSmallInteger, |
|---|
| 413 | sqltypes.Numeric : SybaseNumeric, |
|---|
| 414 | sqltypes.Float : SybaseFloat, |
|---|
| 415 | sqltypes.String : SybaseString, |
|---|
| 416 | sqltypes.Binary : SybaseBinary, |
|---|
| 417 | sqltypes.Boolean : SybaseBoolean, |
|---|
| 418 | sqltypes.Text : SybaseText, |
|---|
| 419 | sqltypes.CHAR : SybaseChar, |
|---|
| 420 | sqltypes.TIMESTAMP : SybaseTimeStamp, |
|---|
| 421 | sqltypes.FLOAT : SybaseFloat, |
|---|
| 422 | } |
|---|
| 423 | |
|---|
| 424 | ischema_names = { |
|---|
| 425 | 'integer' : SybaseInteger, |
|---|
| 426 | 'unsigned int' : SybaseInteger, |
|---|
| 427 | 'unsigned smallint' : SybaseInteger, |
|---|
| 428 | 'unsigned bigint' : SybaseInteger, |
|---|
| 429 | 'bigint': SybaseBigInteger, |
|---|
| 430 | 'smallint' : SybaseSmallInteger, |
|---|
| 431 | 'tinyint' : SybaseTinyInteger, |
|---|
| 432 | 'varchar' : SybaseString, |
|---|
| 433 | 'long varchar' : SybaseText, |
|---|
| 434 | 'char' : SybaseChar, |
|---|
| 435 | 'decimal' : SybaseNumeric, |
|---|
| 436 | 'numeric' : SybaseNumeric, |
|---|
| 437 | 'float' : SybaseFloat, |
|---|
| 438 | 'double' : SybaseFloat, |
|---|
| 439 | 'binary' : SybaseBinary, |
|---|
| 440 | 'long binary' : SybaseBinary, |
|---|
| 441 | 'varbinary' : SybaseBinary, |
|---|
| 442 | 'bit': SybaseBoolean, |
|---|
| 443 | 'image' : SybaseBinary, |
|---|
| 444 | 'timestamp': SybaseTimeStamp, |
|---|
| 445 | 'money': SybaseMoney, |
|---|
| 446 | 'smallmoney': SybaseSmallMoney, |
|---|
| 447 | 'uniqueidentifier': SybaseUniqueIdentifier, |
|---|
| 448 | |
|---|
| 449 | 'java.lang.Object' : SybaseTypeError, |
|---|
| 450 | 'java serialization' : SybaseTypeError, |
|---|
| 451 | } |
|---|
| 452 | |
|---|
| 453 | name = 'sybase' |
|---|
| 454 | # Sybase backend peculiarities |
|---|
| 455 | supports_unicode_statements = False |
|---|
| 456 | supports_sane_rowcount = False |
|---|
| 457 | supports_sane_multi_rowcount = False |
|---|
| 458 | execution_ctx_cls = SybaseSQLExecutionContext |
|---|
| 459 | |
|---|
| 460 | def __new__(cls, dbapi=None, *args, **kwargs): |
|---|
| 461 | if cls != SybaseSQLDialect: |
|---|
| 462 | return super(SybaseSQLDialect, cls).__new__(cls, *args, **kwargs) |
|---|
| 463 | if dbapi: |
|---|
| 464 | print dbapi.__name__ |
|---|
| 465 | dialect = dialect_mapping.get(dbapi.__name__) |
|---|
| 466 | return dialect(*args, **kwargs) |
|---|
| 467 | else: |
|---|
| 468 | return object.__new__(cls, *args, **kwargs) |
|---|
| 469 | |
|---|
| 470 | def __init__(self, **params): |
|---|
| 471 | super(SybaseSQLDialect, self).__init__(**params) |
|---|
| 472 | self.text_as_varchar = False |
|---|
| 473 | # FIXME: what is the default schema for sybase connections (DBA?) ? |
|---|
| 474 | self.set_default_schema_name("dba") |
|---|
| 475 | |
|---|
| 476 | def dbapi(cls, module_name=None): |
|---|
| 477 | if module_name: |
|---|
| 478 | try: |
|---|
| 479 | dialect_cls = dialect_mapping[module_name] |
|---|
| 480 | return dialect_cls.import_dbapi() |
|---|
| 481 | except KeyError: |
|---|
| 482 | raise exc.InvalidRequestError("Unsupported SybaseSQL module '%s' requested (must be " + " or ".join([x for x in dialect_mapping.keys()]) + ")" % module_name) |
|---|
| 483 | else: |
|---|
| 484 | for dialect_cls in dialect_mapping.values(): |
|---|
| 485 | try: |
|---|
| 486 | return dialect_cls.import_dbapi() |
|---|
| 487 | except ImportError, e: |
|---|
| 488 | pass |
|---|
| 489 | else: |
|---|
| 490 | raise ImportError('No DBAPI module detected for SybaseSQL - please install mxodbc') |
|---|
| 491 | dbapi = classmethod(dbapi) |
|---|
| 492 | |
|---|
| 493 | def type_descriptor(self, typeobj): |
|---|
| 494 | newobj = sqltypes.adapt_type(typeobj, self.colspecs) |
|---|
| 495 | return newobj |
|---|
| 496 | |
|---|
| 497 | def last_inserted_ids(self): |
|---|
| 498 | return self.context.last_inserted_ids |
|---|
| 499 | |
|---|
| 500 | def get_default_schema_name(self, connection): |
|---|
| 501 | return self.schema_name |
|---|
| 502 | |
|---|
| 503 | def set_default_schema_name(self, schema_name): |
|---|
| 504 | self.schema_name = schema_name |
|---|
| 505 | |
|---|
| 506 | def do_execute(self, cursor, statement, params, **kwargs): |
|---|
| 507 | params = tuple(params) |
|---|
| 508 | super(SybaseSQLDialect, self).do_execute(cursor, statement, params, **kwargs) |
|---|
| 509 | |
|---|
| 510 | # FIXME: remove ? |
|---|
| 511 | def _execute(self, c, statement, parameters): |
|---|
| 512 | try: |
|---|
| 513 | if parameters == {}: |
|---|
| 514 | parameters = () |
|---|
| 515 | c.execute(statement, parameters) |
|---|
| 516 | self.context.rowcount = c.rowcount |
|---|
| 517 | c.DBPROP_COMMITPRESERVE = "Y" |
|---|
| 518 | except Exception, e: |
|---|
| 519 | raise exc.DBAPIError.instance(statement, parameters, e) |
|---|
| 520 | |
|---|
| 521 | def table_names(self, connection, schema): |
|---|
| 522 | """Ignore the schema and the charset for now.""" |
|---|
| 523 | s = sql.select([tables.c.table_name], |
|---|
| 524 | sql.not_(tables.c.table_name.like("SYS%")) and |
|---|
| 525 | tables.c.creator >= 100 |
|---|
| 526 | ) |
|---|
| 527 | rp = connection.execute(s) |
|---|
| 528 | return [row[0] for row in rp.fetchall()] |
|---|
| 529 | |
|---|
| 530 | def has_table(self, connection, tablename, schema=None): |
|---|
| 531 | # FIXME: ignore schemas for sybase |
|---|
| 532 | s = sql.select([tables.c.table_name], tables.c.table_name == tablename) |
|---|
| 533 | |
|---|
| 534 | c = connection.execute(s) |
|---|
| 535 | row = c.fetchone() |
|---|
| 536 | print "has_table: " + tablename + ": " + str(bool(row is not None)) |
|---|
| 537 | return row is not None |
|---|
| 538 | |
|---|
| 539 | def reflecttable(self, connection, table, include_columns): |
|---|
| 540 | # Get base columns |
|---|
| 541 | if table.schema is not None: |
|---|
| 542 | current_schema = table.schema |
|---|
| 543 | else: |
|---|
| 544 | current_schema = self.get_default_schema_name(connection) |
|---|
| 545 | |
|---|
| 546 | s = sql.select([columns, domains], tables.c.table_name==table.name, from_obj=[columns.join(tables).join(domains)], order_by=[columns.c.column_id]) |
|---|
| 547 | |
|---|
| 548 | c = connection.execute(s) |
|---|
| 549 | found_table = False |
|---|
| 550 | # makes sure we append the columns in the correct order |
|---|
| 551 | while True: |
|---|
| 552 | row = c.fetchone() |
|---|
| 553 | if row is None: |
|---|
| 554 | break |
|---|
| 555 | found_table = True |
|---|
| 556 | (name, type, nullable, charlen, numericprec, numericscale, default, primary_key, max_identity, table_id, column_id) = ( |
|---|
| 557 | row[columns.c.column_name], |
|---|
| 558 | row[domains.c.domain_name], |
|---|
| 559 | row[columns.c.nulls] == 'Y', |
|---|
| 560 | row[columns.c.width], |
|---|
| 561 | row[domains.c.precision], |
|---|
| 562 | row[columns.c.scale], |
|---|
| 563 | row[columns.c.default], |
|---|
| 564 | row[columns.c.pkey] == 'Y', |
|---|
| 565 | row[columns.c.max_identity], |
|---|
| 566 | row[tables.c.table_id], |
|---|
| 567 | row[columns.c.column_id], |
|---|
| 568 | ) |
|---|
| 569 | if include_columns and name not in include_columns: |
|---|
| 570 | continue |
|---|
| 571 | |
|---|
| 572 | # FIXME: else problems with SybaseBinary(size) |
|---|
| 573 | if numericscale == 0: |
|---|
| 574 | numericscale = None |
|---|
| 575 | |
|---|
| 576 | args = [] |
|---|
| 577 | for a in (charlen, numericprec, numericscale): |
|---|
| 578 | if a is not None: |
|---|
| 579 | args.append(a) |
|---|
| 580 | coltype = self.ischema_names.get(type, None) |
|---|
| 581 | if coltype == SybaseString and charlen == -1: |
|---|
| 582 | coltype = SybaseText() |
|---|
| 583 | else: |
|---|
| 584 | if coltype is None: |
|---|
| 585 | util.warn("Did not recognize type '%s' of column '%s'" % |
|---|
| 586 | (type, name)) |
|---|
| 587 | coltype = sqltypes.NULLTYPE |
|---|
| 588 | coltype = coltype(*args) |
|---|
| 589 | colargs = [] |
|---|
| 590 | if default is not None: |
|---|
| 591 | colargs.append(schema.DefaultClause(sql.text(default))) |
|---|
| 592 | |
|---|
| 593 | # any sequences ? |
|---|
| 594 | col = schema.Column(name, coltype, nullable=nullable, primary_key=primary_key, *colargs) |
|---|
| 595 | if int(max_identity) > 0: |
|---|
| 596 | col.sequence = schema.Sequence(name + '_identity') |
|---|
| 597 | col.sequence.start = int(max_identity) |
|---|
| 598 | col.sequence.increment = 1 |
|---|
| 599 | |
|---|
| 600 | # append the column |
|---|
| 601 | table.append_column(col) |
|---|
| 602 | |
|---|
| 603 | # any foreign key constraint for this table ? |
|---|
| 604 | # note: no multi-column foreign keys are considered |
|---|
| 605 | s = "select st1.table_name, sc1.column_name, st2.table_name, sc2.column_name from systable as st1 join sysfkcol on st1.table_id=sysfkcol.foreign_table_id join sysforeignkey join systable as st2 on sysforeignkey.primary_table_id = st2.table_id join syscolumn as sc1 on sysfkcol.foreign_column_id=sc1.column_id and sc1.table_id=st1.table_id join syscolumn as sc2 on sysfkcol.primary_column_id=sc2.column_id and sc2.table_id=st2.table_id where st1.table_name='%(table_name)s';" % { 'table_name' : table.name } |
|---|
| 606 | c = connection.execute(s) |
|---|
| 607 | foreignKeys = {} |
|---|
| 608 | while True: |
|---|
| 609 | row = c.fetchone() |
|---|
| 610 | if row is None: |
|---|
| 611 | break |
|---|
| 612 | (foreign_table, foreign_column, primary_table, primary_column) = ( |
|---|
| 613 | row[0], row[1], row[2], row[3], |
|---|
| 614 | ) |
|---|
| 615 | if not primary_table in foreignKeys.keys(): |
|---|
| 616 | foreignKeys[primary_table] = [['%s' % (foreign_column)], ['%s.%s'%(primary_table, primary_column)]] |
|---|
| 617 | else: |
|---|
| 618 | foreignKeys[primary_table][0].append('%s'%(foreign_column)) |
|---|
| 619 | foreignKeys[primary_table][1].append('%s.%s'%(primary_table, primary_column)) |
|---|
| 620 | for primary_table in foreignKeys.keys(): |
|---|
| 621 | #table.append_constraint(schema.ForeignKeyConstraint(['%s.%s'%(foreign_table, foreign_column)], ['%s.%s'%(primary_table,primary_column)])) |
|---|
| 622 | table.append_constraint(schema.ForeignKeyConstraint(foreignKeys[primary_table][0], foreignKeys[primary_table][1], link_to_name=True)) |
|---|
| 623 | |
|---|
| 624 | if not found_table: |
|---|
| 625 | raise exc.NoSuchTableError(table.name) |
|---|
| 626 | |
|---|
| 627 | |
|---|
| 628 | class SybaseSQLDialect_mxodbc(SybaseSQLDialect): |
|---|
| 629 | execution_ctx_cls = SybaseSQLExecutionContext_mxodbc |
|---|
| 630 | |
|---|
| 631 | def __init__(self, **params): |
|---|
| 632 | super(SybaseSQLDialect_mxodbc, self).__init__(**params) |
|---|
| 633 | |
|---|
| 634 | self.dbapi_type_map = {'getdate' : SybaseDate_mxodbc()} |
|---|
| 635 | |
|---|
| 636 | def import_dbapi(cls): |
|---|
| 637 | #import mx.ODBC.Windows as module |
|---|
| 638 | import mxODBC as module |
|---|
| 639 | return module |
|---|
| 640 | import_dbapi = classmethod(import_dbapi) |
|---|
| 641 | |
|---|
| 642 | colspecs = SybaseSQLDialect.colspecs.copy() |
|---|
| 643 | colspecs[sqltypes.Time] = SybaseTime_mxodbc |
|---|
| 644 | colspecs[sqltypes.Date] = SybaseDate_mxodbc |
|---|
| 645 | colspecs[sqltypes.DateTime] = SybaseDateTime_mxodbc |
|---|
| 646 | |
|---|
| 647 | ischema_names = SybaseSQLDialect.ischema_names.copy() |
|---|
| 648 | ischema_names['time'] = SybaseTime_mxodbc |
|---|
| 649 | ischema_names['date'] = SybaseDate_mxodbc |
|---|
| 650 | ischema_names['datetime'] = SybaseDateTime_mxodbc |
|---|
| 651 | ischema_names['smalldatetime'] = SybaseDateTime_mxodbc |
|---|
| 652 | |
|---|
| 653 | def is_disconnect(self, e): |
|---|
| 654 | # FIXME: optimize |
|---|
| 655 | #return isinstance(e, self.dbapi.Error) and '[08S01]' in str(e) |
|---|
| 656 | #return True |
|---|
| 657 | return False |
|---|
| 658 | |
|---|
| 659 | def do_execute(self, cursor, statement, parameters, context=None, **kwargs): |
|---|
| 660 | super(SybaseSQLDialect_mxodbc, self).do_execute(cursor, statement, parameters, context=context, **kwargs) |
|---|
| 661 | |
|---|
| 662 | def create_connect_args(self, url): |
|---|
| 663 | '''Return a tuple of *args,**kwargs''' |
|---|
| 664 | # FIXME: handle mx.odbc.Windows proprietary args |
|---|
| 665 | opts = url.translate_connect_args(username='user') |
|---|
| 666 | opts.update(url.query) |
|---|
| 667 | argsDict = {} |
|---|
| 668 | argsDict['user'] = opts['user'] |
|---|
| 669 | argsDict['password'] = opts['password'] |
|---|
| 670 | connArgs = [[opts['dsn']], argsDict] |
|---|
| 671 | return connArgs |
|---|
| 672 | |
|---|
| 673 | |
|---|
| 674 | class SybaseSQLDialect_pyodbc(SybaseSQLDialect): |
|---|
| 675 | execution_ctx_cls = SybaseSQLExecutionContext_pyodbc |
|---|
| 676 | |
|---|
| 677 | def __init__(self, **params): |
|---|
| 678 | super(SybaseSQLDialect_pyodbc, self).__init__(**params) |
|---|
| 679 | self.dbapi_type_map = {'getdate' : SybaseDate_pyodbc()} |
|---|
| 680 | |
|---|
| 681 | def import_dbapi(cls): |
|---|
| 682 | import mypyodbc as module |
|---|
| 683 | return module |
|---|
| 684 | import_dbapi = classmethod(import_dbapi) |
|---|
| 685 | |
|---|
| 686 | colspecs = SybaseSQLDialect.colspecs.copy() |
|---|
| 687 | colspecs[sqltypes.Time] = SybaseTime_pyodbc |
|---|
| 688 | colspecs[sqltypes.Date] = SybaseDate_pyodbc |
|---|
| 689 | colspecs[sqltypes.DateTime] = SybaseDateTime_pyodbc |
|---|
| 690 | |
|---|
| 691 | ischema_names = SybaseSQLDialect.ischema_names.copy() |
|---|
| 692 | ischema_names['time'] = SybaseTime_pyodbc |
|---|
| 693 | ischema_names['date'] = SybaseDate_pyodbc |
|---|
| 694 | ischema_names['datetime'] = SybaseDateTime_pyodbc |
|---|
| 695 | ischema_names['smalldatetime'] = SybaseDateTime_pyodbc |
|---|
| 696 | |
|---|
| 697 | def is_disconnect(self, e): |
|---|
| 698 | # FIXME: optimize |
|---|
| 699 | #return isinstance(e, self.dbapi.Error) and '[08S01]' in str(e) |
|---|
| 700 | #return True |
|---|
| 701 | return False |
|---|
| 702 | |
|---|
| 703 | def do_execute(self, cursor, statement, parameters, context=None, **kwargs): |
|---|
| 704 | super(SybaseSQLDialect_pyodbc, self).do_execute(cursor, statement, parameters, context=context, **kwargs) |
|---|
| 705 | |
|---|
| 706 | def create_connect_args(self, url): |
|---|
| 707 | '''Return a tuple of *args,**kwargs''' |
|---|
| 708 | # FIXME: handle pyodbc proprietary args |
|---|
| 709 | opts = url.translate_connect_args(username='user') |
|---|
| 710 | opts.update(url.query) |
|---|
| 711 | |
|---|
| 712 | self.autocommit = False |
|---|
| 713 | if 'autocommit' in opts: |
|---|
| 714 | self.autocommit = bool(int(opts.pop('autocommit'))) |
|---|
| 715 | |
|---|
| 716 | argsDict = {} |
|---|
| 717 | argsDict['UID'] = opts['user'] |
|---|
| 718 | argsDict['PWD'] = opts['password'] |
|---|
| 719 | argsDict['DSN'] = opts['dsn'] |
|---|
| 720 | connArgs = [[';'.join(["%s=%s"%(key, argsDict[key]) for key in argsDict])], {'autocommit' : self.autocommit}] |
|---|
| 721 | return connArgs |
|---|
| 722 | |
|---|
| 723 | |
|---|
| 724 | dialect_mapping = { |
|---|
| 725 | 'sqlalchemy.databases.mxODBC' : SybaseSQLDialect_mxodbc, |
|---|
| 726 | # 'pyodbc' : SybaseSQLDialect_pyodbc, |
|---|
| 727 | } |
|---|
| 728 | |
|---|
| 729 | |
|---|
| 730 | class SybaseSQLCompiler(compiler.DefaultCompiler): |
|---|
| 731 | operators = compiler.DefaultCompiler.operators.copy() |
|---|
| 732 | operators.update({ |
|---|
| 733 | sql_operators.mod: lambda x, y: "MOD(%s, %s)" % (x, y), |
|---|
| 734 | }) |
|---|
| 735 | |
|---|
| 736 | extract_map = compiler.DefaultCompiler.extract_map.copy() |
|---|
| 737 | extract_map.update ({ |
|---|
| 738 | 'doy': 'dayofyear', |
|---|
| 739 | 'dow': 'weekday', |
|---|
| 740 | 'milliseconds': 'millisecond' |
|---|
| 741 | }) |
|---|
| 742 | |
|---|
| 743 | |
|---|
| 744 | def bindparam_string(self, name): |
|---|
| 745 | res = super(SybaseSQLCompiler, self).bindparam_string(name) |
|---|
| 746 | if name.lower().startswith('literal'): |
|---|
| 747 | res = 'STRING(%s)' % res |
|---|
| 748 | return res |
|---|
| 749 | |
|---|
| 750 | def get_select_precolumns(self, select): |
|---|
| 751 | s = select._distinct and "DISTINCT " or "" |
|---|
| 752 | if select._limit: |
|---|
| 753 | #if select._limit == 1: |
|---|
| 754 | #s += "FIRST " |
|---|
| 755 | #else: |
|---|
| 756 | #s += "TOP %s " % (select._limit,) |
|---|
| 757 | s += "TOP %s " % (select._limit,) |
|---|
| 758 | if select._offset: |
|---|
| 759 | if not select._limit: |
|---|
| 760 | # FIXME: sybase doesn't allow an offset without a limit |
|---|
| 761 | # so use a huge value for TOP here |
|---|
| 762 | s += "TOP 1000000 " |
|---|
| 763 | s += "START AT %s " % (select._offset+1,) |
|---|
| 764 | return s |
|---|
| 765 | |
|---|
| 766 | def limit_clause(self, select): |
|---|
| 767 | # Limit in sybase is after the select keyword |
|---|
| 768 | return "" |
|---|
| 769 | |
|---|
| 770 | def visit_binary(self, binary): |
|---|
| 771 | """Move bind parameters to the right-hand side of an operator, where possible.""" |
|---|
| 772 | if isinstance(binary.left, expression._BindParamClause) and binary.operator == operator.eq: |
|---|
| 773 | return self.process(expression._BinaryExpression(binary.right, binary.left, binary.operator)) |
|---|
| 774 | else: |
|---|
| 775 | return super(SybaseSQLCompiler, self).visit_binary(binary) |
|---|
| 776 | |
|---|
| 777 | def label_select_column(self, select, column, asfrom): |
|---|
| 778 | if isinstance(column, expression.Function): |
|---|
| 779 | return column.label(None) |
|---|
| 780 | else: |
|---|
| 781 | return super(SybaseSQLCompiler, self).label_select_column(select, column, asfrom) |
|---|
| 782 | |
|---|
| 783 | function_rewrites = {'current_date': 'getdate', |
|---|
| 784 | } |
|---|
| 785 | def visit_function(self, func): |
|---|
| 786 | func.name = self.function_rewrites.get(func.name, func.name) |
|---|
| 787 | res = super(SybaseSQLCompiler, self).visit_function(func) |
|---|
| 788 | if func.name.lower() == 'getdate': |
|---|
| 789 | # apply CAST operator |
|---|
| 790 | # FIXME: what about _pyodbc ? |
|---|
| 791 | cast = expression._Cast(func, SybaseDate_mxodbc) |
|---|
| 792 | # infinite recursion |
|---|
| 793 | # res = self.visit_cast(cast) |
|---|
| 794 | res = "CAST(%s AS %s)" % (res, self.process(cast.typeclause)) |
|---|
| 795 | return res |
|---|
| 796 | |
|---|
| 797 | def visit_extract(self, extract): |
|---|
| 798 | field = self.extract_map.get(extract.field, extract.field) |
|---|
| 799 | return 'DATEPART("%s", %s)' % (field, self.process(extract.expr)) |
|---|
| 800 | |
|---|
| 801 | def for_update_clause(self, select): |
|---|
| 802 | # "FOR UPDATE" is only allowed on "DECLARE CURSOR" which SQLAlchemy doesn't use |
|---|
| 803 | return '' |
|---|
| 804 | |
|---|
| 805 | def order_by_clause(self, select): |
|---|
| 806 | order_by = self.process(select._order_by_clause) |
|---|
| 807 | |
|---|
| 808 | # SybaseSQL only allows ORDER BY in subqueries if there is a LIMIT |
|---|
| 809 | if order_by and (not self.is_subquery() or select._limit): |
|---|
| 810 | return " ORDER BY " + order_by |
|---|
| 811 | else: |
|---|
| 812 | return "" |
|---|
| 813 | |
|---|
| 814 | |
|---|
| 815 | class SybaseSQLSchemaGenerator(compiler.SchemaGenerator): |
|---|
| 816 | def get_column_specification(self, column, **kwargs): |
|---|
| 817 | |
|---|
| 818 | colspec = self.preparer.format_column(column) |
|---|
| 819 | |
|---|
| 820 | if (not getattr(column.table, 'has_sequence', False)) and column.primary_key and \ |
|---|
| 821 | column.autoincrement and isinstance(column.type, sqltypes.Integer): |
|---|
| 822 | if column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional): |
|---|
| 823 | column.sequence = schema.Sequence(column.name + '_seq') |
|---|
| 824 | |
|---|
| 825 | if hasattr(column, 'sequence'): |
|---|
| 826 | column.table.has_sequence = column |
|---|
| 827 | #colspec += " numeric(30,0) IDENTITY" |
|---|
| 828 | colspec += " Integer IDENTITY" |
|---|
| 829 | else: |
|---|
| 830 | colspec += " " + column.type.dialect_impl(self.dialect).get_col_spec() |
|---|
| 831 | |
|---|
| 832 | if not column.nullable: |
|---|
| 833 | colspec += " NOT NULL" |
|---|
| 834 | |
|---|
| 835 | default = self.get_column_default_string(column) |
|---|
| 836 | if default is not None: |
|---|
| 837 | colspec += " DEFAULT " + default |
|---|
| 838 | |
|---|
| 839 | return colspec |
|---|
| 840 | |
|---|
| 841 | |
|---|
| 842 | class SybaseSQLSchemaDropper(compiler.SchemaDropper): |
|---|
| 843 | def visit_index(self, index): |
|---|
| 844 | self.append("\nDROP INDEX %s.%s" % ( |
|---|
| 845 | self.preparer.quote_identifier(index.table.name), |
|---|
| 846 | self.preparer.quote(self._validate_identifier(index.name, False), index.quote) |
|---|
| 847 | )) |
|---|
| 848 | self.execute() |
|---|
| 849 | |
|---|
| 850 | |
|---|
| 851 | class SybaseSQLDefaultRunner(base.DefaultRunner): |
|---|
| 852 | pass |
|---|
| 853 | |
|---|
| 854 | |
|---|
| 855 | class SybaseSQLIdentifierPreparer(compiler.IdentifierPreparer): |
|---|
| 856 | reserved_words = RESERVED_WORDS |
|---|
| 857 | |
|---|
| 858 | def __init__(self, dialect): |
|---|
| 859 | super(SybaseSQLIdentifierPreparer, self).__init__(dialect) |
|---|
| 860 | |
|---|
| 861 | def _escape_identifier(self, value): |
|---|
| 862 | #TODO: determin SybaseSQL's escapeing rules |
|---|
| 863 | return value |
|---|
| 864 | |
|---|
| 865 | def _fold_identifier_case(self, value): |
|---|
| 866 | #TODO: determin SybaseSQL's case folding rules |
|---|
| 867 | return value |
|---|
| 868 | |
|---|
| 869 | |
|---|
| 870 | dialect = SybaseSQLDialect |
|---|
| 871 | dialect.statement_compiler = SybaseSQLCompiler |
|---|
| 872 | dialect.schemagenerator = SybaseSQLSchemaGenerator |
|---|
| 873 | dialect.schemadropper = SybaseSQLSchemaDropper |
|---|
| 874 | dialect.preparer = SybaseSQLIdentifierPreparer |
|---|
| 875 | dialect.defaultrunner = SybaseSQLDefaultRunner |
|---|