[web/views] Controllers should always return bytes
Previous code breaks only on py3k.
# copyright 2004-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr## This file is part of cubicweb.## yams is free software: you can redistribute it and/or modify it under the# terms of the GNU Lesser General Public License as published by the Free# Software Foundation, either version 2.1 of the License, or (at your option)# any later version.## yams is distributed in the hope that it will be useful, but WITHOUT ANY# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more# details.## You should have received a copy of the GNU Lesser General Public License along# with yams. If not, see <http://www.gnu.org/licenses/>."""write a schema as sql"""__docformat__="restructuredtext en"fromhashlibimportmd5fromsiximportstring_typesfromsix.movesimportrangefromyams.constraintsimport(SizeConstraint,UniqueConstraint,Attribute,NOW,TODAY)# default are usually not handled at the sql level. If you want them, set# SET_DEFAULT to TrueSET_DEFAULT=Falsedefrschema_has_table(rschema,skip_relations):"""Return True if the given schema should have a table in the database"""returnnot(rschema.finalorrschema.inlinedorrschema.ruleorrschema.typeinskip_relations)defschema2sql(dbhelper,schema,skip_entities=(),skip_relations=(),prefix=''):"""write to the output stream a SQL schema to store the objects corresponding to the given schema """output=[]w=output.appendforetypeinsorted(schema.entities()):eschema=schema.eschema(etype)ifeschema.finaloreschema.typeinskip_entities:continuew(eschema2sql(dbhelper,eschema,skip_relations,prefix=prefix))forrtypeinsorted(schema.relations()):rschema=schema.rschema(rtype)ifrschema_has_table(rschema,skip_relations):w(rschema2sql(rschema))return'\n'.join(output)defdropschema2sql(dbhelper,schema,skip_entities=(),skip_relations=(),prefix=''):"""write to the output stream a SQL schema to store the objects corresponding to the given schema """output=[]w=output.appendforetypeinsorted(schema.entities()):eschema=schema.eschema(etype)ifeschema.finaloreschema.typeinskip_entities:continuestmts=dropeschema2sql(dbhelper,eschema,skip_relations,prefix=prefix)forstmtinstmts:w(stmt)forrtypeinsorted(schema.relations()):rschema=schema.rschema(rtype)ifrschema_has_table(rschema,skip_relations):w(droprschema2sql(rschema))return'\n'.join(output)defeschema_attrs(eschema,skip_relations):attrs=[attrdefforattrdefineschema.attribute_definitions()ifnotattrdef[0].typeinskip_relations]attrs+=[(rschema,None)forrschemaineschema.subject_relations()ifnotrschema.finalandrschema.inlined]returnattrsdefunique_index_name(eschema,columns):returnu'unique_%s'%md5((eschema.type+','+','.join(sorted(columns))).encode('ascii')).hexdigest()defiter_unique_index_names(eschema):forcolumnsineschema._unique_togetheror():yieldcolumns,unique_index_name(eschema,columns)defdropeschema2sql(dbhelper,eschema,skip_relations=(),prefix=''):"""return sql to drop an entity type's table"""# not necessary to drop indexes, that's implictly done when# dropping the table, but we need to drop SQLServer views used to# create multicol unique indicesstatements=[]tablename=prefix+eschema.typeifeschema._unique_togetherisnotNone:forcolumns,index_nameiniter_unique_index_names(eschema):cols=['%s%s'%(prefix,col)forcolincolumns]sqls=dbhelper.sqls_drop_multicol_unique_index(tablename,cols,index_name)statements+=sqlsstatements+=['DROP TABLE %s;'%(tablename)]returnstatementsdefeschema2sql(dbhelper,eschema,skip_relations=(),prefix=''):"""write an entity schema as SQL statements to stdout"""output=[]w=output.appendtable=prefix+eschema.typew('CREATE TABLE %s('%(table))attrs=eschema_attrs(eschema,skip_relations)# XXX handle objectinline physical modeforiinrange(len(attrs)):rschema,attrschema=attrs[i]ifattrschemaisnotNone:sqltype=aschema2sql(dbhelper,eschema,rschema,attrschema,indent=' ')else:# inline relationsqltype='integer REFERENCES entities (eid)'ifi==len(attrs)-1:w(' %s%s%s'%(prefix,rschema.type,sqltype))else:w(' %s%s%s,'%(prefix,rschema.type,sqltype))forrschema,aschemainattrs:ifaschemaisNone:# inline relationcontinueattr=rschema.typerdef=rschema.rdef(eschema.type,aschema.type)forconstraintinrdef.constraints:cstrname,check=check_constraint(eschema,aschema,attr,constraint,dbhelper,prefix=prefix)ifcstrnameisnotNone:w(', CONSTRAINT %s CHECK(%s)'%(cstrname,check))w(');')# create indexesforiinrange(len(attrs)):rschema,attrschema=attrs[i]ifattrschemaisNoneoreschema.rdef(rschema).indexed:w(dbhelper.sql_create_index(table,prefix+rschema.type))forcolumns,index_nameiniter_unique_index_names(eschema):cols=['%s%s'%(prefix,col)forcolincolumns]sqls=dbhelper.sqls_create_multicol_unique_index(table,cols,index_name)forsqlinsqls:w(sql)w('')return'\n'.join(output)defas_sql(value,dbhelper,prefix):ifisinstance(value,Attribute):returnprefix+value.attrelifisinstance(value,TODAY):returndbhelper.sql_current_date()elifisinstance(value,NOW):returndbhelper.sql_current_timestamp()else:# XXX more quoting for literals?returnvaluedefcheck_constraint(eschema,aschema,attr,constraint,dbhelper,prefix=''):# XXX should find a better namecstrname='cstr'+md5((eschema.type+attr+constraint.type()+(constraint.serialize()or'')).encode('ascii')).hexdigest()ifconstraint.type()=='BoundaryConstraint':value=as_sql(constraint.boundary,dbhelper,prefix)returncstrname,'%s%s%s%s'%(prefix,attr,constraint.operator,value)elifconstraint.type()=='IntervalBoundConstraint':condition=[]ifconstraint.minvalueisnotNone:value=as_sql(constraint.minvalue,dbhelper,prefix)condition.append('%s%s >= %s'%(prefix,attr,value))ifconstraint.maxvalueisnotNone:value=as_sql(constraint.maxvalue,dbhelper,prefix)condition.append('%s%s <= %s'%(prefix,attr,value))returncstrname,' AND '.join(condition)elifconstraint.type()=='StaticVocabularyConstraint':sample=next(iter(constraint.vocabulary()))ifnotisinstance(sample,string_types):values=', '.join(str(word)forwordinconstraint.vocabulary())else:# XXX better quoting?values=', '.join("'%s'"%word.replace("'","''")forwordinconstraint.vocabulary())returncstrname,'%s%s IN (%s)'%(prefix,attr,values)returnNone,Nonedefaschema2sql(dbhelper,eschema,rschema,aschema,creating=True,indent=''):"""write an attribute schema as SQL statements to stdout"""attr=rschema.typerdef=rschema.rdef(eschema.type,aschema.type)sqltype=type_from_constraints(dbhelper,aschema.type,rdef.constraints,creating)ifSET_DEFAULT:default=eschema.default(attr)ifdefaultisnotNone:ifaschema.type=='Boolean':sqltype+=' DEFAULT %s'%dbhelper.boolean_value(default)elifaschema.type=='String':sqltype+=' DEFAULT %r'%str(default)elifaschema.typein('Int','BigInt','Float'):sqltype+=' DEFAULT %s'%default# XXX ignore default for other type# this is expected for NOW / TODAYifcreating:ifrdef.uid:sqltype+=' PRIMARY KEY REFERENCES entities (eid)'elifrdef.cardinality[0]=='1':# don't set NOT NULL if backend isn't able to change it laterifdbhelper.alter_column_support:sqltype+=' NOT NULL'# else we're getting sql type to alter a column, we don't want key / indexes# / null modifiersreturnsqltypedeftype_from_constraints(dbhelper,etype,constraints,creating=True):"""return a sql type string corresponding to the constraints"""constraints=list(constraints)unique,sqltype=False,Nonesize_constrained_string=dbhelper.TYPE_MAPPING.get('SizeConstrainedString','varchar(%s)')ifetype=='String':forconstraintinconstraints:ifisinstance(constraint,SizeConstraint):ifconstraint.maxisnotNone:sqltype=size_constrained_string%constraint.maxelifisinstance(constraint,UniqueConstraint):unique=TrueifsqltypeisNone:sqltype=dbhelper.TYPE_MAPPING[etype]ifcreatingandunique:sqltype+=' UNIQUE'returnsqltype_SQL_SCHEMA="""CREATE TABLE %(table)s ( eid_from INTEGER NOT NULL REFERENCES entities (eid), eid_to INTEGER NOT NULL REFERENCES entities (eid), CONSTRAINT %(table)s_p_key PRIMARY KEY(eid_from, eid_to));CREATE INDEX %(table)s_from_idx ON %(table)s(eid_from);CREATE INDEX %(table)s_to_idx ON %(table)s(eid_to);"""defrschema2sql(rschema):assertnotrschema.rulereturn_SQL_SCHEMA%{'table':'%s_relation'%rschema.type}defdroprschema2sql(rschema):"""return sql to drop a relation type's table"""# not necessary to drop indexes, that's implictly done when dropping# the tablereturn'DROP TABLE %s_relation;'%rschema.typedefgrant_schema(schema,user,set_owner=True,skip_entities=(),prefix=''):"""write to the output stream a SQL schema to store the objects corresponding to the given schema """output=[]w=output.appendforetypeinsorted(schema.entities()):eschema=schema.eschema(etype)ifeschema.finaloretypeinskip_entities:continuew(grant_eschema(eschema,user,set_owner,prefix=prefix))forrtypeinsorted(schema.relations()):rschema=schema.rschema(rtype)ifrschema_has_table(rschema,skip_relations=()):# XXX skip_relations should be specifiedw(grant_rschema(rschema,user,set_owner))return'\n'.join(output)defgrant_eschema(eschema,user,set_owner=True,prefix=''):output=[]w=output.appendetype=eschema.typeifset_owner:w('ALTER TABLE %s%s OWNER TO %s;'%(prefix,etype,user))w('GRANT ALL ON %s%s TO %s;'%(prefix,etype,user))return'\n'.join(output)defgrant_rschema(rschema,user,set_owner=True):output=[]ifset_owner:output.append('ALTER TABLE %s_relation OWNER TO %s;'%(rschema.type,user))output.append('GRANT ALL ON %s_relation TO %s;'%(rschema.type,user))return'\n'.join(output)