0001"""
0002Col -- SQLObject columns
0003
0004Note that each column object is named BlahBlahCol, and these are used
0005in class definitions. But there's also a corresponding SOBlahBlahCol
0006object, which is used in SQLObject *classes*.
0007
0008An explanation: when a SQLObject subclass is created, the metaclass
0009looks through your class definition for any subclasses of Col. It
0010collects them together, and indexes them to do all the database stuff
0011you like, like the magic attributes and whatnot. It then asks the Col
0012object to create an SOCol object (usually a subclass, actually). The
0013SOCol object contains all the interesting logic, as well as a record
0014of the attribute name you used and the class it is bound to (set by
0015the metaclass).
0016
0017So, in summary: Col objects are what you define, but SOCol objects
0018are what gets used.
0019"""
0020
0021from array import array
0022from itertools import count
0023import re, time
0024try:
0025 import cPickle as pickle
0026except ImportError:
0027 import pickle
0028import weakref
0029from formencode import compound, validators
0030from classregistry import findClass
0031
0032
0033import constraints as constrs
0034import sqlbuilder
0035from styles import capword
0036
0037NoDefault = sqlbuilder.NoDefault
0038
0039import datetime
0040datetime_available = True
0041
0042try:
0043 from mx import DateTime
0044except ImportError:
0045 try:
0046 import DateTime
0047 except ImportError:
0048 mxdatetime_available = False
0049 else:
0050 mxdatetime_available = True
0051else:
0052 mxdatetime_available = True
0053
0054DATETIME_IMPLEMENTATION = "datetime"
0055MXDATETIME_IMPLEMENTATION = "mxDateTime"
0056
0057if mxdatetime_available:
0058 if hasattr(DateTime, "Time"):
0059 DateTimeType = type(DateTime.now())
0060 TimeType = type(DateTime.Time())
0061 else:
0062 DateTimeType = type(DateTime.DateTime())
0063 TimeType = type(DateTime.DateTime.Time(DateTime.DateTime()))
0064
0065default_datetime_implementation = DATETIME_IMPLEMENTATION
0066
0067__all__ = ["datetime_available", "mxdatetime_available",
0068 "default_datetime_implementation", "DATETIME_IMPLEMENTATION"]
0069
0070if mxdatetime_available:
0071 __all__.append("MXDATETIME_IMPLEMENTATION")
0072
0073
0074creationOrder = count()
0075
0076
0077
0078
0079
0080
0081
0082class SOCol(object):
0083
0084 def __init__(self,
0085 name,
0086 soClass,
0087 creationOrder,
0088 dbName=None,
0089 default=NoDefault,
0090 defaultSQL=None,
0091 foreignKey=None,
0092 alternateID=False,
0093 alternateMethodName=None,
0094 constraints=None,
0095 notNull=NoDefault,
0096 notNone=NoDefault,
0097 unique=NoDefault,
0098 sqlType=None,
0099 columnDef=None,
0100 validator=None,
0101 validator2=None,
0102 immutable=False,
0103 cascade=None,
0104 lazy=False,
0105 noCache=False,
0106 forceDBName=False,
0107 title=None,
0108 tags=[],
0109 origName=None,
0110 dbEncoding=None,
0111 extra_vars=None):
0112
0113 super(SOCol, self).__init__()
0114
0115
0116
0117
0118
0119
0120 if not forceDBName:
0121 assert sqlbuilder.sqlIdentifier(name), 'Name must be SQL-safe (letters, numbers, underscores): %s (or use forceDBName=True)' % repr(name)
0123 assert name != 'id', 'The column name "id" is reserved for SQLObject use (and is implicitly created).'
0124 assert name, "You must provide a name for all columns"
0125
0126 self.columnDef = columnDef
0127 self.creationOrder = creationOrder
0128
0129 self.immutable = immutable
0130
0131
0132
0133
0134
0135
0136 if isinstance(cascade, str):
0137 assert cascade == 'null', (
0138 "The only string value allowed for cascade is 'null' (you gave: %r)" % cascade)
0139 self.cascade = cascade
0140
0141 if not isinstance(constraints, (list, tuple)):
0142 constraints = [constraints]
0143 self.constraints = self.autoConstraints() + constraints
0144
0145 self.notNone = False
0146 if notNull is not NoDefault:
0147 self.notNone = notNull
0148 assert notNone is NoDefault or (not notNone) == (not notNull), "The notNull and notNone arguments are aliases, and must not conflict. You gave notNull=%r, notNone=%r" % (notNull, notNone)
0151 elif notNone is not NoDefault:
0152 self.notNone = notNone
0153 if self.notNone:
0154 self.constraints = [constrs.notNull] + self.constraints
0155
0156 self.name = name
0157 self.soClass = soClass
0158 self._default = default
0159 self.defaultSQL = defaultSQL
0160 self.customSQLType = sqlType
0161
0162
0163 self.foreignKey = foreignKey
0164 if self.foreignKey:
0165 if origName is not None:
0166 idname = soClass.sqlmeta.style.instanceAttrToIDAttr(origName)
0167 else:
0168 idname = soClass.sqlmeta.style.instanceAttrToIDAttr(name)
0169 if self.name != idname:
0170 self.foreignName = self.name
0171 self.name = idname
0172 else:
0173 self.foreignName = soClass.sqlmeta.style.instanceIDAttrToAttr(self.name)
0174 else:
0175 self.foreignName = None
0176
0177
0178
0179
0180 if dbName is None:
0181 self.dbName = soClass.sqlmeta.style.pythonAttrToDBColumn(self.name)
0182 else:
0183 self.dbName = dbName
0184
0185
0186
0187 self.alternateID = alternateID
0188
0189 if unique is NoDefault:
0190 self.unique = alternateID
0191 else:
0192 self.unique = unique
0193 if self.unique and alternateMethodName is None:
0194 self.alternateMethodName = 'by' + capword(self.name)
0195 else:
0196 self.alternateMethodName = alternateMethodName
0197
0198 _validators = self.createValidators()
0199 if validator: _validators.append(validator)
0200 if validator2: _validators.insert(0, validator2)
0201 _vlen = len(_validators)
0202 if _vlen:
0203 for _validator in _validators:
0204 _validator.soCol=weakref.proxy(self)
0205 if _vlen == 0:
0206 self.validator = None
0207 elif _vlen == 1:
0208 self.validator = _validators[0]
0209 elif _vlen > 1:
0210 self.validator = compound.All.join(_validators[0], *_validators[1:])
0211 self.noCache = noCache
0212 self.lazy = lazy
0213
0214
0215 self.origName = origName or name
0216 self.title = title
0217 self.tags = tags
0218 self.dbEncoding = dbEncoding
0219
0220 if extra_vars:
0221 for name, value in extra_vars.items():
0222 setattr(self, name, value)
0223
0224 def _set_validator(self, value):
0225 self._validator = value
0226 if self._validator:
0227 self.to_python = self._validator.to_python
0228 self.from_python = self._validator.from_python
0229 else:
0230 self.to_python = None
0231 self.from_python = None
0232
0233 def _get_validator(self):
0234 return self._validator
0235
0236 validator = property(_get_validator, _set_validator)
0237
0238 def createValidators(self):
0239 """Create a list of validators for the column."""
0240 return []
0241
0242 def autoConstraints(self):
0243 return []
0244
0245 def _get_default(self):
0246
0247
0248 if self._default is NoDefault:
0249 return NoDefault
0250 elif hasattr(self._default, '__sqlrepr__'):
0251 return self._default
0252 elif callable(self._default):
0253 return self._default()
0254 else:
0255 return self._default
0256 default = property(_get_default, None, None)
0257
0258 def _get_joinName(self):
0259 return self.soClass.sqlmeta.style.instanceIDAttrToAttr(self.name)
0260 joinName = property(_get_joinName, None, None)
0261
0262 def __repr__(self):
0263 r = '<%s %s' % (self.__class__.__name__, self.name)
0264 if self.default is not NoDefault:
0265 r += ' default=%s' % repr(self.default)
0266 if self.foreignKey:
0267 r += ' connected to %s' % self.foreignKey
0268 if self.alternateID:
0269 r += ' alternate ID'
0270 if self.notNone:
0271 r += ' not null'
0272 return r + '>'
0273
0274 def createSQL(self):
0275 return ' '.join([self._sqlType()] + self._extraSQL())
0276
0277 def _extraSQL(self):
0278 result = []
0279 if self.notNone or self.alternateID:
0280 result.append('NOT NULL')
0281 if self.unique or self.alternateID:
0282 result.append('UNIQUE')
0283 if self.defaultSQL is not None:
0284 result.append("DEFAULT %s" % self.defaultSQL)
0285 return result
0286
0287 def _sqlType(self):
0288 if self.customSQLType is None:
0289 raise ValueError, ("Col %s (%s) cannot be used for automatic "
0290 "schema creation (too abstract)" %
0291 (self.name, self.__class__))
0292 else:
0293 return self.customSQLType
0294
0295 def _mysqlType(self):
0296 return self._sqlType()
0297
0298 def _postgresType(self):
0299 return self._sqlType()
0300
0301 def _sqliteType(self):
0302
0303
0304 try:
0305 return self._sqlType()
0306 except ValueError:
0307 return ''
0308
0309 def _sybaseType(self):
0310 return self._sqlType()
0311
0312 def _mssqlType(self):
0313 return self._sqlType()
0314
0315 def _firebirdType(self):
0316 return self._sqlType()
0317
0318 def _maxdbType(self):
0319 return self._sqlType()
0320
0321 def mysqlCreateSQL(self):
0322 return ' '.join([self.dbName, self._mysqlType()] + self._extraSQL())
0323
0324 def postgresCreateSQL(self):
0325 return ' '.join([self.dbName, self._postgresType()] + self._extraSQL())
0326
0327 def sqliteCreateSQL(self):
0328 return ' '.join([self.dbName, self._sqliteType()] + self._extraSQL())
0329
0330 def sybaseCreateSQL(self):
0331 return ' '.join([self.dbName, self._sybaseType()] + self._extraSQL())
0332
0333 def mssqlCreateSQL(self, connection=None):
0334 self.connection = connection
0335 return ' '.join([self.dbName, self._mssqlType()] + self._extraSQL())
0336
0337 def firebirdCreateSQL(self):
0338
0339
0340
0341 if not isinstance(self, SOEnumCol):
0342 return ' '.join([self.dbName, self._firebirdType()] + self._extraSQL())
0343 else:
0344 return ' '.join([self.dbName] + [self._firebirdType()[0]] + self._extraSQL() + [self._firebirdType()[1]])
0345
0346 def maxdbCreateSQL(self):
0347 return ' '.join([self.dbName, self._maxdbType()] + self._extraSQL())
0348
0349 def __get__(self, obj, type=None):
0350 if obj is None:
0351
0352 return self
0353 if obj.sqlmeta._obsolete:
0354 raise RuntimeError('The object <%s %s> is obsolete' % (
0355 obj.__class__.__name__, obj.id))
0356 if obj.sqlmeta.cacheColumns:
0357 columns = obj.sqlmeta._columnCache
0358 if columns is None:
0359 obj.sqlmeta.loadValues()
0360 try:
0361 return columns[name]
0362 except KeyError:
0363 return obj.sqlmeta.loadColumn(self)
0364 else:
0365 return obj.sqlmeta.loadColumn(self)
0366
0367 def __set__(self, obj, value):
0368 if self.immutable:
0369 raise AttributeError("The column %s.%s is immutable" %
0370 (obj.__class__.__name__,
0371 self.name))
0372 obj.sqlmeta.setColumn(self, value)
0373
0374 def __delete__(self, obj):
0375 raise AttributeError("I can't be deleted from %r" % obj)
0376
0377 def getDbEncoding(self, state, default='utf-8'):
0378 if self.dbEncoding:
0379 return self.dbEncoding
0380 dbEncoding = state.soObject.sqlmeta.dbEncoding
0381 if dbEncoding:
0382 return dbEncoding
0383 try:
0384 connection = state.connection or state.soObject._connection
0385 except AttributeError:
0386 dbEncoding = None
0387 else:
0388 dbEncoding = getattr(connection, "dbEncoding", None)
0389 if not dbEncoding:
0390 dbEncoding = default
0391 return dbEncoding
0392
0393
0394class Col(object):
0395
0396 baseClass = SOCol
0397
0398 def __init__(self, name=None, **kw):
0399 super(Col, self).__init__()
0400 self.__dict__['_name'] = name
0401 self.__dict__['_kw'] = kw
0402 self.__dict__['creationOrder'] = creationOrder.next()
0403 self.__dict__['_extra_vars'] = {}
0404
0405 def _set_name(self, value):
0406 assert self._name is None or self._name == value, (
0407 "You cannot change a name after it has already been set "
0408 "(from %s to %s)" % (self.name, value))
0409 self.__dict__['_name'] = value
0410
0411 def _get_name(self):
0412 return self._name
0413
0414 name = property(_get_name, _set_name)
0415
0416 def withClass(self, soClass):
0417 return self.baseClass(soClass=soClass, name=self._name,
0418 creationOrder=self.creationOrder,
0419 columnDef=self,
0420 extra_vars=self._extra_vars,
0421 **self._kw)
0422
0423 def __setattr__(self, var, value):
0424 if var == 'name':
0425 super(Col, self).__setattr__(var, value)
0426 return
0427 self._extra_vars[var] = value
0428
0429 def __repr__(self):
0430 return '<%s %s %s>' % (
0431 self.__class__.__name__, hex(abs(id(self)))[2:],
0432 self._name or '(unnamed)')
0433
0434
0435class SOValidator(validators.Validator):
0436 def getDbEncoding(self, state, default='utf-8'):
0437 try:
0438 return self.dbEncoding
0439 except AttributeError:
0440 return self.soCol.getDbEncoding(state, default=default)
0441
0442
0443class SOStringLikeCol(SOCol):
0444 """A common ancestor for SOStringCol and SOUnicodeCol"""
0445 def __init__(self, **kw):
0446 self.length = kw.pop('length', None)
0447 self.varchar = kw.pop('varchar', 'auto')
0448 self.char_binary = kw.pop('char_binary', None)
0449 if not self.length:
0450 assert self.varchar == 'auto' or not self.varchar, "Without a length strings are treated as TEXT, not varchar"
0452 self.varchar = False
0453 elif self.varchar == 'auto':
0454 self.varchar = True
0455
0456 super(SOStringLikeCol, self).__init__(**kw)
0457
0458 def autoConstraints(self):
0459 constraints = [constrs.isString]
0460 if self.length is not None:
0461 constraints += [constrs.MaxLength(self.length)]
0462 return constraints
0463
0464 def _sqlType(self):
0465 if self.customSQLType is not None:
0466 return self.customSQLType
0467 if not self.length:
0468 return 'TEXT'
0469 elif self.varchar:
0470 return 'VARCHAR(%i)' % self.length
0471 else:
0472 return 'CHAR(%i)' % self.length
0473
0474 def _check_case_sensitive(self, db):
0475 if self.char_binary:
0476 raise ValueError, "%s does not support binary character columns" % db
0477
0478 def _mysqlType(self):
0479 type = self._sqlType()
0480 if self.char_binary:
0481 type += " BINARY"
0482 return type
0483
0484 def _postgresType(self):
0485 self._check_case_sensitive("PostgreSQL")
0486 return super(SOStringLikeCol, self)._postgresType()
0487
0488 def _sqliteType(self):
0489 self._check_case_sensitive("SQLite")
0490 return super(SOStringLikeCol, self)._sqliteType()
0491
0492 def _sybaseType(self):
0493 self._check_case_sensitive("SYBASE")
0494 type = self._sqlType()
0495 if not self.notNone and not self.alternateID:
0496 type += ' NULL'
0497 return type
0498
0499 def _mssqlType(self):
0500 if self.customSQLType is not None:
0501 return self.customSQLType
0502 if not self.length:
0503 if self.connection and self.connection.can_use_max_types():
0504 type = 'VARCHAR(MAX)'
0505 else:
0506 type = 'VARCHAR(4000)'
0507 elif self.varchar:
0508 type = 'VARCHAR(%i)' % self.length
0509 else:
0510 type = 'CHAR(%i)' % self.length
0511 if not self.notNone and not self.alternateID:
0512 type += ' NULL'
0513 return type
0514
0515 def _firebirdType(self):
0516 self._check_case_sensitive("FireBird")
0517 if not self.length:
0518 return 'BLOB SUB_TYPE TEXT'
0519 else:
0520 return self._sqlType()
0521
0522 def _maxdbType(self):
0523 self._check_case_sensitive("SAP DB/MaxDB")
0524 if not self.length:
0525 return 'LONG ASCII'
0526 else:
0527 return self._sqlType()
0528
0529
0530class StringValidator(SOValidator):
0531
0532 def to_python(self, value, state):
0533 if value is None:
0534 return None
0535 try:
0536 connection = state.connection or state.soObject._connection
0537 binaryType = connection._binaryType
0538 except AttributeError:
0539 binaryType = type(None)
0540 dbEncoding = self.getDbEncoding(state, default='ascii')
0541 if isinstance(value, unicode):
0542 return value.encode(dbEncoding)
0543 if self.dataType and isinstance(value, self.dataType):
0544 return value
0545 if isinstance(value, (str, buffer, binaryType, sqlbuilder.SQLExpression)):
0546 return value
0547 if hasattr(value, '__unicode__'):
0548 return unicode(value).encode(dbEncoding)
0549 raise validators.Invalid("expected a str in the StringCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0551
0552 from_python = to_python
0553
0554class SOStringCol(SOStringLikeCol):
0555
0556 def createValidators(self, dataType=None):
0557 return [StringValidator(name=self.name, dataType=dataType)] + super(SOStringCol, self).createValidators()
0559
0560class StringCol(Col):
0561 baseClass = SOStringCol
0562
0563
0564class NQuoted(sqlbuilder.SQLExpression):
0565 def __init__(self, value):
0566 assert isinstance(value, unicode)
0567 self.value = value
0568 def __hash__(self):
0569 return hash(self.value)
0570 def __sqlrepr__(self, db):
0571 assert db == 'mssql'
0572 return "N" + sqlbuilder.sqlrepr(self.value, db)
0573
0574class UnicodeStringValidator(SOValidator):
0575
0576 def to_python(self, value, state):
0577 if value is None:
0578 return None
0579 if isinstance(value, (unicode, sqlbuilder.SQLExpression)):
0580 return value
0581 if isinstance(value, str):
0582 return unicode(value, self.getDbEncoding(state))
0583 if isinstance(value, array):
0584 return unicode(value.tostring(), self.getDbEncoding(state))
0585 if hasattr(value, '__unicode__'):
0586 return unicode(value)
0587 raise validators.Invalid("expected a str or a unicode in the UnicodeCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0589
0590 def from_python(self, value, state):
0591 if value is None:
0592 return None
0593 if isinstance(value, (str, sqlbuilder.SQLExpression)):
0594 return value
0595 if isinstance(value, unicode):
0596 try:
0597 connection = state.connection or state.soObject._connection
0598 except AttributeError:
0599 pass
0600 else:
0601 if connection.dbName == 'mssql':
0602 return NQuoted(value)
0603 return value.encode(self.getDbEncoding(state))
0604 if hasattr(value, '__unicode__'):
0605 return unicode(value).encode(self.getDbEncoding(state))
0606 raise validators.Invalid("expected a str or a unicode in the UnicodeCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0608
0609class SOUnicodeCol(SOStringLikeCol):
0610 def _mssqlType(self):
0611 if self.customSQLType is not None:
0612 return self.customSQLType
0613 return 'N' + super(SOUnicodeCol, self)._mssqlType()
0614
0615 def createValidators(self):
0616 return [UnicodeStringValidator(name=self.name)] + super(SOUnicodeCol, self).createValidators()
0618
0619class UnicodeCol(Col):
0620 baseClass = SOUnicodeCol
0621
0622
0623class IntValidator(SOValidator):
0624
0625 def to_python(self, value, state):
0626 if value is None:
0627 return None
0628 if isinstance(value, (int, long, sqlbuilder.SQLExpression)):
0629 return value
0630 for converter, attr_name in (int, '__int__'), (long, '__long__'):
0631 if hasattr(value, attr_name):
0632 try:
0633 return converter(value)
0634 except:
0635 break
0636 raise validators.Invalid("expected an int in the IntCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0638
0639 from_python = to_python
0640
0641class SOIntCol(SOCol):
0642
0643 def __init__(self, **kw):
0644 self.length = kw.pop('length', None)
0645 self.unsigned = bool(kw.pop('unsigned', None))
0646 self.zerofill = bool(kw.pop('zerofill', None))
0647 SOCol.__init__(self, **kw)
0648
0649 def autoConstraints(self):
0650 return [constrs.isInt]
0651
0652 def createValidators(self):
0653 return [IntValidator(name=self.name)] + super(SOIntCol, self).createValidators()
0655
0656 def addSQLAttrs(self, str):
0657 _ret = str
0658 if str is None or len(str) < 1:
0659 return None
0660
0661 if self.length >= 1:
0662 _ret = "%s(%d)" % (_ret, self.length)
0663 if self.unsigned:
0664 _ret = _ret + " UNSIGNED"
0665 if self.zerofill:
0666 _ret = _ret + " ZEROFILL"
0667 return _ret
0668
0669 def _sqlType(self):
0670 return self.addSQLAttrs("INT")
0671
0672class IntCol(Col):
0673 baseClass = SOIntCol
0674
0675class SOTinyIntCol(SOIntCol):
0676 def _sqlType(self):
0677 return self.addSQLAttrs("TINYINT")
0678
0679class TinyIntCol(Col):
0680 baseClass = SOTinyIntCol
0681
0682class SOSmallIntCol(SOIntCol):
0683 def _sqlType(self):
0684 return self.addSQLAttrs("SMALLINT")
0685
0686class SmallIntCol(Col):
0687 baseClass = SOSmallIntCol
0688
0689class SOMediumIntCol(SOIntCol):
0690 def _sqlType(self):
0691 return self.addSQLAttrs("MEDIUMINT")
0692
0693class MediumIntCol(Col):
0694 baseClass = SOMediumIntCol
0695
0696class SOBigIntCol(SOIntCol):
0697 def _sqlType(self):
0698 return self.addSQLAttrs("BIGINT")
0699
0700class BigIntCol(Col):
0701 baseClass = SOBigIntCol
0702
0703
0704class BoolValidator(SOValidator):
0705
0706 def to_python(self, value, state):
0707 if value is None:
0708 return None
0709 if isinstance(value, (bool, sqlbuilder.SQLExpression)):
0710 return value
0711 if isinstance(value, (int, long)) or hasattr(value, '__nonzero__'):
0712 return bool(value)
0713 raise validators.Invalid("expected a bool or an int in the BoolCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0715
0716 from_python = to_python
0717
0718class SOBoolCol(SOCol):
0719 def autoConstraints(self):
0720 return [constrs.isBool]
0721
0722 def createValidators(self):
0723 return [BoolValidator(name=self.name)] + super(SOBoolCol, self).createValidators()
0725
0726 def _postgresType(self):
0727 return 'BOOL'
0728
0729 def _mysqlType(self):
0730 return "BOOL"
0731
0732 def _sybaseType(self):
0733 return "BIT"
0734
0735 def _mssqlType(self):
0736 return "BIT"
0737
0738 def _firebirdType(self):
0739 return 'INT'
0740
0741 def _maxdbType(self):
0742 return "BOOLEAN"
0743
0744 def _sqliteType(self):
0745 return "BOOLEAN"
0746
0747class BoolCol(Col):
0748 baseClass = SOBoolCol
0749
0750
0751class FloatValidator(SOValidator):
0752
0753 def to_python(self, value, state):
0754 if value is None:
0755 return None
0756 if isinstance(value, (float, int, long, sqlbuilder.SQLExpression)):
0757 return value
0758 for converter, attr_name in (float, '__float__'), (int, '__int__'), (long, '__long__'):
0759 if hasattr(value, attr_name):
0760 try:
0761 return converter(value)
0762 except:
0763 break
0764 raise validators.Invalid("expected a float in the FloatCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
0766
0767 from_python = to_python
0768
0769class SOFloatCol(SOCol):
0770
0771
0772 def autoConstraints(self):
0773 return [constrs.isFloat]
0774
0775 def createValidators(self):
0776 return [FloatValidator(name=self.name)] + super(SOFloatCol, self).createValidators()
0778
0779 def _sqlType(self):
0780 return 'FLOAT'
0781
0782 def _mysqlType(self):
0783 return "DOUBLE PRECISION"
0784
0785class FloatCol(Col):
0786 baseClass = SOFloatCol
0787
0788
0789class SOKeyCol(SOCol):
0790 key_type = {int: "INT", str: "TEXT"}
0791
0792
0793
0794
0795 def __init__(self, **kw):
0796 self.refColumn = kw.pop('refColumn', None)
0797 super(SOKeyCol, self).__init__(**kw)
0798
0799 def _sqlType(self):
0800 return self.key_type[self.soClass.sqlmeta.idType]
0801
0802 def _sybaseType(self):
0803 key_type = {int: "NUMERIC(18,0) NULL", str: "TEXT"}
0804 return key_type[self.soClass.sqlmeta.idType]
0805
0806 def _mssqlType(self):
0807 key_type = {int: "INT NULL", str: "TEXT"}
0808 return key_type[self.soClass.sqlmeta.idType]
0809
0810class KeyCol(Col):
0811
0812 baseClass = SOKeyCol
0813
0814class SOForeignKey(SOKeyCol):
0815
0816 def __init__(self, **kw):
0817 foreignKey = kw['foreignKey']
0818 style = kw['soClass'].sqlmeta.style
0819 if kw.get('name'):
0820 kw['origName'] = kw['name']
0821 kw['name'] = style.instanceAttrToIDAttr(kw['name'])
0822 else:
0823 kw['name'] = style.instanceAttrToIDAttr(style.pythonClassToAttr(foreignKey))
0824 super(SOForeignKey, self).__init__(**kw)
0825
0826 def sqliteCreateSQL(self):
0827 sql = SOKeyCol.sqliteCreateSQL(self)
0828 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0829 tName = other.sqlmeta.table
0830 idName = self.refColumn or other.sqlmeta.idName
0831 if self.cascade is not None:
0832 if self.cascade == 'null':
0833 action = 'ON DELETE SET NULL'
0834 elif self.cascade:
0835 action = 'ON DELETE CASCADE'
0836 else:
0837 action = 'ON DELETE RESTRICT'
0838 else:
0839 action = ''
0840 constraint = ('CONSTRAINT %(colName)s_exists '
0841
0842 'REFERENCES %(tName)s(%(idName)s) '
0843 '%(action)s' %
0844 {'tName': tName,
0845 'colName': self.dbName,
0846 'idName': idName,
0847 'action': action})
0848 sql = ' '.join([sql, constraint])
0849 return sql
0850
0851 def postgresCreateSQL(self):
0852 sql = SOKeyCol.postgresCreateSQL(self)
0853 return sql
0854
0855 def postgresCreateReferenceConstraint(self):
0856 sTName = self.soClass.sqlmeta.table
0857 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0858 tName = other.sqlmeta.table
0859 idName = self.refColumn or other.sqlmeta.idName
0860 if self.cascade is not None:
0861 if self.cascade == 'null':
0862 action = 'ON DELETE SET NULL'
0863 elif self.cascade:
0864 action = 'ON DELETE CASCADE'
0865 else:
0866 action = 'ON DELETE RESTRICT'
0867 else:
0868 action = ''
0869 constraint = ('ALTER TABLE %(sTName)s ADD CONSTRAINT %(colName)s_exists '
0870 'FOREIGN KEY (%(colName)s) '
0871 'REFERENCES %(tName)s (%(idName)s) '
0872 '%(action)s' %
0873 {'tName': tName,
0874 'colName': self.dbName,
0875 'idName': idName,
0876 'action': action,
0877 'sTName': sTName})
0878 return constraint
0879
0880 def mysqlCreateReferenceConstraint(self):
0881 sTName = self.soClass.sqlmeta.table
0882 sTLocalName = sTName.split('.')[-1]
0883 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0884 tName = other.sqlmeta.table
0885 idName = self.refColumn or other.sqlmeta.idName
0886 if self.cascade is not None:
0887 if self.cascade == 'null':
0888 action = 'ON DELETE SET NULL'
0889 elif self.cascade:
0890 action = 'ON DELETE CASCADE'
0891 else:
0892 action = 'ON DELETE RESTRICT'
0893 else:
0894 action = ''
0895 constraint = ('ALTER TABLE %(sTName)s ADD CONSTRAINT %(sTLocalName)s_%(colName)s_exists '
0896 'FOREIGN KEY (%(colName)s) '
0897 'REFERENCES %(tName)s (%(idName)s) '
0898 '%(action)s' %
0899 {'tName': tName,
0900 'colName': self.dbName,
0901 'idName': idName,
0902 'action': action,
0903 'sTName': sTName,
0904 'sTLocalName': sTLocalName})
0905 return constraint
0906
0907 def mysqlCreateSQL(self):
0908 return SOKeyCol.mysqlCreateSQL(self)
0909
0910 def sybaseCreateSQL(self):
0911 sql = SOKeyCol.sybaseCreateSQL(self)
0912 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0913 tName = other.sqlmeta.table
0914 idName = self.refColumn or other.sqlmeta.idName
0915 reference = ('REFERENCES %(tName)s(%(idName)s) ' %
0916 {'tName':tName,
0917 'idName':idName})
0918 sql = ' '.join([sql, reference])
0919 return sql
0920
0921 def sybaseCreateReferenceConstraint(self):
0922
0923 return None
0924
0925 def mssqlCreateSQL(self, connection=None):
0926 sql = SOKeyCol.mssqlCreateSQL(self, connection)
0927 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0928 tName = other.sqlmeta.table
0929 idName = self.refColumn or other.sqlmeta.idName
0930 reference = ('REFERENCES %(tName)s(%(idName)s) ' %
0931 {'tName':tName,
0932 'idName':idName})
0933 sql = ' '.join([sql, reference])
0934 return sql
0935
0936 def mssqlCreateReferenceConstraint(self):
0937
0938 return None
0939
0940 def maxdbCreateSQL(self):
0941 other = findClass(self.foreignKey, self.soClass.sqlmeta.registry)
0942 fidName = self.dbName
0943
0944 sql = ' '.join([fidName, self._maxdbType()])
0945 tName = other.sqlmeta.table
0946 idName = self.refColumn or other.sqlmeta.idName
0947 sql=sql + ',' + '\n'
0948 sql=sql + 'FOREIGN KEY (%s) REFERENCES %s(%s)'%(fidName,tName,idName)
0949 return sql
0950
0951 def maxdbCreateReferenceConstraint(self):
0952
0953 return None
0954
0955class ForeignKey(KeyCol):
0956
0957 baseClass = SOForeignKey
0958
0959 def __init__(self, foreignKey=None, **kw):
0960 super(ForeignKey, self).__init__(foreignKey=foreignKey, **kw)
0961
0962
0963class EnumValidator(SOValidator):
0964
0965 def to_python(self, value, state):
0966 if value in self.enumValues:
0967 if isinstance(value, unicode):
0968 dbEncoding = self.getDbEncoding(state)
0969 value = value.encode(dbEncoding)
0970 return value
0971 elif not self.notNone and value is None:
0972 return None
0973 raise validators.Invalid("expected a member of %r in the EnumCol '%s', got %r instead" % (self.enumValues, self.name, value), value, state)
0975
0976 from_python = to_python
0977
0978class SOEnumCol(SOCol):
0979
0980 def __init__(self, **kw):
0981 self.enumValues = kw.pop('enumValues', None)
0982 assert self.enumValues is not None, 'You must provide an enumValues keyword argument'
0984 super(SOEnumCol, self).__init__(**kw)
0985
0986 def autoConstraints(self):
0987 return [constrs.isString, constrs.InList(self.enumValues)]
0988
0989 def createValidators(self):
0990 return [EnumValidator(name=self.name, enumValues=self.enumValues,
0991 notNone=self.notNone)] + super(SOEnumCol, self).createValidators()
0993
0994 def _mysqlType(self):
0995
0996
0997 if None in self.enumValues:
0998 return "ENUM(%s)" % ', '.join([sqlbuilder.sqlrepr(v, 'mysql') for v in self.enumValues if v is not None])
0999 else:
1000 return "ENUM(%s) NOT NULL" % ', '.join([sqlbuilder.sqlrepr(v, 'mysql') for v in self.enumValues])
1001
1002 def _postgresType(self):
1003 length = max(map(self._getlength, self.enumValues))
1004 enumValues = ', '.join([sqlbuilder.sqlrepr(v, 'postgres') for v in self.enumValues])
1005 checkConstraint = "CHECK (%s in (%s))" % (self.dbName, enumValues)
1006 return "VARCHAR(%i) %s" % (length, checkConstraint)
1007
1008 _sqliteType = _postgresType
1009
1010 def _sybaseType(self):
1011 return self._postgresType()
1012
1013 def _mssqlType(self):
1014 return self._postgresType()
1015
1016 def _firebirdType(self):
1017 length = max(map(self._getlength, self.enumValues))
1018 enumValues = ', '.join([sqlbuilder.sqlrepr(v, 'firebird') for v in self.enumValues])
1019 checkConstraint = "CHECK (%s in (%s))" % (self.dbName, enumValues)
1020
1021 return "VARCHAR(%i)" % (length), checkConstraint
1022
1023 def _maxdbType(self):
1024 raise TypeError("Enum type is not supported on MAX DB")
1025
1026 def _getlength(self, obj):
1027 """
1028 None counts as 0; everything else uses len()
1029 """
1030 if obj is None:
1031 return 0
1032 else:
1033 return len(obj)
1034
1035class EnumCol(Col):
1036 baseClass = SOEnumCol
1037
1038
1039class SetValidator(SOValidator):
1040 """
1041 Translates Python tuples into SQL comma-delimited SET strings.
1042 """
1043
1044 def to_python(self, value, state):
1045 if isinstance(value, str):
1046 return tuple(value.split(","))
1047 raise validators.Invalid("expected a string in the SetCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1049
1050 def from_python(self, value, state):
1051 if isinstance(value, basestring):
1052 value = (value,)
1053 try:
1054 return ",".join(value)
1055 except:
1056 raise validators.Invalid("expected a string or a sequence of stringsin the SetCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1058
1059class SOSetCol(SOCol):
1060 def __init__(self, **kw):
1061 self.setValues = kw.pop('setValues', None)
1062 assert self.setValues is not None, 'You must provide a setValues keyword argument'
1064 super(SOSetCol, self).__init__(**kw)
1065
1066 def autoConstraints(self):
1067 return [constrs.isString, constrs.InList(self.setValues)]
1068
1069 def createValidators(self):
1070 return [SetValidator(name=self.name, setValues=self.setValues)] + super(SOSetCol, self).createValidators()
1072
1073 def _mysqlType(self):
1074 return "SET(%s)" % ', '.join([sqlbuilder.sqlrepr(v, 'mysql') for v in self.setValues])
1075
1076class SetCol(Col):
1077 baseClass = SOSetCol
1078
1079
1080class DateTimeValidator(validators.DateValidator):
1081 def to_python(self, value, state):
1082 if value is None:
1083 return None
1084 if isinstance(value, (datetime.datetime, datetime.date, datetime.time, sqlbuilder.SQLExpression)):
1085 return value
1086 if mxdatetime_available:
1087 if isinstance(value, DateTimeType):
1088
1089 if (self.format.find("%H") >= 0) or (self.format.find("%T")) >= 0:
1090 return datetime.datetime(value.year, value.month, value.day,
1091 value.hour, value.minute, int(value.second))
1092 else:
1093 return datetime.date(value.year, value.month, value.day)
1094 elif isinstance(value, TimeType):
1095
1096 if self.format.find("%d") >= 0:
1097 return datetime.timedelta(seconds=value.seconds)
1098 else:
1099 return datetime.time(value.hour, value.minute, int(value.second))
1100 try:
1101 stime = time.strptime(value, self.format)
1102 except:
1103 raise validators.Invalid("expected a date/time string of the '%s' format in the DateTimeCol '%s', got %s %r instead" % (self.format, self.name, type(value), value), value, state)
1105 return datetime.datetime(*stime[:6])
1106
1107 def from_python(self, value, state):
1108 if value is None:
1109 return None
1110 if isinstance(value, (datetime.datetime, datetime.date, datetime.time, sqlbuilder.SQLExpression)):
1111 return value
1112 if hasattr(value, "strftime"):
1113 return value.strftime(self.format)
1114 raise validators.Invalid("expected a datetime in the DateTimeCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1116
1117if mxdatetime_available:
1118 class MXDateTimeValidator(validators.DateValidator):
1119 def to_python(self, value, state):
1120 if value is None:
1121 return None
1122 if isinstance(value, (DateTimeType, TimeType, sqlbuilder.SQLExpression)):
1123 return value
1124 if isinstance(value, datetime.datetime):
1125 return DateTime.DateTime(value.year, value.month, value.day,
1126 value.hour, value.minute, value.second)
1127 elif isinstance(value, datetime.date):
1128 return DateTime.Date(value.year, value.month, value.day)
1129 elif isinstance(value, datetime.time):
1130 return DateTime.Time(value.hour, value.minute, value.second)
1131 try:
1132 stime = time.strptime(value, self.format)
1133 except:
1134 raise validators.Invalid("expected a date/time string of the '%s' format in the DateTimeCol '%s', got %s %r instead" % (self.format, self.name, type(value), value), value, state)
1136 return DateTime.mktime(stime)
1137
1138 def from_python(self, value, state):
1139 if value is None:
1140 return None
1141 if isinstance(value, (DateTimeType, TimeType, sqlbuilder.SQLExpression)):
1142 return value
1143 if hasattr(value, "strftime"):
1144 return value.strftime(self.format)
1145 raise validators.Invalid("expected a mxDateTime in the DateTimeCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1147
1148class SODateTimeCol(SOCol):
1149 datetimeFormat = '%Y-%m-%d %H:%M:%S'
1150
1151 def __init__(self, **kw):
1152 datetimeFormat = kw.pop('datetimeFormat', None)
1153 if datetimeFormat:
1154 self.datetimeFormat = datetimeFormat
1155 super(SODateTimeCol, self).__init__(**kw)
1156
1157 def createValidators(self):
1158 _validators = super(SODateTimeCol, self).createValidators()
1159 if default_datetime_implementation == DATETIME_IMPLEMENTATION:
1160 validatorClass = DateTimeValidator
1161 elif default_datetime_implementation == MXDATETIME_IMPLEMENTATION:
1162 validatorClass = MXDateTimeValidator
1163 if default_datetime_implementation:
1164 _validators.insert(0, validatorClass(name=self.name, format=self.datetimeFormat))
1165 return _validators
1166
1167 def _mysqlType(self):
1168 return 'DATETIME'
1169
1170 def _postgresType(self):
1171 return 'TIMESTAMP'
1172
1173 def _sybaseType(self):
1174 return 'DATETIME'
1175
1176 def _mssqlType(self):
1177 return 'DATETIME'
1178
1179 def _sqliteType(self):
1180 return 'TIMESTAMP'
1181
1182 def _firebirdType(self):
1183 return 'TIMESTAMP'
1184
1185 def _maxdbType(self):
1186 return 'TIMESTAMP'
1187
1188class DateTimeCol(Col):
1189 baseClass = SODateTimeCol
1190 @staticmethod
1191 def now():
1192 if default_datetime_implementation == DATETIME_IMPLEMENTATION:
1193 return datetime.datetime.now()
1194 elif default_datetime_implementation == MXDATETIME_IMPLEMENTATION:
1195 return DateTime.now()
1196 else:
1197 assert 0, ("No datetime implementation available "
1198 "(DATETIME_IMPLEMENTATION=%r)"
1199 % DATETIME_IMPLEMENTATION)
1200
1201
1202class DateValidator(DateTimeValidator):
1203 def to_python(self, value, state):
1204 if isinstance(value, datetime.datetime):
1205 value = value.date()
1206 if isinstance(value, (datetime.date, sqlbuilder.SQLExpression)):
1207 return value
1208 value = super(DateValidator, self).to_python(value, state)
1209 if isinstance(value, datetime.datetime):
1210 value = value.date()
1211 return value
1212
1213 from_python = to_python
1214
1215class SODateCol(SOCol):
1216 dateFormat = '%Y-%m-%d'
1217
1218 def __init__(self, **kw):
1219 dateFormat = kw.pop('dateFormat', None)
1220 if dateFormat: self.dateFormat = dateFormat
1221 super(SODateCol, self).__init__(**kw)
1222
1223 def createValidators(self):
1224 """Create a validator for the column. Can be overriden in descendants."""
1225 _validators = super(SODateCol, self).createValidators()
1226 if default_datetime_implementation == DATETIME_IMPLEMENTATION:
1227 validatorClass = DateValidator
1228 elif default_datetime_implementation == MXDATETIME_IMPLEMENTATION:
1229 validatorClass = MXDateTimeValidator
1230 if default_datetime_implementation:
1231 _validators.insert(0, validatorClass(name=self.name, format=self.dateFormat))
1232 return _validators
1233
1234 def _mysqlType(self):
1235 return 'DATE'
1236
1237 def _postgresType(self):
1238 return 'DATE'
1239
1240 def _sybaseType(self):
1241 return self._postgresType()
1242
1243 def _mssqlType(self):
1244 """
1245 SQL Server doesn't have a DATE data type, to emulate we use a vc(10)
1246 """
1247 return 'VARCHAR(10)'
1248
1249 def _firebirdType(self):
1250 return 'DATE'
1251
1252 def _maxdbType(self):
1253 return 'DATE'
1254
1255 def _sqliteType(self):
1256 return 'DATE'
1257
1258class DateCol(Col):
1259 baseClass = SODateCol
1260
1261
1262class TimeValidator(DateTimeValidator):
1263 def to_python(self, value, state):
1264 if isinstance(value, (datetime.time, sqlbuilder.SQLExpression)):
1265 return value
1266 if isinstance(value, datetime.timedelta):
1267 if value.days:
1268 raise validators.Invalid(
1269 "the value for the TimeCol '%s' must has days=0, it has days=%d" %
1270 (self.name, value.days), value, state)
1271 return datetime.time(*time.gmtime(value.seconds)[3:6])
1272 value = super(TimeValidator, self).to_python(value, state)
1273 if isinstance(value, datetime.datetime):
1274 value = value.time()
1275 return value
1276
1277 from_python = to_python
1278
1279class SOTimeCol(SOCol):
1280 timeFormat = '%H:%M:%S'
1281
1282 def __init__(self, **kw):
1283 timeFormat = kw.pop('timeFormat', None)
1284 if timeFormat:
1285 self.timeFormat = timeFormat
1286 super(SOTimeCol, self).__init__(**kw)
1287
1288 def createValidators(self):
1289 _validators = super(SOTimeCol, self).createValidators()
1290 if default_datetime_implementation == DATETIME_IMPLEMENTATION:
1291 validatorClass = TimeValidator
1292 elif default_datetime_implementation == MXDATETIME_IMPLEMENTATION:
1293 validatorClass = MXDateTimeValidator
1294 if default_datetime_implementation:
1295 _validators.insert(0, validatorClass(name=self.name, format=self.timeFormat))
1296 return _validators
1297
1298 def _mysqlType(self):
1299 return 'TIME'
1300
1301 def _postgresType(self):
1302 return 'TIME'
1303
1304 def _sybaseType(self):
1305 return 'TIME'
1306
1307 def _sqliteType(self):
1308 return 'TIME'
1309
1310 def _firebirdType(self):
1311 return 'TIME'
1312
1313 def _maxdbType(self):
1314 return 'TIME'
1315
1316class TimeCol(Col):
1317 baseClass = SOTimeCol
1318
1319
1320class SOTimestampCol(SODateTimeCol):
1321 """
1322 Necessary to support MySQL's use of TIMESTAMP versus DATETIME types
1323 """
1324
1325 def __init__(self, **kw):
1326 if 'default' not in kw:
1327 kw['default'] = None
1328 SOCol.__init__(self, **kw)
1329
1330 def _mysqlType(self):
1331 return 'TIMESTAMP'
1332
1333class TimestampCol(Col):
1334 baseClass = SOTimestampCol
1335
1336
1337class TimedeltaValidator(SOValidator):
1338 def to_python(self, value, state):
1339 return value
1340
1341 from_python = to_python
1342
1343class SOTimedeltaCol(SOCol):
1344 def _postgresType(self):
1345 return 'INTERVAL'
1346
1347 def createValidators(self):
1348 return [TimedeltaValidator(name=self.name)] + super(SOTimedeltaCol, self).createValidators()
1350
1351class TimedeltaCol(Col):
1352 baseClass = SOTimedeltaCol
1353
1354
1355from decimal import Decimal
1356
1357class DecimalValidator(SOValidator):
1358 def to_python(self, value, state):
1359 if value is None:
1360 return None
1361 if isinstance(value, (int, long, Decimal, sqlbuilder.SQLExpression)):
1362 return value
1363 if isinstance(value, float):
1364 value = str(value)
1365 try:
1366 connection = state.connection or state.soObject._connection
1367 except AttributeError:
1368 pass
1369 else:
1370 if hasattr(connection, "decimalSeparator"):
1371 value = value.replace(connection.decimalSeparator, ".")
1372 try:
1373 return Decimal(value)
1374 except:
1375 raise validators.Invalid("expected a Decimal in the DecimalCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1377
1378 def from_python(self, value, state):
1379 if value is None:
1380 return None
1381 if isinstance(value, float):
1382 value = str(value)
1383 if isinstance(value, basestring):
1384 try:
1385 connection = state.connection or state.soObject._connection
1386 except AttributeError:
1387 pass
1388 else:
1389 if hasattr(connection, "decimalSeparator"):
1390 value = value.replace(connection.decimalSeparator, ".")
1391 try:
1392 return Decimal(value)
1393 except:
1394 raise validators.Invalid("can not parse Decimal value '%s' in the DecimalCol from '%s'" %
1395 (value, getattr(state, 'soObject', '(unknown)')), value, state)
1396 if isinstance(value, (int, long, Decimal, sqlbuilder.SQLExpression)):
1397 return value
1398 raise validators.Invalid("expected a Decimal in the DecimalCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1400
1401class SODecimalCol(SOCol):
1402
1403 def __init__(self, **kw):
1404 self.size = kw.pop('size', NoDefault)
1405 assert self.size is not NoDefault, "You must give a size argument"
1407 self.precision = kw.pop('precision', NoDefault)
1408 assert self.precision is not NoDefault, "You must give a precision argument"
1410 super(SODecimalCol, self).__init__(**kw)
1411
1412 def _sqlType(self):
1413 return 'DECIMAL(%i, %i)' % (self.size, self.precision)
1414
1415 def createValidators(self):
1416 return [DecimalValidator(name=self.name)] + super(SODecimalCol, self).createValidators()
1418
1419class DecimalCol(Col):
1420 baseClass = SODecimalCol
1421
1422class SOCurrencyCol(SODecimalCol):
1423
1424 def __init__(self, **kw):
1425 pushKey(kw, 'size', 10)
1426 pushKey(kw, 'precision', 2)
1427 super(SOCurrencyCol, self).__init__(**kw)
1428
1429class CurrencyCol(DecimalCol):
1430 baseClass = SOCurrencyCol
1431
1432
1433class DecimalStringValidator(DecimalValidator):
1434 def to_python(self, value, state):
1435 value = super(DecimalStringValidator, self).to_python(value, state)
1436 if self.precision and isinstance(value, Decimal):
1437 assert value < self.max, "Value must be less than %s" % int(self.max)
1439 value = value.quantize(self.precision)
1440 return value
1441
1442 def from_python(self, value, state):
1443 value = super(DecimalStringValidator, self).from_python(value, state)
1444 if isinstance(value, Decimal):
1445 if self.precision:
1446 assert value < self.max, "Value must be less than %s" % int(self.max)
1448 value = value.quantize(self.precision)
1449 value = value.to_eng_string()
1450 elif isinstance(value, (int, long)):
1451 value = str(value)
1452 return value
1453
1454class SODecimalStringCol(SOStringCol):
1455 def __init__(self, **kw):
1456 self.size = kw.pop('size', NoDefault)
1457 assert (self.size is not NoDefault) and (self.size >= 0), "You must give a size argument as a positive integer"
1459 self.precision = kw.pop('precision', NoDefault)
1460 assert (self.precision is not NoDefault) and (self.precision >= 0), "You must give a precision argument as a positive integer"
1462 kw['length'] = int(self.size) + int(self.precision)
1463 self.quantize = kw.pop('quantize', False)
1464 assert isinstance(self.quantize, bool), "quantize argument must be Boolean True/False"
1466 super(SODecimalStringCol, self).__init__(**kw)
1467
1468 def createValidators(self):
1469 if self.quantize:
1470 v = DecimalStringValidator(name=self.name,
1471 precision=Decimal(10) ** (-1 * int(self.precision)),
1472 max=Decimal(10) ** (int(self.size) - int(self.precision)))
1473 else:
1474 v = DecimalStringValidator(name=self.name, precision=0)
1475 return [v] + super(SODecimalStringCol, self).createValidators(dataType=Decimal)
1477
1478class DecimalStringCol(StringCol):
1479 baseClass = SODecimalStringCol
1480
1481
1482class BinaryValidator(SOValidator):
1483 """
1484 Validator for binary types.
1485
1486 We're assuming that the per-database modules provide some form
1487 of wrapper type for binary conversion.
1488 """
1489
1490 _cachedValue = None
1491
1492 def to_python(self, value, state):
1493 if value is None:
1494 return None
1495 try:
1496 connection = state.connection or state.soObject._connection
1497 except AttributeError:
1498 dbName = None
1499 binaryType = type(None)
1500 else:
1501 dbName = connection.dbName
1502 binaryType = connection._binaryType
1503 if isinstance(value, str):
1504 if dbName == "sqlite":
1505 value = connection.module.decode(value)
1506 return value
1507 if isinstance(value, (buffer, binaryType)):
1508 cachedValue = self._cachedValue
1509 if cachedValue and cachedValue[1] == value:
1510 return cachedValue[0]
1511 if isinstance(value, array):
1512 return value.tostring()
1513 return str(value)
1514 raise validators.Invalid("expected a string in the BLOBCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1516
1517 def from_python(self, value, state):
1518 if value is None:
1519 return None
1520 connection = state.connection or state.soObject._connection
1521 binary = connection.createBinary(value)
1522 self._cachedValue = (value, binary)
1523 return binary
1524
1525class SOBLOBCol(SOStringCol):
1526 def __init__(self, **kw):
1527
1528 if 'varchar' not in kw: kw['varchar'] = False
1529 super(SOBLOBCol, self).__init__(**kw)
1530
1531 def createValidators(self):
1532 return [BinaryValidator(name=self.name)] + super(SOBLOBCol, self).createValidators()
1534
1535 def _mysqlType(self):
1536 length = self.length
1537 varchar = self.varchar
1538 if length >= 2**24:
1539 return varchar and "LONGTEXT" or "LONGBLOB"
1540 if length >= 2**16:
1541 return varchar and "MEDIUMTEXT" or "MEDIUMBLOB"
1542 if length >= 2**8:
1543 return varchar and "TEXT" or "BLOB"
1544 return varchar and "TINYTEXT" or "TINYBLOB"
1545
1546 def _postgresType(self):
1547 return 'BYTEA'
1548
1549 def _mssqlType(self):
1550 if self.connection and self.connection.can_use_max_types():
1551 return 'VARBINARY(MAX)'
1552 else:
1553 return "IMAGE"
1554
1555class BLOBCol(StringCol):
1556 baseClass = SOBLOBCol
1557
1558
1559class PickleValidator(BinaryValidator):
1560 """
1561 Validator for pickle types. A pickle type is simply a binary type
1562 with hidden pickling, so that we can simply store any kind of
1563 stuff in a particular column.
1564
1565 The support for this relies directly on the support for binary for
1566 your database.
1567 """
1568
1569 def to_python(self, value, state):
1570 if value is None:
1571 return None
1572 if isinstance(value, unicode):
1573 dbEncoding = self.getDbEncoding(state, default='ascii')
1574 value = value.encode(dbEncoding)
1575 if isinstance(value, str):
1576 return pickle.loads(value)
1577 raise validators.Invalid("expected a pickle string in the PickleCol '%s', got %s %r instead" % (self.name, type(value), value), value, state)
1579
1580 def from_python(self, value, state):
1581 if value is None:
1582 return None
1583 return pickle.dumps(value, self.pickleProtocol)
1584
1585class SOPickleCol(SOBLOBCol):
1586
1587 def __init__(self, **kw):
1588 self.pickleProtocol = kw.pop('pickleProtocol', pickle.HIGHEST_PROTOCOL)
1589 super(SOPickleCol, self).__init__(**kw)
1590
1591 def createValidators(self):
1592 return [PickleValidator(name=self.name,
1593 pickleProtocol=self.pickleProtocol)] + super(SOPickleCol, self).createValidators()
1595
1596 def _mysqlType(self):
1597 length = self.length
1598 if length >= 2**24:
1599 return "LONGBLOB"
1600 if length >= 2**16:
1601 return "MEDIUMBLOB"
1602 return "BLOB"
1603
1604class PickleCol(BLOBCol):
1605 baseClass = SOPickleCol
1606
1607
1608def pushKey(kw, name, value):
1609 if not name in kw:
1610 kw[name] = value
1611
1612all = []
1613for key, value in globals().items():
1614 if isinstance(value, type) and (issubclass(value, (Col, SOCol))):
1615 all.append(key)
1616__all__.extend(all)
1617del all