0001"""0002Col -- SQLObject columns00030004Note that each column object is named BlahBlahCol, and these are used0005in class definitions. But there's also a corresponding SOBlahBlahCol0006object, which is used in SQLObject *classes*.00070008An explanation: when a SQLObject subclass is created, the metaclass0009looks through your class definition for any subclasses of Col. It0010collects them together, and indexes them to do all the database stuff0011you like, like the magic attributes and whatnot. It then asks the Col0012object to create an SOCol object (usually a subclass, actually). The0013SOCol object contains all the interesting logic, as well as a record0014of the attribute name you used and the class it is bound to (set by0015the metaclass).00160017So, in summary: Col objects are what you define, but SOCol objects0018are what gets used.0019"""00200021fromarrayimportarray0022fromdecimalimportDecimal0023fromitertoolsimportcount0024importtime0025try:0026importcPickleaspickle0027exceptImportError:0028importpickle0029importweakref00300031fromformencodeimportcompound,validators0032from.classregistryimportfindClass0033# Sadly the name "constraints" conflicts with many of the function0034# arguments in this module, so we rename it:0035from.importconstraintsasconstrs0036from.importconverters0037from.importsqlbuilder0038from.stylesimportcapword0039from.compatimportPY2,string_type,unicode_type,buffer_type00400041importdatetime0042datetime_available=True00430044try:0045frommximportDateTime0046exceptImportError:0047try:0048# old version of mxDateTime,0049# or Zope's Version if we're running with Zope0050importDateTime0051exceptImportError:0052mxdatetime_available=False0053else:0054mxdatetime_available=True0055else:0056mxdatetime_available=True00570058DATETIME_IMPLEMENTATION="datetime"0059MXDATETIME_IMPLEMENTATION="mxDateTime"00600061ifmxdatetime_available:0062ifhasattr(DateTime,"Time"):0063DateTimeType=type(DateTime.now())0064TimeType=type(DateTime.Time())0065else:# Zope0066DateTimeType=type(DateTime.DateTime())0067TimeType=type(DateTime.DateTime.Time(DateTime.DateTime()))00680069__all__=["datetime_available","mxdatetime_available",0070"default_datetime_implementation","DATETIME_IMPLEMENTATION"]00710072ifmxdatetime_available:0073__all__.append("MXDATETIME_IMPLEMENTATION")00740075default_datetime_implementation=DATETIME_IMPLEMENTATION00760077ifnotPY2:0078# alias for python 3 compatibility0079long=int0080# This is to satisfy flake8 under python 30081unicode=str00820083NoDefault=sqlbuilder.NoDefault008400850086defuse_microseconds(use=True):0087ifuse:0088SODateTimeCol.datetimeFormat='%Y-%m-%d %H:%M:%S.%f'0089SOTimeCol.timeFormat='%H:%M:%S.%f'0090dt_types=[(datetime.datetime,converters.DateTimeConverterMS),0091(datetime.time,converters.TimeConverterMS)]0092else:0093SODateTimeCol.datetimeFormat='%Y-%m-%d %H:%M:%S'0094SOTimeCol.timeFormat='%H:%M:%S'0095dt_types=[(datetime.datetime,converters.DateTimeConverter),0096(datetime.time,converters.TimeConverter)]0097fordt_type,converterindt_types:0098converters.registerConverter(dt_type,converter)009901000101__all__.append("use_microseconds")010201030104creationOrder=count()01050106########################################0107# Columns0108########################################01090110# Col is essentially a column definition, it doesn't have much logic to it.011101120113classSOCol(object):01140115def__init__(self,0116name,0117soClass,0118creationOrder,0119dbName=None,0120default=NoDefault,0121defaultSQL=None,0122foreignKey=None,0123alternateID=False,0124alternateMethodName=None,0125constraints=None,0126notNull=NoDefault,0127notNone=NoDefault,0128unique=NoDefault,0129sqlType=None,0130columnDef=None,0131validator=None,0132validator2=None,0133immutable=False,0134cascade=None,0135lazy=False,0136noCache=False,0137forceDBName=False,0138title=None,0139tags=[],0140origName=None,0141dbEncoding=None,0142extra_vars=None):01430144super(SOCol,self).__init__()01450146# This isn't strictly true, since we *could* use backquotes or0147# " or something (database-specific) around column names, but0148# why would anyone *want* to use a name like that?0149# @@: I suppose we could actually add backquotes to the0150# dbName if we needed to...0151ifnotforceDBName:0152assertsqlbuilder.sqlIdentifier(name),(0153'Name must be SQL-safe '0154'(letters, numbers, underscores): %s (or use forceDBName=True)'0155%repr(name))0156assertname!='id',(0157'The column name "id" is reserved for SQLObject use '0158'(and is implicitly created).')0159assertname,"You must provide a name for all columns"01600161self.columnDef=columnDef0162self.creationOrder=creationOrder01630164self.immutable=immutable01650166# cascade can be one of:0167# None: no constraint is generated0168# True: a CASCADE constraint is generated0169# False: a RESTRICT constraint is generated0170# 'null': a SET NULL trigger is generated0171ifisinstance(cascade,str):0172assertcascade=='null',(0173"The only string value allowed for cascade is 'null' "0174"(you gave: %r)"%cascade)0175self.cascade=cascade01760177ifnotisinstance(constraints,(list,tuple)):0178constraints=[constraints]0179self.constraints=self.autoConstraints()+constraints01800181self.notNone=False0182ifnotNullisnotNoDefault:0183self.notNone=notNull0184assertnotNoneisNoDefaultor(notnotNone)==(notnotNull),(0185"The notNull and notNone arguments are aliases, "0186"and must not conflict. "0187"You gave notNull=%r, notNone=%r"%(notNull,notNone))0188elifnotNoneisnotNoDefault:0189self.notNone=notNone0190ifself.notNone:0191self.constraints=[constrs.notNull]+self.constraints01920193self.name=name0194self.soClass=soClass0195self._default=default0196self.defaultSQL=defaultSQL0197self.customSQLType=sqlType01980199# deal with foreign keys0200self.foreignKey=foreignKey0201ifself.foreignKey:0202iforigNameisnotNone:0203idname=soClass.sqlmeta.style.instanceAttrToIDAttr(origName)0204else:0205idname=soClass.sqlmeta.style.instanceAttrToIDAttr(name)0206ifself.name!=idname:0207self.foreignName=self.name0208self.name=idname0209else:0210self.foreignName=soClass.sqlmeta.style.instanceIDAttrToAttr(self.name)0212else:0213self.foreignName=None02140215# if they don't give us a specific database name for0216# the column, we separate the mixedCase into mixed_case0217# and assume that.0218ifdbNameisNone:0219self.dbName=soClass.sqlmeta.style.pythonAttrToDBColumn(self.name)0220else:0221self.dbName=dbName02220223# alternateID means that this is a unique column that0224# can be used to identify rows0225self.alternateID=alternateID02260227ifuniqueisNoDefault:0228self.unique=alternateID0229else:0230self.unique=unique0231ifself.uniqueandalternateMethodNameisNone:0232self.alternateMethodName='by'+capword(self.name)0233else:0234self.alternateMethodName=alternateMethodName02350236_validators=self.createValidators()0237ifvalidator:0238_validators.append(validator)0239ifvalidator2:0240_validators.insert(0,validator2)0241_vlen=len(_validators)0242if_vlen:0243for_validatorin_validators:0244_validator.soCol=weakref.proxy(self)0245if_vlen==0:0246self.validator=None# Set sef.{from,to}_python0247elif_vlen==1:0248self.validator=_validators[0]0249elif_vlen>1:0250self.validator=compound.All.join(0251_validators[0],*_validators[1:])0252self.noCache=noCache0253self.lazy=lazy0254# this is in case of ForeignKey, where we rename the column0255# and append an ID0256self.origName=origNameorname0257self.title=title0258self.tags=tags0259self.dbEncoding=dbEncoding02600261ifextra_vars:0262forname,valueinextra_vars.items():0263setattr(self,name,value)02640265def_set_validator(self,value):0266self._validator=value0267ifself._validator:0268self.to_python=self._validator.to_python0269self.from_python=self._validator.from_python0270else:0271self.to_python=None0272self.from_python=None02730274def_get_validator(self):0275returnself._validator02760277validator=property(_get_validator,_set_validator)02780279defcreateValidators(self):0280"""Create a list of validators for the column."""0281return[]02820283defautoConstraints(self):0284return[]02850286def_get_default(self):0287# A default can be a callback or a plain value,0288# here we resolve the callback0289ifself._defaultisNoDefault:0290returnNoDefault0291elifhasattr(self._default,'__sqlrepr__'):0292returnself._default0293elifcallable(self._default):0294returnself._default()0295else:0296returnself._default0297default=property(_get_default,None,None)02980299def_get_joinName(self):0300returnself.soClass.sqlmeta.style.instanceIDAttrToAttr(self.name)0301joinName=property(_get_joinName,None,None)03020303def__repr__(self):0304r='<%s %s'%(self.__class__.__name__,self.name)0305ifself.defaultisnotNoDefault:0306r+=' default=%s'%repr(self.default)0307ifself.foreignKey:0308r+=' connected to %s'%self.foreignKey0309ifself.alternateID:0310r+=' alternate ID'0311ifself.notNone:0312r+=' not null'0313returnr+'>'03140315defcreateSQL(self):0316return' '.join([self._sqlType()]+self._extraSQL())03170318def_extraSQL(self):0319result=[]0320ifself.notNoneorself.alternateID:0321result.append('NOT NULL')0322ifself.uniqueorself.alternateID:0323result.append('UNIQUE')0324ifself.defaultSQLisnotNone:0325result.append("DEFAULT %s"%self.defaultSQL)0326returnresult03270328def_sqlType(self):0329ifself.customSQLTypeisNone:0330raiseValueError("Col %s (%s) cannot be used for automatic "0331"schema creation (too abstract)"%0332(self.name,self.__class__))0333else:0334returnself.customSQLType03350336def_mysqlType(self):0337returnself._sqlType()03380339def_postgresType(self):0340returnself._sqlType()03410342def_sqliteType(self):0343# SQLite is naturally typeless, so as a fallback it uses0344# no type.0345try:0346returnself._sqlType()0347exceptValueError:0348return''03490350def_sybaseType(self):0351returnself._sqlType()03520353def_mssqlType(self):0354returnself._sqlType()03550356def_firebirdType(self):0357returnself._sqlType()03580359def_maxdbType(self):0360returnself._sqlType()03610362defmysqlCreateSQL(self,connection=None):0363self.connection=connection0364return' '.join([self.dbName,self._mysqlType()]+self._extraSQL())03650366defpostgresCreateSQL(self):0367return' '.join([self.dbName,self._postgresType()]+self._extraSQL())03680369defsqliteCreateSQL(self):0370return' '.join([self.dbName,self._sqliteType()]+self._extraSQL())03710372defsybaseCreateSQL(self):0373return' '.join([self.dbName,self._sybaseType()]+self._extraSQL())03740375defmssqlCreateSQL(self,connection=None):0376self.connection=connection0377return' '.join([self.dbName,self._mssqlType()]+self._extraSQL())03780379deffirebirdCreateSQL(self):0380# Ian Sparks pointed out that fb is picky about the order0381# of the NOT NULL clause in a create statement. So, we handle0382# them differently for Enum columns.0383ifnotisinstance(self,SOEnumCol):0384return' '.join(0385[self.dbName,self._firebirdType()]+self._extraSQL())0386else:0387return' '.join(0388[self.dbName]+[self._firebirdType()[0]]+0389self._extraSQL()+[self._firebirdType()[1]])03900391defmaxdbCreateSQL(self):0392return' '.join([self.dbName,self._maxdbType()]+self._extraSQL())03930394def__get__(self,obj,type=None):0395ifobjisNone:0396# class attribute, return the descriptor itself0397returnself0398ifobj.sqlmeta._obsolete:0399raiseRuntimeError('The object <%s %s> is obsolete'%(0400obj.__class__.__name__,obj.id))0401ifobj.sqlmeta.cacheColumns:0402columns=obj.sqlmeta._columnCache0403ifcolumnsisNone:0404obj.sqlmeta.loadValues()0405try:0406returncolumns[name]# noqa0407exceptKeyError:0408returnobj.sqlmeta.loadColumn(self)0409else:0410returnobj.sqlmeta.loadColumn(self)04110412def__set__(self,obj,value):0413ifself.immutable:0414raiseAttributeError("The column %s.%s is immutable"%0415(obj.__class__.__name__,0416self.name))0417obj.sqlmeta.setColumn(self,value)04180419def__delete__(self,obj):0420raiseAttributeError("I can't be deleted from %r"%obj)04210422defgetDbEncoding(self,state,default='utf-8'):0423ifself.dbEncoding:0424returnself.dbEncoding0425dbEncoding=state.soObject.sqlmeta.dbEncoding0426ifdbEncoding:0427returndbEncoding0428try:0429connection=state.connectionorstate.soObject._connection0430exceptAttributeError:0431dbEncoding=None0432else:0433dbEncoding=getattr(connection,"dbEncoding",None)0434ifnotdbEncoding:0435dbEncoding=default0436returndbEncoding043704380439classCol(object):04400441baseClass=SOCol04420443def__init__(self,name=None,**kw):0444super(Col,self).__init__()0445self.__dict__['_name']=name0446self.__dict__['_kw']=kw0447self.__dict__['creationOrder']=next(creationOrder)0448self.__dict__['_extra_vars']={}04490450def_set_name(self,value):0451assertself._nameisNoneorself._name==value,(0452"You cannot change a name after it has already been set "0453"(from %s to %s)"%(self.name,value))0454self.__dict__['_name']=value04550456def_get_name(self):0457returnself._name04580459name=property(_get_name,_set_name)04600461defwithClass(self,soClass):0462returnself.baseClass(soClass=soClass,name=self._name,0463creationOrder=self.creationOrder,0464columnDef=self,0465extra_vars=self._extra_vars,0466**self._kw)04670468def__setattr__(self,var,value):0469ifvar=='name':0470super(Col,self).__setattr__(var,value)0471return0472self._extra_vars[var]=value04730474def__repr__(self):0475return'<%s %s %s>'%(0476self.__class__.__name__,hex(abs(id(self)))[2:],0477self._nameor'(unnamed)')047804790480classSOValidator(validators.Validator):0481defgetDbEncoding(self,state,default='utf-8'):0482try:0483returnself.dbEncoding0484exceptAttributeError:0485returnself.soCol.getDbEncoding(state,default=default)048604870488classSOStringLikeCol(SOCol):0489"""A common ancestor for SOStringCol and SOUnicodeCol"""0490def__init__(self,**kw):0491self.length=kw.pop('length',None)0492self.varchar=kw.pop('varchar','auto')0493self.char_binary=kw.pop('char_binary',None)# A hack for MySQL0494ifnotself.length:0495assertself.varchar=='auto'ornotself.varchar,"Without a length strings are treated as TEXT, not varchar"0497self.varchar=False0498elifself.varchar=='auto':0499self.varchar=True05000501super(SOStringLikeCol,self).__init__(**kw)05020503defautoConstraints(self):0504constraints=[constrs.isString]0505ifself.lengthisnotNone:0506constraints+=[constrs.MaxLength(self.length)]0507returnconstraints05080509def_sqlType(self):0510ifself.customSQLTypeisnotNone:0511returnself.customSQLType0512ifnotself.length:0513return'TEXT'0514elifself.varchar:0515return'VARCHAR(%i)'%self.length0516else:0517return'CHAR(%i)'%self.length05180519def_check_case_sensitive(self,db):0520ifself.char_binary:0521raiseValueError("%s does not support "0522"binary character columns"%db)05230524def_mysqlType(self):0525type=self._sqlType()0526ifself.char_binary:0527type+=" BINARY"0528returntype05290530def_postgresType(self):0531self._check_case_sensitive("PostgreSQL")0532returnsuper(SOStringLikeCol,self)._postgresType()05330534def_sqliteType(self):0535self._check_case_sensitive("SQLite")0536returnsuper(SOStringLikeCol,self)._sqliteType()05370538def_sybaseType(self):0539self._check_case_sensitive("SYBASE")0540type=self._sqlType()0541ifnotself.notNoneandnotself.alternateID:0542type+=' NULL'0543returntype05440545def_mssqlType(self):0546ifself.customSQLTypeisnotNone:0547returnself.customSQLType0548ifnotself.length:0549ifself.connectionandself.connection.can_use_max_types():0550type='VARCHAR(MAX)'0551else:0552type='VARCHAR(4000)'0553elifself.varchar:0554type='VARCHAR(%i)'%self.length0555else:0556type='CHAR(%i)'%self.length0557ifnotself.notNoneandnotself.alternateID:0558type+=' NULL'0559returntype05600561def_firebirdType(self):0562self._check_case_sensitive("FireBird")0563ifnotself.length:0564return'BLOB SUB_TYPE TEXT'0565else:0566returnself._sqlType()05670568def_maxdbType(self):0569self._check_case_sensitive("SAP DB/MaxDB")0570ifnotself.length:0571return'LONG ASCII'0572else:0573returnself._sqlType()057405750576classStringValidator(SOValidator):05770578defto_python(self,value,state):0579ifvalueisNone:0580returnNone0581try:0582connection=state.connectionorstate.soObject._connection0583binaryType=connection._binaryType0584dbName=connection.dbName0585exceptAttributeError:0586binaryType=type(None)# Just a simple workaround0587dbEncoding=self.getDbEncoding(state,default='ascii')0588ifisinstance(value,unicode_type):0589ifPY2:0590returnvalue.encode(dbEncoding)0591returnvalue0592ifself.dataTypeandisinstance(value,self.dataType):0593returnvalue0594ifisinstance(value,0595(str,buffer_type,binaryType,0596sqlbuilder.SQLExpression)):0597returnvalue0598ifhasattr(value,'__unicode__'):0599returnunicode(value).encode(dbEncoding)0600ifdbName=='mysql'andnotPY2andisinstance(value,bytes):0601returnvalue.decode('ascii',errors='surrogateescape')0602raisevalidators.Invalid(0603"expected a str in the StringCol '%s', got %s %r instead"%(0604self.name,type(value),value),value,state)06050606from_python=to_python060706080609classSOStringCol(SOStringLikeCol):06100611defcreateValidators(self,dataType=None):0612return[StringValidator(name=self.name,dataType=dataType)]+super(SOStringCol,self).createValidators()061406150616classStringCol(Col):0617baseClass=SOStringCol061806190620classNQuoted(sqlbuilder.SQLExpression):0621def__init__(self,value):0622assertisinstance(value,unicode_type)0623self.value=value06240625def__hash__(self):0626returnhash(self.value)06270628def__sqlrepr__(self,db):0629assertdb=='mssql'0630return"N"+sqlbuilder.sqlrepr(self.value,db)063106320633classUnicodeStringValidator(SOValidator):06340635defto_python(self,value,state):0636ifvalueisNone:0637returnNone0638ifisinstance(value,(unicode_type,sqlbuilder.SQLExpression)):0639returnvalue0640ifisinstance(value,str):0641returnvalue.decode(self.getDbEncoding(state))0642ifisinstance(value,array):# MySQL0643returnvalue.tostring().decode(self.getDbEncoding(state))0644ifhasattr(value,'__unicode__'):0645returnunicode(value)0646raisevalidators.Invalid(0647"expected a str or a unicode in the UnicodeCol '%s', "0648"got %s %r instead"%(0649self.name,type(value),value),value,state)06500651deffrom_python(self,value,state):0652ifvalueisNone:0653returnNone0654ifisinstance(value,(str,sqlbuilder.SQLExpression)):0655returnvalue0656ifisinstance(value,unicode_type):0657try:0658connection=state.connectionorstate.soObject._connection0659exceptAttributeError:0660pass0661else:0662ifconnection.dbName=='mssql':0663returnNQuoted(value)0664returnvalue.encode(self.getDbEncoding(state))0665ifhasattr(value,'__unicode__'):0666returnunicode(value).encode(self.getDbEncoding(state))0667raisevalidators.Invalid(0668"expected a str or a unicode in the UnicodeCol '%s', "0669"got %s %r instead"%(0670self.name,type(value),value),value,state)067106720673classSOUnicodeCol(SOStringLikeCol):0674def_mssqlType(self):0675ifself.customSQLTypeisnotNone:0676returnself.customSQLType0677return'N'+super(SOUnicodeCol,self)._mssqlType()06780679defcreateValidators(self):0680return[UnicodeStringValidator(name=self.name)]+super(SOUnicodeCol,self).createValidators()068206830684classUnicodeCol(Col):0685baseClass=SOUnicodeCol068606870688classIntValidator(SOValidator):06890690defto_python(self,value,state):0691ifvalueisNone:0692returnNone0693ifisinstance(value,(int,long,sqlbuilder.SQLExpression)):0694returnvalue0695forconverter,attr_namein(int,'__int__'),(long,'__long__'):0696ifhasattr(value,attr_name):0697try:0698returnconverter(value)0699except:0700break0701raisevalidators.Invalid(0702"expected an int in the IntCol '%s', got %s %r instead"%(0703self.name,type(value),value),value,state)07040705from_python=to_python070607070708classSOIntCol(SOCol):0709# 3-03 @@: support precision, maybe max and min directly0710def__init__(self,**kw):0711self.length=kw.pop('length',None)0712self.unsigned=bool(kw.pop('unsigned',None))0713self.zerofill=bool(kw.pop('zerofill',None))0714SOCol.__init__(self,**kw)07150716defautoConstraints(self):0717return[constrs.isInt]07180719defcreateValidators(self):0720return[IntValidator(name=self.name)]+super(SOIntCol,self).createValidators()07220723defaddSQLAttrs(self,str):0724_ret=str0725ifstrisNoneorlen(str)<1:0726returnNone07270728ifself.lengthandself.length>=1:0729_ret="%s(%d)"%(_ret,self.length)0730ifself.unsigned:0731_ret=_ret+" UNSIGNED"0732ifself.zerofill:0733_ret=_ret+" ZEROFILL"0734return_ret07350736def_sqlType(self):0737returnself.addSQLAttrs("INT")073807390740classIntCol(Col):0741baseClass=SOIntCol074207430744classSOTinyIntCol(SOIntCol):0745def_sqlType(self):0746returnself.addSQLAttrs("TINYINT")074707480749classTinyIntCol(Col):0750baseClass=SOTinyIntCol075107520753classSOSmallIntCol(SOIntCol):0754def_sqlType(self):0755returnself.addSQLAttrs("SMALLINT")075607570758classSmallIntCol(Col):0759baseClass=SOSmallIntCol076007610762classSOMediumIntCol(SOIntCol):0763def_sqlType(self):0764returnself.addSQLAttrs("MEDIUMINT")076507660767classMediumIntCol(Col):0768baseClass=SOMediumIntCol076907700771classSOBigIntCol(SOIntCol):0772def_sqlType(self):0773returnself.addSQLAttrs("BIGINT")077407750776classBigIntCol(Col):0777baseClass=SOBigIntCol077807790780classBoolValidator(SOValidator):07810782defto_python(self,value,state):0783ifvalueisNone:0784returnNone0785ifisinstance(value,(bool,sqlbuilder.SQLExpression)):0786returnvalue0787ifisinstance(value,(int,long))orhasattr(value,'__nonzero__'):0788returnbool(value)0789raisevalidators.Invalid(0790"expected a bool or an int in the BoolCol '%s', "0791"got %s %r instead"%(0792self.name,type(value),value),value,state)07930794from_python=to_python079507960797classSOBoolCol(SOCol):0798defautoConstraints(self):0799return[constrs.isBool]08000801defcreateValidators(self):0802return[BoolValidator(name=self.name)]+super(SOBoolCol,self).createValidators()08040805def_postgresType(self):0806return'BOOL'08070808def_mysqlType(self):0809return"BOOL"08100811def_sybaseType(self):0812return"BIT"08130814def_mssqlType(self):0815return"BIT"08160817def_firebirdType(self):0818return'INT'08190820def_maxdbType(self):0821return"BOOLEAN"08220823def_sqliteType(self):0824return"BOOLEAN"082508260827classBoolCol(Col):0828baseClass=SOBoolCol082908300831classFloatValidator(SOValidator):08320833defto_python(self,value,state):0834ifvalueisNone:0835returnNone0836ifisinstance(value,(float,int,long,sqlbuilder.SQLExpression)):0837returnvalue0838forconverter,attr_namein(0839(float,'__float__'),(int,'__int__'),(long,'__long__')):0840ifhasattr(value,attr_name):0841try:0842returnconverter(value)0843except:0844break0845raisevalidators.Invalid(0846"expected a float in the FloatCol '%s', got %s %r instead"%(0847self.name,type(value),value),value,state)08480849from_python=to_python085008510852classSOFloatCol(SOCol):0853# 3-03 @@: support precision (e.g., DECIMAL)08540855defautoConstraints(self):0856return[constrs.isFloat]08570858defcreateValidators(self):0859return[FloatValidator(name=self.name)]+super(SOFloatCol,self).createValidators()08610862def_sqlType(self):0863return'FLOAT'08640865def_mysqlType(self):0866return"DOUBLE PRECISION"086708680869classFloatCol(Col):0870baseClass=SOFloatCol087108720873classSOKeyCol(SOCol):0874key_type={int:"INT",str:"TEXT"}08750876# 3-03 @@: this should have a simplified constructor0877# Should provide foreign key information for other DBs.08780879def__init__(self,**kw):0880self.refColumn=kw.pop('refColumn',None)0881super(SOKeyCol,self).__init__(**kw)08820883def_sqlType(self):0884returnself.key_type[self.soClass.sqlmeta.idType]08850886def_sybaseType(self):0887key_type={int:"NUMERIC(18,0) NULL",str:"TEXT"}0888returnkey_type[self.soClass.sqlmeta.idType]08890890def_mssqlType(self):0891key_type={int:"INT NULL",str:"TEXT"}0892returnkey_type[self.soClass.sqlmeta.idType]089308940895classKeyCol(Col):08960897baseClass=SOKeyCol089808990900classSOForeignKey(SOKeyCol):09010902def__init__(self,**kw):0903foreignKey=kw['foreignKey']0904style=kw['soClass'].sqlmeta.style0905ifkw.get('name'):0906kw['origName']=kw['name']0907kw['name']=style.instanceAttrToIDAttr(kw['name'])0908else:0909kw['name']=style.instanceAttrToIDAttr(0910style.pythonClassToAttr(foreignKey))0911super(SOForeignKey,self).__init__(**kw)09120913defsqliteCreateSQL(self):0914sql=SOKeyCol.sqliteCreateSQL(self)0915other=findClass(self.foreignKey,self.soClass.sqlmeta.registry)0916tName=other.sqlmeta.table0917idName=self.refColumnorother.sqlmeta.idName0918ifself.cascadeisnotNone:0919ifself.cascade=='null':0920action='ON DELETE SET NULL'0921elifself.cascade:0922action='ON DELETE CASCADE'0923else:0924action='ON DELETE RESTRICT'0925else:0926action=''0927constraint=('CONSTRAINT %(colName)s_exists '0928# 'FOREIGN KEY(%(colName)s) '0929'REFERENCES %(tName)s(%(idName)s) '0930'%(action)s'%0931{'tName':tName,0932'colName':self.dbName,0933'idName':idName,0934'action':action})0935sql=' '.join([sql,constraint])0936returnsql09370938defpostgresCreateSQL(self):0939sql=SOKeyCol.postgresCreateSQL(self)0940returnsql09410942defpostgresCreateReferenceConstraint(self):0943sTName=self.soClass.sqlmeta.table0944other=findClass(self.foreignKey,self.soClass.sqlmeta.registry)0945tName=other.sqlmeta.table0946idName=self.refColumnorother.sqlmeta.idName0947ifself.cascadeisnotNone:0948ifself.cascade=='null':0949action='ON DELETE SET NULL'0950elifself.cascade:0951action='ON DELETE CASCADE'0952else:0953action='ON DELETE RESTRICT'0954else:0955action=''0956constraint=('ALTER TABLE %(sTName)s '0957'ADD CONSTRAINT %(colName)s_exists '0958'FOREIGN KEY (%(colName)s) '0959'REFERENCES %(tName)s (%(idName)s) '0960'%(action)s'%0961{'tName':tName,0962'colName':self.dbName,0963'idName':idName,0964'action':action,0965'sTName':sTName})0966returnconstraint09670968defmysqlCreateReferenceConstraint(self):0969sTName=self.soClass.sqlmeta.table0970sTLocalName=sTName.split('.')[-1]0971other=findClass(self.foreignKey,self.soClass.sqlmeta.registry)0972tName=other.sqlmeta.table0973idName=self.refColumnorother.sqlmeta.idName0974ifself.cascadeisnotNone:0975ifself.cascade=='null':0976action='ON DELETE SET NULL'0977elifself.cascade:0978action='ON DELETE CASCADE'0979else:0980action='ON DELETE RESTRICT'0981else:0982action=''0983constraint=('ALTER TABLE %(sTName)s '0984'ADD CONSTRAINT %(sTLocalName)s_%(colName)s_exists '0985'FOREIGN KEY (%(colName)s) '0986'REFERENCES %(tName)s (%(idName)s) '0987'%(action)s'%0988{'tName':tName,0989'colName':self.dbName,0990'idName':idName,0991'action':action,0992'sTName':sTName,0993'sTLocalName':sTLocalName})0994returnconstraint09950996defmysqlCreateSQL(self,connection=None):0997returnSOKeyCol.mysqlCreateSQL(self,connection)09980999defsybaseCreateSQL(self):1000sql=SOKeyCol.sybaseCreateSQL(self)1001other=findClass(self.foreignKey,self.soClass.sqlmeta.registry)1002tName=other.sqlmeta.table1003idName=self.refColumnorother.sqlmeta.idName1004reference=('REFERENCES %(tName)s(%(idName)s) '%1005{'tName':tName,1006'idName':idName})1007sql=' '.join([sql,reference])1008returnsql10091010defsybaseCreateReferenceConstraint(self):1011# @@: Code from above should be moved here1012returnNone10131014defmssqlCreateSQL(self,connection=None):1015sql=SOKeyCol.mssqlCreateSQL(self,connection)1016other=findClass(self.foreignKey,self.soClass.sqlmeta.registry)1017tName=other.sqlmeta.table1018idName=self.refColumnorother.sqlmeta.idName1019reference=('REFERENCES %(tName)s(%(idName)s) '%1020{'tName':tName,1021'idName':idName})1022sql=' '.join([sql,reference])1023returnsql10241025defmssqlCreateReferenceConstraint(self):1026# @@: Code from above should be moved here1027returnNone10281029defmaxdbCreateSQL(self):1030other=findClass(self.foreignKey,self.soClass.sqlmeta.registry)1031fidName=self.dbName1032# I assume that foreign key name is identical1033# to the id of the reference table1034sql=' '.join([fidName,self._maxdbType()])1035tName=other.sqlmeta.table1036idName=self.refColumnorother.sqlmeta.idName1037sql=sql+','+'\n'1038sql=sql+'FOREIGN KEY (%s) REFERENCES %s(%s)'%(fidName,tName,1039idName)1040returnsql10411042defmaxdbCreateReferenceConstraint(self):1043# @@: Code from above should be moved here1044returnNone104510461047classForeignKey(KeyCol):10481049baseClass=SOForeignKey10501051def__init__(self,foreignKey=None,**kw):1052super(ForeignKey,self).__init__(foreignKey=foreignKey,**kw)105310541055classEnumValidator(SOValidator):10561057defto_python(self,value,state):1058ifvalueinself.enumValues:1059# Only encode on python 2 - on python 3, the database driver1060# will handle this1061ifisinstance(value,unicode_type)andPY2:1062dbEncoding=self.getDbEncoding(state)1063value=value.encode(dbEncoding)1064returnvalue1065elifnotself.notNoneandvalueisNone:1066returnNone1067raisevalidators.Invalid(1068"expected a member of %r in the EnumCol '%s', got %r instead"%(1069self.enumValues,self.name,value),value,state)10701071from_python=to_python107210731074classSOEnumCol(SOCol):10751076def__init__(self,**kw):1077self.enumValues=kw.pop('enumValues',None)1078assertself.enumValuesisnotNone,'You must provide an enumValues keyword argument'1080super(SOEnumCol,self).__init__(**kw)10811082defautoConstraints(self):1083return[constrs.isString,constrs.InList(self.enumValues)]10841085defcreateValidators(self):1086return[EnumValidator(name=self.name,enumValues=self.enumValues,1087notNone=self.notNone)]+super(SOEnumCol,self).createValidators()10891090def_mysqlType(self):1091# We need to map None in the enum expression to an appropriate1092# condition on NULL1093ifNoneinself.enumValues:1094return"ENUM(%s)"%', '.join(1095[sqlbuilder.sqlrepr(v,'mysql')forvinself.enumValues1096ifvisnotNone])1097else:1098return"ENUM(%s) NOT NULL"%', '.join(1099[sqlbuilder.sqlrepr(v,'mysql')forvinself.enumValues])11001101def_postgresType(self):1102length=max(map(self._getlength,self.enumValues))1103enumValues=', '.join(1104[sqlbuilder.sqlrepr(v,'postgres')forvinself.enumValues])1105checkConstraint="CHECK (%s in (%s))"%(self.dbName,enumValues)1106return"VARCHAR(%i) %s"%(length,checkConstraint)11071108_sqliteType=_postgresType11091110def_sybaseType(self):1111returnself._postgresType()11121113def_mssqlType(self):1114returnself._postgresType()11151116def_firebirdType(self):1117length=max(map(self._getlength,self.enumValues))1118enumValues=', '.join(1119[sqlbuilder.sqlrepr(v,'firebird')forvinself.enumValues])1120checkConstraint="CHECK (%s in (%s))"%(self.dbName,enumValues)1121# NB. Return a tuple, not a string here1122return"VARCHAR(%i)"%(length),checkConstraint11231124def_maxdbType(self):1125raiseTypeError("Enum type is not supported on MAX DB")11261127def_getlength(self,obj):1128"""1129 None counts as 0; everything else uses len()1130 """1131ifobjisNone:1132return01133else:1134returnlen(obj)113511361137classEnumCol(Col):1138baseClass=SOEnumCol113911401141classSetValidator(SOValidator):1142"""1143 Translates Python tuples into SQL comma-delimited SET strings.1144 """11451146defto_python(self,value,state):1147ifisinstance(value,str):1148returntuple(value.split(","))1149raisevalidators.Invalid(1150"expected a string in the SetCol '%s', got %s %r instead"%(1151self.name,type(value),value),value,state)11521153deffrom_python(self,value,state):1154ifisinstance(value,string_type):1155value=(value,)1156try:1157return",".join(value)1158except:1159raisevalidators.Invalid(1160"expected a string or a sequence of strings "1161"in the SetCol '%s', got %s %r instead"%(1162self.name,type(value),value),value,state)116311641165classSOSetCol(SOCol):1166def__init__(self,**kw):1167self.setValues=kw.pop('setValues',None)1168assertself.setValuesisnotNone,'You must provide a setValues keyword argument'1170super(SOSetCol,self).__init__(**kw)11711172defautoConstraints(self):1173return[constrs.isString,constrs.InList(self.setValues)]11741175defcreateValidators(self):1176return[SetValidator(name=self.name,setValues=self.setValues)]+super(SOSetCol,self).createValidators()11781179def_mysqlType(self):1180return"SET(%s)"%', '.join(1181[sqlbuilder.sqlrepr(v,'mysql')forvinself.setValues])118211831184classSetCol(Col):1185baseClass=SOSetCol118611871188classDateTimeValidator(validators.DateValidator):1189defto_python(self,value,state):1190ifvalueisNone:1191returnNone1192ifisinstance(value,1193(datetime.datetime,datetime.date,1194datetime.time,sqlbuilder.SQLExpression)):1195returnvalue1196ifmxdatetime_available:1197ifisinstance(value,DateTimeType):1198# convert mxDateTime instance to datetime1199if(self.format.find("%H")>=0)or(self.format.find("%T"))>=0:1201returndatetime.datetime(value.year,value.month,1202value.day,1203value.hour,value.minute,1204int(value.second))1205else:1206returndatetime.date(value.year,value.month,value.day)1207elifisinstance(value,TimeType):1208# convert mxTime instance to time1209ifself.format.find("%d")>=0:1210returndatetime.timedelta(seconds=value.seconds)1211else:1212returndatetime.time(value.hour,value.minute,1213int(value.second))1214try:1215ifself.format.find(".%f")>=0:1216if'.'invalue:1217_value=value.split('.')1218microseconds=_value[-1]1219_l=len(microseconds)1220if_l<6:1221_value[-1]=microseconds+'0'*(6-_l)1222elif_l>6:1223_value[-1]=microseconds[:6]1224if_l!=6:1225value='.'.join(_value)1226else:1227value+='.0'1228returndatetime.datetime.strptime(value,self.format)1229except:1230raisevalidators.Invalid(1231"expected a date/time string of the '%s' format "1232"in the DateTimeCol '%s', got %s %r instead"%(1233self.format,self.name,type(value),value),value,state)12341235deffrom_python(self,value,state):1236ifvalueisNone:1237returnNone1238ifisinstance(value,1239(datetime.datetime,datetime.date,1240datetime.time,sqlbuilder.SQLExpression)):1241returnvalue1242ifhasattr(value,"strftime"):1243returnvalue.strftime(self.format)1244raisevalidators.Invalid(1245"expected a datetime in the DateTimeCol '%s', "1246"got %s %r instead"%(1247self.name,type(value),value),value,state)12481249ifmxdatetime_available:1250classMXDateTimeValidator(validators.DateValidator):1251defto_python(self,value,state):1252ifvalueisNone:1253returnNone1254ifisinstance(value,1255(DateTimeType,TimeType,sqlbuilder.SQLExpression)):1256returnvalue1257ifisinstance(value,datetime.datetime):1258returnDateTime.DateTime(value.year,value.month,value.day,1259value.hour,value.minute,1260value.second)1261elifisinstance(value,datetime.date):1262returnDateTime.Date(value.year,value.month,value.day)1263elifisinstance(value,datetime.time):1264returnDateTime.Time(value.hour,value.minute,value.second)1265try:1266ifself.format.find(".%f")>=0:1267if'.'invalue:1268_value=value.split('.')1269microseconds=_value[-1]1270_l=len(microseconds)1271if_l<6:1272_value[-1]=microseconds+'0'*(6-_l)1273elif_l>6:1274_value[-1]=microseconds[:6]1275if_l!=6:1276value='.'.join(_value)1277else:1278value+='.0'1279value=datetime.datetime.strptime(value,self.format)1280returnDateTime.DateTime(value.year,value.month,value.day,1281value.hour,value.minute,1282value.second)1283except:1284raisevalidators.Invalid(1285"expected a date/time string of the '%s' format "1286"in the DateTimeCol '%s', got %s %r instead"%(1287self.format,self.name,type(value),value),1288value,state)12891290deffrom_python(self,value,state):1291ifvalueisNone:1292returnNone1293ifisinstance(value,1294(DateTimeType,TimeType,sqlbuilder.SQLExpression)):1295returnvalue1296ifhasattr(value,"strftime"):1297returnvalue.strftime(self.format)1298raisevalidators.Invalid(1299"expected a mxDateTime in the DateTimeCol '%s', "1300"got %s %r instead"%(1301self.name,type(value),value),value,state)130213031304classSODateTimeCol(SOCol):1305datetimeFormat='%Y-%m-%d %H:%M:%S.%f'13061307def__init__(self,**kw):1308datetimeFormat=kw.pop('datetimeFormat',None)1309ifdatetimeFormat:1310self.datetimeFormat=datetimeFormat1311super(SODateTimeCol,self).__init__(**kw)13121313defcreateValidators(self):1314_validators=super(SODateTimeCol,self).createValidators()1315ifdefault_datetime_implementation==DATETIME_IMPLEMENTATION:1316validatorClass=DateTimeValidator1317elifdefault_datetime_implementation==MXDATETIME_IMPLEMENTATION:1318validatorClass=MXDateTimeValidator1319ifdefault_datetime_implementation:1320_validators.insert(0,validatorClass(name=self.name,1321format=self.datetimeFormat))1322return_validators13231324def_mysqlType(self):1325ifself.connectionandself.connection.can_use_microseconds():1326return'DATETIME(6)'1327else:1328return'DATETIME'13291330def_postgresType(self):1331return'TIMESTAMP'13321333def_sybaseType(self):1334return'DATETIME'13351336def_mssqlType(self):1337ifself.connectionandself.connection.can_use_microseconds():1338return'DATETIME2(6)'1339else:1340return'DATETIME'13411342def_sqliteType(self):1343return'TIMESTAMP'13441345def_firebirdType(self):1346return'TIMESTAMP'13471348def_maxdbType(self):1349return'TIMESTAMP'135013511352classDateTimeCol(Col):1353baseClass=SODateTimeCol13541355@staticmethod1356defnow():1357ifdefault_datetime_implementation==DATETIME_IMPLEMENTATION:1358returndatetime.datetime.now()1359elifdefault_datetime_implementation==MXDATETIME_IMPLEMENTATION:1360returnDateTime.now()1361else:1362assert0,("No datetime implementation available "1363"(DATETIME_IMPLEMENTATION=%r)"1364%DATETIME_IMPLEMENTATION)136513661367classDateValidator(DateTimeValidator):1368defto_python(self,value,state):1369ifisinstance(value,datetime.datetime):1370value=value.date()1371ifisinstance(value,(datetime.date,sqlbuilder.SQLExpression)):1372returnvalue1373value=super(DateValidator,self).to_python(value,state)1374ifisinstance(value,datetime.datetime):1375value=value.date()1376returnvalue13771378from_python=to_python137913801381classSODateCol(SOCol):1382dateFormat='%Y-%m-%d'13831384def__init__(self,**kw):1385dateFormat=kw.pop('dateFormat',None)1386ifdateFormat:1387self.dateFormat=dateFormat1388super(SODateCol,self).__init__(**kw)13891390defcreateValidators(self):1391"""Create a validator for the column.13921393 Can be overriden in descendants.13941395 """1396_validators=super(SODateCol,self).createValidators()1397ifdefault_datetime_implementation==DATETIME_IMPLEMENTATION:1398validatorClass=DateValidator1399elifdefault_datetime_implementation==MXDATETIME_IMPLEMENTATION:1400validatorClass=MXDateTimeValidator1401ifdefault_datetime_implementation:1402_validators.insert(0,validatorClass(name=self.name,1403format=self.dateFormat))1404return_validators14051406def_mysqlType(self):1407return'DATE'14081409def_postgresType(self):1410return'DATE'14111412def_sybaseType(self):1413returnself._postgresType()14141415def_mssqlType(self):1416"""1417 SQL Server doesn't have a DATE data type, to emulate we use a vc(10)1418 """1419return'VARCHAR(10)'14201421def_firebirdType(self):1422return'DATE'14231424def_maxdbType(self):1425return'DATE'14261427def_sqliteType(self):1428return'DATE'142914301431classDateCol(Col):1432baseClass=SODateCol143314341435classTimeValidator(DateTimeValidator):1436defto_python(self,value,state):1437ifisinstance(value,(datetime.time,sqlbuilder.SQLExpression)):1438returnvalue1439ifisinstance(value,datetime.timedelta):1440ifvalue.days:1441raisevalidators.Invalid(1442"the value for the TimeCol '%s' must has days=0, "1443"it has days=%d"%(self.name,value.days),value,state)1444returndatetime.time(*time.gmtime(value.seconds)[3:6])1445value=super(TimeValidator,self).to_python(value,state)1446ifisinstance(value,datetime.datetime):1447value=value.time()1448returnvalue14491450from_python=to_python145114521453classSOTimeCol(SOCol):1454timeFormat='%H:%M:%S.%f'14551456def__init__(self,**kw):1457timeFormat=kw.pop('timeFormat',None)1458iftimeFormat:1459self.timeFormat=timeFormat1460super(SOTimeCol,self).__init__(**kw)14611462defcreateValidators(self):1463_validators=super(SOTimeCol,self).createValidators()1464ifdefault_datetime_implementation==DATETIME_IMPLEMENTATION:1465validatorClass=TimeValidator1466elifdefault_datetime_implementation==MXDATETIME_IMPLEMENTATION:1467validatorClass=MXDateTimeValidator1468ifdefault_datetime_implementation:1469_validators.insert(0,validatorClass(name=self.name,1470format=self.timeFormat))1471return_validators14721473def_mysqlType(self):1474ifself.connectionandself.connection.can_use_microseconds():1475return'TIME(6)'1476else:1477return'TIME'14781479def_postgresType(self):1480return'TIME'14811482def_sybaseType(self):1483return'TIME'14841485def_mssqlType(self):1486ifself.connectionandself.connection.can_use_microseconds():1487return'TIME(6)'1488else:1489return'TIME'14901491def_sqliteType(self):1492return'TIME'14931494def_firebirdType(self):1495return'TIME'14961497def_maxdbType(self):1498return'TIME'149915001501classTimeCol(Col):1502baseClass=SOTimeCol150315041505classSOTimestampCol(SODateTimeCol):1506"""1507 Necessary to support MySQL's use of TIMESTAMP versus DATETIME types1508 """15091510def__init__(self,**kw):1511if'default'notinkw:1512kw['default']=None1513SOCol.__init__(self,**kw)15141515def_mysqlType(self):1516ifself.connectionandself.connection.can_use_microseconds():1517return'TIMESTAMP(6)'1518else:1519return'TIMESTAMP'152015211522classTimestampCol(Col):1523baseClass=SOTimestampCol152415251526classTimedeltaValidator(SOValidator):1527defto_python(self,value,state):1528returnvalue15291530from_python=to_python153115321533classSOTimedeltaCol(SOCol):1534def_postgresType(self):1535return'INTERVAL'15361537defcreateValidators(self):1538return[TimedeltaValidator(name=self.name)]+super(SOTimedeltaCol,self).createValidators()154015411542classTimedeltaCol(Col):1543baseClass=SOTimedeltaCol154415451546classDecimalValidator(SOValidator):1547defto_python(self,value,state):1548ifvalueisNone:1549returnNone1550ifisinstance(value,(int,long,Decimal,sqlbuilder.SQLExpression)):1551returnvalue1552ifisinstance(value,float):1553value=str(value)1554try:1555connection=state.connectionorstate.soObject._connection1556exceptAttributeError:1557pass1558else:1559ifhasattr(connection,"decimalSeparator"):1560value=value.replace(connection.decimalSeparator,".")1561try:1562returnDecimal(value)1563except:1564raisevalidators.Invalid(1565"expected a Decimal in the DecimalCol '%s', "1566"got %s %r instead"%(1567self.name,type(value),value),value,state)15681569deffrom_python(self,value,state):1570ifvalueisNone:1571returnNone1572ifisinstance(value,float):1573value=str(value)1574ifisinstance(value,string_type):1575try:1576connection=state.connectionorstate.soObject._connection1577exceptAttributeError:1578pass1579else:1580ifhasattr(connection,"decimalSeparator"):1581value=value.replace(connection.decimalSeparator,".")1582try:1583returnDecimal(value)1584except:1585raisevalidators.Invalid(1586"can not parse Decimal value '%s' "1587"in the DecimalCol from '%s'"%(1588value,getattr(state,'soObject','(unknown)')),1589value,state)1590ifisinstance(value,(int,long,Decimal,sqlbuilder.SQLExpression)):1591returnvalue1592raisevalidators.Invalid(1593"expected a Decimal in the DecimalCol '%s', got %s %r instead"%(1594self.name,type(value),value),value,state)159515961597classSODecimalCol(SOCol):15981599def__init__(self,**kw):1600self.size=kw.pop('size',NoDefault)1601assertself.sizeisnotNoDefault,"You must give a size argument"1603self.precision=kw.pop('precision',NoDefault)1604assertself.precisionisnotNoDefault,"You must give a precision argument"1606super(SODecimalCol,self).__init__(**kw)16071608def_sqlType(self):1609return'DECIMAL(%i, %i)'%(self.size,self.precision)16101611defcreateValidators(self):1612return[DecimalValidator(name=self.name)]+super(SODecimalCol,self).createValidators()161416151616classDecimalCol(Col):1617baseClass=SODecimalCol161816191620classSOCurrencyCol(SODecimalCol):16211622def__init__(self,**kw):1623pushKey(kw,'size',10)1624pushKey(kw,'precision',2)1625super(SOCurrencyCol,self).__init__(**kw)162616271628classCurrencyCol(DecimalCol):1629baseClass=SOCurrencyCol163016311632classDecimalStringValidator(DecimalValidator):1633defto_python(self,value,state):1634value=super(DecimalStringValidator,self).to_python(value,state)1635ifself.precisionandisinstance(value,Decimal):1636assertvalue<self.max,"Value must be less than %s"%int(self.max)1638value=value.quantize(self.precision)1639returnvalue16401641deffrom_python(self,value,state):1642value=super(DecimalStringValidator,self).from_python(value,state)1643ifisinstance(value,Decimal):1644ifself.precision:1645assertvalue<self.max,"Value must be less than %s"%int(self.max)1647value=value.quantize(self.precision)1648value=value.to_eng_string()1649elifisinstance(value,(int,long)):1650value=str(value)1651returnvalue165216531654classSODecimalStringCol(SOStringCol):1655def__init__(self,**kw):1656self.size=kw.pop('size',NoDefault)1657assert(self.sizeisnotNoDefault)and(self.size>=0),"You must give a size argument as a positive integer"1659self.precision=kw.pop('precision',NoDefault)1660assert(self.precisionisnotNoDefault)and(self.precision>=0),"You must give a precision argument as a positive integer"1662kw['length']=int(self.size)+int(self.precision)1663self.quantize=kw.pop('quantize',False)1664assertisinstance(self.quantize,bool),"quantize argument must be Boolean True/False"1666super(SODecimalStringCol,self).__init__(**kw)16671668defcreateValidators(self):1669ifself.quantize:1670v=DecimalStringValidator(1671name=self.name,1672precision=Decimal(10)**(-1*int(self.precision)),1673max=Decimal(10)**(int(self.size)-int(self.precision)))1674else:1675v=DecimalStringValidator(name=self.name,precision=0)1676return[v]+super(SODecimalStringCol,self).createValidators(dataType=Decimal)167816791680classDecimalStringCol(StringCol):1681baseClass=SODecimalStringCol168216831684classBinaryValidator(SOValidator):1685"""1686 Validator for binary types.16871688 We're assuming that the per-database modules provide some form1689 of wrapper type for binary conversion.1690 """16911692_cachedValue=None16931694defto_python(self,value,state):1695ifvalueisNone:1696returnNone1697try:1698connection=state.connectionorstate.soObject._connection1699exceptAttributeError:1700dbName=None1701binaryType=type(None)# Just a simple workaround1702else:1703dbName=connection.dbName1704binaryType=connection._binaryType1705ifisinstance(value,str):1706ifdbName=="sqlite":1707ifnotPY2:1708value=bytes(value,'ascii')1709value=connection.module.decode(value)1710ifdbName=="mysql"andnotPY2:1711value=value.encode('ascii',errors='surrogateescape')1712returnvalue1713ifisinstance(value,(buffer_type,binaryType)):1714cachedValue=self._cachedValue1715ifcachedValueandcachedValue[1]==value:1716returncachedValue[0]1717ifisinstance(value,array):# MySQL1718returnvalue.tostring()1719ifnotPY2andisinstance(value,memoryview):1720returnvalue.tobytes()1721returnstr(value)# buffer => string1722raisevalidators.Invalid(1723"expected a string in the BLOBCol '%s', got %s %r instead"%(1724self.name,type(value),value),value,state)17251726deffrom_python(self,value,state):1727ifvalueisNone:1728returnNone1729connection=state.connectionorstate.soObject._connection1730binary=connection.createBinary(value)1731ifnotPY2andisinstance(binary,memoryview):1732binary=str(binary.tobytes(),'ascii')1733self._cachedValue=(value,binary)1734returnbinary173517361737classSOBLOBCol(SOStringCol):1738def__init__(self,**kw):1739# Change the default from 'auto' to False -1740# this is a (mostly) binary column1741if'varchar'notinkw:1742kw['varchar']=False1743super(SOBLOBCol,self).__init__(**kw)17441745defcreateValidators(self):1746return[BinaryValidator(name=self.name)]+super(SOBLOBCol,self).createValidators()17481749def_mysqlType(self):1750length=self.length1751varchar=self.varchar1752iflength:1753iflength>=2**24:1754returnvarcharand"LONGTEXT"or"LONGBLOB"1755iflength>=2**16:1756returnvarcharand"MEDIUMTEXT"or"MEDIUMBLOB"1757iflength>=2**8:1758returnvarcharand"TEXT"or"BLOB"1759returnvarcharand"TINYTEXT"or"TINYBLOB"17601761def_postgresType(self):1762return'BYTEA'17631764def_mssqlType(self):1765ifself.connectionandself.connection.can_use_max_types():1766return'VARBINARY(MAX)'1767else:1768return"IMAGE"176917701771classBLOBCol(StringCol):1772baseClass=SOBLOBCol177317741775classPickleValidator(BinaryValidator):1776"""1777 Validator for pickle types. A pickle type is simply a binary type1778 with hidden pickling, so that we can simply store any kind of1779 stuff in a particular column.17801781 The support for this relies directly on the support for binary for1782 your database.1783 """17841785defto_python(self,value,state):1786ifvalueisNone:1787returnNone1788ifisinstance(value,unicode_type):1789dbEncoding=self.getDbEncoding(state,default='ascii')1790value=value.encode(dbEncoding)1791ifisinstance(value,bytes):1792returnpickle.loads(value)1793raisevalidators.Invalid(1794"expected a pickle string in the PickleCol '%s', "1795"got %s %r instead"%(1796self.name,type(value),value),value,state)17971798deffrom_python(self,value,state):1799ifvalueisNone:1800returnNone1801returnpickle.dumps(value,self.pickleProtocol)180218031804classSOPickleCol(SOBLOBCol):18051806def__init__(self,**kw):1807self.pickleProtocol=kw.pop('pickleProtocol',pickle.HIGHEST_PROTOCOL)1808super(SOPickleCol,self).__init__(**kw)18091810defcreateValidators(self):1811return[PickleValidator(name=self.name,1812pickleProtocol=self.pickleProtocol)]+super(SOPickleCol,self).createValidators()18141815def_mysqlType(self):1816length=self.length1817iflength:1818iflength>=2**24:1819return"LONGBLOB"1820iflength>=2**16:1821return"MEDIUMBLOB"1822return"BLOB"182318241825classPickleCol(BLOBCol):1826baseClass=SOPickleCol182718281829defpushKey(kw,name,value):1830ifnamenotinkw:1831kw[name]=value18321833all=[]1834# Use copy() to avoid 'dictionary changed' issues on python 31835forkey,valueinglobals().copy().items():1836ifisinstance(value,type)and(issubclass(value,(Col,SOCol))):1837all.append(key)1838__all__.extend(all)1839delall