Package gavo :: Package registry :: Module tableset
[frames] | no frames]

Source Code for Module gavo.registry.tableset

  1  """ 
  2  Generation of VODataService 1.1 tablesets from resources. 
  3   
  4  Fudge note: sprinkled in below are lots of lower()s for column names and the 
  5  like.  These were added for the convenience of TAP clients that may 
  6  want to use these names quoted.  Quoted identifiers match regular identifiers 
  7  only if case-normalized (i.e., all-lower in DaCHS). 
  8  """ 
  9   
 10  #c Copyright 2008-2019, the GAVO project 
 11  #c 
 12  #c This program is free software, covered by the GNU GPL.  See the 
 13  #c COPYING file in the source distribution. 
 14   
 15   
 16  import itertools 
 17   
 18  from gavo import base 
 19  from gavo import rscdef 
 20  from gavo import svcs 
 21  from gavo import utils 
 22  from gavo.registry.model import VS 
 23   
 24   
 25  _VOTABLE_TO_SIMPLE_TYPE_MAP = { 
 26          "char": "char", 
 27          "bytea": "char", 
 28          "unicodeChar": "char", 
 29          "short": "integer", 
 30          "int": "integer", 
 31          "long": "integer", 
 32          "float": "real", 
 33          "double": "real", 
 34  } 
 35   
