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