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