36 -def simpleDataTypeFactory(dbType, declaredXtype=None):
37 type, length, xtype = base.sqltypeToVOTable(dbType) 38 if length=='1': 39 length = None 40 return VS.dataType(arraysize=length)[ 41 _VOTABLE_TO_SIMPLE_TYPE_MAP.get(type, "char")]
42 43
44 -def voTableDataTypeFactory(dbType, declaredXtype=None):
45 type, length, xtype = base.sqltypeToVOTable(dbType) 46 # the following hack is mirrored in //tap; any similar hacks 47 # would have to go there, too. 48 if declaredXtype=="adql:REGION": 49 type, length, xtype = "char", "*", "adql:REGION" 50 return VS.voTableDataType(arraysize=length)[type](extendedType=xtype)
51 52
53 -def getSchemaForRD(rd):
54 """returns a VS.schema instance for an rd. 55 56 No tables are added. You need to pick and choose them yourself. 57 """ 58 return VS.schema[ 59 VS.name[rd.schema.lower()], 60 VS.title[base.getMetaText(rd, "title")], 61 VS.description[base.getMetaText(rd, "description")], 62 ]
63 64
65 -def getForeignKeyForForeignKey(fk, namesInSet):
66 """returns a VS.foreignKey for a rscdef.ForeignKey. 67 68 If the target table's name is not in nameInSet, the foreign key 69 is not created. 70 """ 71 targetName = fk.destTableName 72 if targetName not in namesInSet: 73 return None 74 75 return VS.foreignKey[ 76 VS.targetTable[targetName], [ 77 VS.fkColumn[ 78 VS.fromColumn[fromColName.lower()], 79 VS.targetColumn[toColName.lower()]] 80 for fromColName,toColName in zip(fk.source, fk.dest)]]
81 82
83 -def getTableColumnFromColumn(column, typeFactory):
84 """returns a VS.column instance for an rscdef.Column instance. 85 86 typeElement is a factory for types that has to accept an internal (SQL) 87 type as child and generate whatever is necessary from that. 88 89 This module contains simpleDataTypeFactory, voTableDataTypeFactory, 90 and should soon contain tapTypeFactory. 91 """ 92 if isinstance(column.name, utils.QuotedName): 93 colName = str(column.name) 94 else: 95 colName = column.name.lower() 96 97 flags = [] 98 if column.isIndexed(): 99 flags.append("indexed") 100 if column.isPrimary(): 101 flags.append("primary") 102 elif not column.required: 103 flags.append("nullable") 104 105 return VS.column[ 106 VS.name[colName], 107 VS.description[column.description], 108 VS.unit[column.unit], 109 VS.ucd[column.ucd], 110 VS.utype[column.utype], 111 typeFactory(column.type, column.xtype), 112 [VS.flag[f] for f in flags]]
113 114
115 -def getEffectiveTableName(tableDef):
116 """returns the "effective name" of tableDef. 117 118 This is mainly for fudging the names of output tables since, 119 by default, they're ugly (and meaningless on top of that). 120 """ 121 if isinstance(tableDef, svcs.OutputTableDef): 122 return "output" 123 else: 124 return tableDef.getQName().lower()
125 126
127 -def getTableForTableDef( 128 tableDef, namesInSet, rootElement=VS.table, suppressBodies=False):
129 """returns a VS.table instance for a rscdef.TableDef. 130 131 namesInSet is a set of lowercased qualified table names; we need this 132 to figure out which foreign keys to create. 133 """ 134 name = getEffectiveTableName(tableDef) 135 136 # Fake type=output on the basis of the table name. We'll have 137 # to do something sensible here if this "type" thing ever becomes 138 # more meaningful. 139 type = None 140 if name=="output": 141 type = "output" 142 143 res = rootElement(type=type)[ 144 VS.name[name], 145 VS.title[base.getMetaText(tableDef, "title", propagate=False)], 146 VS.description[base.getMetaText(tableDef, "description", propagate=True)], 147 VS.utype[base.getMetaText(tableDef, "utype")]] 148 149 if not suppressBodies: 150 res[[ 151 getTableColumnFromColumn(col, voTableDataTypeFactory) 152 for col in tableDef if not col.hidden], [ 153 getForeignKeyForForeignKey(fk, namesInSet) 154 for fk in tableDef.foreignKeys]] 155 156 return res
157 158
159 -def getTablesetForSchemaCollection( 160 schemas, rootElement=VS.tableset, suppressBodies=False):
161 """returns a vs:tableset element from a sequence of (rd, tables) pairs. 162 163 In each pair, rd is used to define a VODataService schema, and tables is 164 a sequence of TableDefs that define the tables within that schema. 165 """ 166 # we don't want to report foreign keys into tables not part of the 167 # service's tableset (this is for consistency with TAP_SCHEMA, 168 # mainly). Hence, we collect the table names given. 169 namesInSet = set(getEffectiveTableName(td).lower() 170 for td in itertools.chain(*(tables for rd, tables in schemas))) 171 172 res = rootElement() 173 for rd, tables in schemas: 174 res[VS.schema[ 175 VS.name[rd.schema], 176 VS.title[base.getMetaText(rd, "title")], 177 VS.description[base.getMetaText(rd, "description")], 178 VS.utype[base.getMetaText(rd, "utype", None)], 179 [getTableForTableDef(td, namesInSet, suppressBodies=suppressBodies) 180 for td in tables]]] 181 return res
182 183
184 -def getTablesetForService( 185 resource, rootElement=VS.tableset, suppressBodies=False):
186 """returns a VS.tableset for a service or a published data resource. 187 188 This is for VOSI queries and the generation of registry records. 189 Where it actually works on services, it uses the service's getTableset 190 method to find out the service's table set; if it's passed a TableDef 191 of a DataDescriptor, it will turn these into tablesets. 192 193 Sorry about the name. 194 """ 195 if isinstance(resource, rscdef.TableDef): 196 tables = [resource] 197 198 elif isinstance(resource, rscdef.DataDescriptor): 199 tables = list(resource.iterTableDefs()) 200 201 else: 202 tables = resource.getTableSet() 203 204 if not tables: 205 return rootElement[ 206 VS.schema[ 207 VS.name["default"]]] 208 209 # it's possible that multiple RDs define the same schema (don't do 210 # that, it's going to cause all kinds of pain). To avoid 211 # generating bad tablesets in that case, we have the separate 212 # account of schema names; the schema meta is random when 213 # more than one RD exists for the schema. 214 bySchema, rdForSchema = {}, {} 215 for t in tables: 216 bySchema.setdefault(t.rd.schema, []).append(t) 217 rdForSchema[t.rd.schema] = t.rd 218 219 schemas = [] 220 for schemaName, tables in sorted(bySchema.iteritems()): 221 schemas.append((rdForSchema[schemaName], tables)) 222 223 return getTablesetForSchemaCollection( 224 schemas, 225 rootElement, 226 suppressBodies=suppressBodies)
227