"""Support for the MaxDB database.
-TODO: module docs!
+TODO: More module docs! MaxDB support is currently experimental.
Overview
--------
Only the native Python DB-API is currently supported. ODBC driver support
is a future enhancement.
+Connecting
+----------
+
+The username is case-sensitive. If you usually connect to the
+database with sqlcli and other tools in lower case, you likely need to
+use upper case for DB-API.
+
Implementation Notes
--------------------
Also check the DatabaseNotes page on the wiki for detailed information.
+With the 7.6.00.37 driver and Python 2.5, it seems that all DB-API
+generated exceptions are broken and can cause Python to crash.
+
For 'somecol.in_([])' to work, the IN operator's generation must be changed
to cast 'NULL' to a numeric, i.e. NUM(NULL). The DB-API doesn't accept a
bind parameter there, so that particular generation must inline the NULL value,
__all__ = [
'MaxString', 'MaxUnicode', 'MaxChar', 'MaxText', 'MaxInteger',
- 'MaxSmallInteger', 'MaxNumeric', 'MaxFloat', 'MaxTimestamp',
- 'MaxDate', 'MaxTime', 'MaxBoolean', 'MaxBlob',
+ 'MaxSmallInteger', 'MaxNumeric', 'MaxFloat', 'MaxTimestamp',
+ 'MaxDate', 'MaxTime', 'MaxBoolean', 'MaxBlob',
]
class MaxFloat(sqltypes.Float):
"""The FLOAT data type."""
-
+
def get_col_spec(self):
if self.precision is None:
return 'FLOAT'
"datetimeformat '%s' is not supported." % (
dialect.datetimeformat,))
return process
-
+
class MaxDate(sqltypes.Date):
def get_col_spec(self):
sqltypes.Unicode: MaxUnicode,
}
-ischema_names = {
+ischema_names = {
'boolean': MaxBoolean,
'char': MaxChar,
'character': MaxChar,
self._last_inserted_ids = \
[None] * len(table.primary_key.columns)
self._last_inserted_ids[index] = id
-
+
super(MaxDBExecutionContext, self).post_exec()
def get_result_proxy(self):
class MaxDBCachedColumnRow(engine_base.RowProxy):
"""A RowProxy that only runs result_processors once per column."""
-
+
def __init__(self, parent, row):
super(MaxDBCachedColumnRow, self).__init__(parent, row)
self.columns = {}
'FROM FOREIGNKEYCOLUMNS '
'WHERE TABLENAME=? AND SCHEMANAME=%s '
'ORDER BY FKEYNAME ')
-
+
params = [denormalize(table.name)]
if not table.schema:
st = st % 'CURRENT_SCHEMA'
raise exceptions.NoSuchTableError(table.fullname)
include_columns = util.Set(include_columns or [])
-
+
for row in rows:
(name, mode, col_type, encoding, length, scale,
nullable, constant_def, func_def) = row
name = normalize(name)
-
+
if include_columns and name not in include_columns:
continue
"'%s'" % constant_def.replace("'", "''")))
table.append_column(schema.Column(name, type_instance, **col_kw))
-
+
fk_sets = itertools.groupby(connection.execute(fk, params),
lambda row: row.FKEYNAME)
for fkeyname, fkey in fk_sets:
referants.append('.'.join(
[quote(normalize(row[c]))
for c in ('REFTABLENAME', 'REFCOLUMNNAME')]))
-
+
constraint_kw = {'name': fkeyname.lower()}
if fkey[0].RULE is not None:
rule = fkey[0].RULE
'CURRENT_SCHEMA', 'DATE', 'FALSE', 'SYSDBA', 'TIME', 'TIMESTAMP',
'TIMEZONE', 'TRANSACTION', 'TRUE', 'USER', 'UID', 'USERGROUP',
'UTCDATE', 'UTCDIFF'])
-
+
def default_from(self):
return ' FROM DUAL'
def visit_column(self, column):
self.column = column
self.count += 1
-
+
def _find_labeled_columns(self, columns, use_labels=False):
labels = {}
for column in columns:
labels[unicode(snagger.column)] = column.name
elif use_labels:
labels[unicode(snagger.column)] = column._label
-
+
return labels
-
+
def order_by_clause(self, select):
order_by = self.process(select._order_by_clause)
if order_by and getattr(select, '_distinct', False):
labels = self._find_labeled_columns(select.inner_columns,
select.use_labels)
- if labels:
+ if labels:
for needs_alias in labels.keys():
r = re.compile(r'(^| )(%s)(,| |$)' %
re.escape(needs_alias))
if self.is_subquery(select) and select._limit:
if select._offset:
raise exceptions.InvalidRequestError(
- 'MaxDB does not support LIMIT with an offset.')
+ 'MaxDB does not support LIMIT with an offset.')
sql += 'TOP %s ' % select._limit
return sql
'MaxDB does not support LIMIT with an offset.')
else:
return ' \n LIMIT %s' % (select._limit,)
-
+
def visit_insert(self, insert):
self.isinsert = True
self._safeserial = True
for value in (insert.parameters or {}).itervalues():
if isinstance(value, sql_expr._Function):
self._safeserial = False
- break
+ break
return ''.join(('INSERT INTO ',
self.preparer.format_table(insert.table),
class MaxDBIdentifierPreparer(compiler.IdentifierPreparer):
reserved_words = util.Set([
- 'abs', 'absolute', 'acos', 'adddate', 'addtime', 'all', 'alpha',
- 'alter', 'any', 'ascii', 'asin', 'atan', 'atan2', 'avg', 'binary',
- 'bit', 'boolean', 'byte', 'case', 'ceil', 'ceiling', 'char',
- 'character', 'check', 'chr', 'column', 'concat', 'constraint', 'cos',
- 'cosh', 'cot', 'count', 'cross', 'curdate', 'current', 'curtime',
- 'database', 'date', 'datediff', 'day', 'dayname', 'dayofmonth',
- 'dayofweek', 'dayofyear', 'dec', 'decimal', 'decode', 'default',
- 'degrees', 'delete', 'digits', 'distinct', 'double', 'except',
- 'exists', 'exp', 'expand', 'first', 'fixed', 'float', 'floor', 'for',
- 'from', 'full', 'get_objectname', 'get_schema', 'graphic', 'greatest',
- 'group', 'having', 'hex', 'hextoraw', 'hour', 'ifnull', 'ignore',
- 'index', 'initcap', 'inner', 'insert', 'int', 'integer', 'internal',
- 'intersect', 'into', 'join', 'key', 'last', 'lcase', 'least', 'left',
- 'length', 'lfill', 'list', 'ln', 'locate', 'log', 'log10', 'long',
- 'longfile', 'lower', 'lpad', 'ltrim', 'makedate', 'maketime',
- 'mapchar', 'max', 'mbcs', 'microsecond', 'min', 'minute', 'mod',
- 'month', 'monthname', 'natural', 'nchar', 'next', 'no', 'noround',
- 'not', 'now', 'null', 'num', 'numeric', 'object', 'of', 'on',
- 'order', 'packed', 'pi', 'power', 'prev', 'primary', 'radians',
- 'real', 'reject', 'relative', 'replace', 'rfill', 'right', 'round',
- 'rowid', 'rowno', 'rpad', 'rtrim', 'second', 'select', 'selupd',
- 'serial', 'set', 'show', 'sign', 'sin', 'sinh', 'smallint', 'some',
- 'soundex', 'space', 'sqrt', 'stamp', 'statistics', 'stddev',
- 'subdate', 'substr', 'substring', 'subtime', 'sum', 'sysdba',
- 'table', 'tan', 'tanh', 'time', 'timediff', 'timestamp', 'timezone',
- 'to', 'toidentifier', 'transaction', 'translate', 'trim', 'trunc',
- 'truncate', 'ucase', 'uid', 'unicode', 'union', 'update', 'upper',
- 'user', 'usergroup', 'using', 'utcdate', 'utcdiff', 'value', 'values',
- 'varchar', 'vargraphic', 'variance', 'week', 'weekofyear', 'when',
+ 'abs', 'absolute', 'acos', 'adddate', 'addtime', 'all', 'alpha',
+ 'alter', 'any', 'ascii', 'asin', 'atan', 'atan2', 'avg', 'binary',
+ 'bit', 'boolean', 'byte', 'case', 'ceil', 'ceiling', 'char',
+ 'character', 'check', 'chr', 'column', 'concat', 'constraint', 'cos',
+ 'cosh', 'cot', 'count', 'cross', 'curdate', 'current', 'curtime',
+ 'database', 'date', 'datediff', 'day', 'dayname', 'dayofmonth',
+ 'dayofweek', 'dayofyear', 'dec', 'decimal', 'decode', 'default',
+ 'degrees', 'delete', 'digits', 'distinct', 'double', 'except',
+ 'exists', 'exp', 'expand', 'first', 'fixed', 'float', 'floor', 'for',
+ 'from', 'full', 'get_objectname', 'get_schema', 'graphic', 'greatest',
+ 'group', 'having', 'hex', 'hextoraw', 'hour', 'ifnull', 'ignore',
+ 'index', 'initcap', 'inner', 'insert', 'int', 'integer', 'internal',
+ 'intersect', 'into', 'join', 'key', 'last', 'lcase', 'least', 'left',
+ 'length', 'lfill', 'list', 'ln', 'locate', 'log', 'log10', 'long',
+ 'longfile', 'lower', 'lpad', 'ltrim', 'makedate', 'maketime',
+ 'mapchar', 'max', 'mbcs', 'microsecond', 'min', 'minute', 'mod',
+ 'month', 'monthname', 'natural', 'nchar', 'next', 'no', 'noround',
+ 'not', 'now', 'null', 'num', 'numeric', 'object', 'of', 'on',
+ 'order', 'packed', 'pi', 'power', 'prev', 'primary', 'radians',
+ 'real', 'reject', 'relative', 'replace', 'rfill', 'right', 'round',
+ 'rowid', 'rowno', 'rpad', 'rtrim', 'second', 'select', 'selupd',
+ 'serial', 'set', 'show', 'sign', 'sin', 'sinh', 'smallint', 'some',
+ 'soundex', 'space', 'sqrt', 'stamp', 'statistics', 'stddev',
+ 'subdate', 'substr', 'substring', 'subtime', 'sum', 'sysdba',
+ 'table', 'tan', 'tanh', 'time', 'timediff', 'timestamp', 'timezone',
+ 'to', 'toidentifier', 'transaction', 'translate', 'trim', 'trunc',
+ 'truncate', 'ucase', 'uid', 'unicode', 'union', 'update', 'upper',
+ 'user', 'usergroup', 'using', 'utcdate', 'utcdiff', 'value', 'values',
+ 'varchar', 'vargraphic', 'variance', 'week', 'weekofyear', 'when',
'where', 'with', 'year', 'zoned' ])
def _normalize_name(self, name):
if not self._requires_quotes(lc_name):
return lc_name
return name
-
+
def _denormalize_name(self, name):
if name is None:
return None
colspec.append('DEFAULT SERIAL')
except IndexError:
pass
-
return ' '.join(colspec)
def get_column_default_string(self, column):
maxdb_minvalue
maxdb_maxvalue
With an integer value, sets the corresponding sequence option.
-
+
maxdb_no_minvalue
maxdb_no_maxvalue
Defaults to False. If true, sets the corresponding sequence option.
maxdb_cache
With an integer value, sets the CACHE option.
-
+
maxdb_no_cache
Defaults to False. If true, sets NOCACHE.
"""
-
+
if (not sequence.optional and
(not self.checkfirst or
not self.dialect.has_sequence(self.connection, sequence.name))):
if sequence.start is not None:
ddl.extend(('START WITH', str(sequence.start)))
-
+
opts = dict([(pair[0][6:].lower(), pair[1])
for pair in sequence.kwargs.items()
if pair[0].startswith('maxdb_')])
dialect.statement_compiler = MaxDBCompiler
dialect.schemagenerator = MaxDBSchemaGenerator
dialect.schemadropper = MaxDBSchemaDropper
-dialect.defaultrunner = MaxDBDefaultRunner
-
+dialect.defaultrunner = MaxDBDefaultRunner