mirror of
https://github.com/djohnlewis/stackdump
synced 2025-01-23 07:01:41 +00:00
181 lines
6.3 KiB
Python
181 lines
6.3 KiB
Python
|
from itertools import count
|
||
|
from types import *
|
||
|
from converters import sqlrepr
|
||
|
|
||
|
creationOrder = count()
|
||
|
|
||
|
class SODatabaseIndex(object):
|
||
|
|
||
|
def __init__(self,
|
||
|
soClass,
|
||
|
name,
|
||
|
columns,
|
||
|
creationOrder,
|
||
|
unique=False):
|
||
|
self.soClass = soClass
|
||
|
self.name = name
|
||
|
self.descriptions = self.convertColumns(columns)
|
||
|
self.creationOrder = creationOrder
|
||
|
self.unique = unique
|
||
|
|
||
|
def get(self, *args, **kw):
|
||
|
if not self.unique:
|
||
|
raise AttributeError, (
|
||
|
"'%s' object has no attribute 'get' (index is not unique)" % self.name)
|
||
|
connection = kw.pop('connection', None)
|
||
|
if args and kw:
|
||
|
raise TypeError, "You cannot mix named and unnamed arguments"
|
||
|
columns = [d['column'] for d in self.descriptions
|
||
|
if 'column' in d]
|
||
|
if kw and len(kw) != len(columns) or args and len(args) != len(columns):
|
||
|
raise TypeError, ("get() takes exactly %d argument and an optional "
|
||
|
"named argument 'connection' (%d given)" % (
|
||
|
len(columns), len(args)+len(kw)))
|
||
|
if args:
|
||
|
kw = {}
|
||
|
for i in range(len(args)):
|
||
|
if columns[i].foreignName is not None:
|
||
|
kw[columns[i].foreignName] = args[i]
|
||
|
else:
|
||
|
kw[columns[i].name] = args[i]
|
||
|
return self.soClass.selectBy(connection=connection, **kw).getOne()
|
||
|
|
||
|
def convertColumns(self, columns):
|
||
|
"""
|
||
|
Converts all the columns to dictionary descriptors;
|
||
|
dereferences string column names.
|
||
|
"""
|
||
|
new = []
|
||
|
for desc in columns:
|
||
|
if not isinstance(desc, dict):
|
||
|
desc = {'column': desc}
|
||
|
if 'expression' in desc:
|
||
|
assert 'column' not in desc, (
|
||
|
'You cannot provide both an expression and a column '
|
||
|
'(for %s in index %s in %s)' %
|
||
|
(desc, self.name, self.soClass))
|
||
|
assert 'length' not in desc, (
|
||
|
'length does not apply to expressions (for %s in '
|
||
|
'index %s in %s)' %
|
||
|
(desc, self.name, self.soClass))
|
||
|
new.append(desc)
|
||
|
continue
|
||
|
columnName = desc['column']
|
||
|
if not isinstance(columnName, str):
|
||
|
columnName = columnName.name
|
||
|
colDict = self.soClass.sqlmeta.columns
|
||
|
if columnName not in colDict:
|
||
|
for possible in colDict.values():
|
||
|
if possible.origName == columnName:
|
||
|
column = possible
|
||
|
break
|
||
|
else:
|
||
|
# None found
|
||
|
raise ValueError, "The column by the name %r was not found in the class %r" % (columnName, self.soClass)
|
||
|
else:
|
||
|
column = colDict[columnName]
|
||
|
desc['column'] = column
|
||
|
new.append(desc)
|
||
|
return new
|
||
|
|
||
|
def getExpression(self, desc, db):
|
||
|
if isinstance(desc['expression'], str):
|
||
|
return desc['expression']
|
||
|
else:
|
||
|
return sqlrepr(desc['expression'], db)
|
||
|
|
||
|
def sqliteCreateIndexSQL(self, soClass):
|
||
|
if self.unique:
|
||
|
uniqueOrIndex = 'UNIQUE INDEX'
|
||
|
else:
|
||
|
uniqueOrIndex = 'INDEX'
|
||
|
spec = []
|
||
|
for desc in self.descriptions:
|
||
|
if 'expression' in desc:
|
||
|
spec.append(self.getExpression(desc, 'sqlite'))
|
||
|
else:
|
||
|
spec.append(desc['column'].dbName)
|
||
|
ret = 'CREATE %s %s_%s ON %s (%s)' % \
|
||
|
(uniqueOrIndex,
|
||
|
self.soClass.sqlmeta.table,
|
||
|
self.name,
|
||
|
self.soClass.sqlmeta.table,
|
||
|
', '.join(spec))
|
||
|
return ret
|
||
|
|
||
|
postgresCreateIndexSQL = maxdbCreateIndexSQL = mssqlCreateIndexSQL = sybaseCreateIndexSQL = firebirdCreateIndexSQL = sqliteCreateIndexSQL
|
||
|
def mysqlCreateIndexSQL(self, soClass):
|
||
|
if self.unique:
|
||
|
uniqueOrIndex = 'UNIQUE'
|
||
|
else:
|
||
|
uniqueOrIndex = 'INDEX'
|
||
|
spec = []
|
||
|
for desc in self.descriptions:
|
||
|
if 'expression' in desc:
|
||
|
spec.append(self.getExpression(desc, 'mysql'))
|
||
|
elif 'length' in desc:
|
||
|
spec.append('%s(%d)' % (desc['column'].dbName, desc['length']))
|
||
|
else:
|
||
|
spec.append(desc['column'].dbName)
|
||
|
|
||
|
return 'ALTER TABLE %s ADD %s %s (%s)' % \
|
||
|
(soClass.sqlmeta.table, uniqueOrIndex,
|
||
|
self.name,
|
||
|
', '.join(spec))
|
||
|
|
||
|
|
||
|
class DatabaseIndex(object):
|
||
|
"""
|
||
|
This takes a variable number of parameters, each of which is a
|
||
|
column for indexing. Each column may be a column object or the
|
||
|
string name of the column (*not* the database name). You may also
|
||
|
use dictionaries, to further customize the indexing of the column.
|
||
|
The dictionary may have certain keys:
|
||
|
|
||
|
'column':
|
||
|
The column object or string identifier.
|
||
|
'length':
|
||
|
MySQL will only index the first N characters if this is
|
||
|
given. For other databases this is ignored.
|
||
|
'expression':
|
||
|
You can create an index based on an expression, e.g.,
|
||
|
'lower(column)'. This can either be a string or a sqlbuilder
|
||
|
expression.
|
||
|
|
||
|
Further keys may be added to the column specs in the future.
|
||
|
|
||
|
The class also take the keyword argument `unique`; if true then
|
||
|
a UNIQUE index is created.
|
||
|
"""
|
||
|
|
||
|
baseClass = SODatabaseIndex
|
||
|
|
||
|
def __init__(self, *columns, **kw):
|
||
|
kw['columns'] = columns
|
||
|
self.kw = kw
|
||
|
self.creationOrder = creationOrder.next()
|
||
|
|
||
|
def setName(self, value):
|
||
|
assert self.kw.get('name') is None, "You cannot change a name after it has already been set (from %s to %s)" % (self.kw['name'], value)
|
||
|
self.kw['name'] = value
|
||
|
|
||
|
def _get_name(self):
|
||
|
return self.kw['name']
|
||
|
|
||
|
def _set_name(self, value):
|
||
|
self.setName(value)
|
||
|
|
||
|
name = property(_get_name, _set_name)
|
||
|
|
||
|
def withClass(self, soClass):
|
||
|
return self.baseClass(soClass=soClass,
|
||
|
creationOrder=self.creationOrder, **self.kw)
|
||
|
|
||
|
def __repr__(self):
|
||
|
return '<%s %s %s>' % (
|
||
|
self.__class__.__name__,
|
||
|
hex(abs(id(self)))[2:],
|
||
|
self.kw)
|
||
|
|
||
|
__all__ = ['DatabaseIndex']
|