1 """
2 Conversions between type systems.
3
4 The DC software has to deal with a quite a few type systems (see
5 base.typesystems). In general, we keep metadata in the SQL type system;
6 in particular, column's and param's type attribute takes values in that.
7
8 In fact, we use a couple of extensions:
9
10 - file -- this corresponds to a file upload from the web (i.e., a pair
11 (filename, file object)). It would be conceivable to turn this into
12 blobs at some point, but right now we simply don't touch it.
13 - vexpr-float, -text, -date, -mjd -- vizier-like expressions coming in from
14 the web. These are always strings.
15 - raw -- handed right through, whatever it is. For target formats that
16 can't do this, usually strings are used.
17 - unicode -- this is TEXT in the database, but while normal text will
18 be rendered as byte strings in VOTables (with non-ASCII-characters
19 replaced by ?), unicode will become an array of unicodeChars.
20
21 This module contains a base class and the VOTable type system conversion,
22 as the VOTable module (that should not depend on base) depends on it.
23 The remaining actual converters are in base.typesystems, as they may depend
24 on details of base. Even the SQL converters should be taken from there
25 when code can rely on gavo.base; this module should be considered an
26 implementation detail.
27 """
28
29
30
31
32
33
34
35
36
37
38 import re
39
40 from gavo.utils import excs
41
42
45
46
48 """is an abstract base class for type converters from the SQL type system.
49
50 Implementing classes have to provide a dict simpleMap mapping sql type
51 strings to target types, and a method mapComplex that receives a type
52 and a length (both strings, derived from SQL array types) and either
53 returns None (no matching type) or the target type.
54
55 Implementing classes should also provide a typeSystem attribute giving
56 a short name of the type system they convert to.
57 """
58 _charTypes = set(["character varying", "varchar", "character", "char"])
59
73
76
77
79 typeSystem = "VOTable"
80
81 simpleMap = {
82 "smallint": ("short", None, None),
83 "integer": ("int", None, None),
84 "bigint": ("long", None, None),
85 "real": ("float", None, None),
86 "boolean": ("boolean", None, None),
87 "double precision": ("double", None, None),
88 "text": ("char", "*", None),
89 "char": ("char", "1", None),
90 "date": ("char", "*", None),
91 "timestamp": ("char", "19", "timestamp"),
92 "time": ("char", "*", None),
93 "box": ("double", "*", None),
94 "vexpr-mjd": ("char", "*", None),
95 "vexpr-string": ("char", "*", None),
96 "vexpr-date": ("char", "*", None),
97 "vexpr-float": ("char", "*", None),
98 "file": ("bytea", "*", None),
99 "pql-float": ("char", "*", None),
100 "pql-string": ("char", "*", None),
101 "pql-date": ("char", "*", None),
102 "pql-int": ("char", "*", None),
103 "pql-upload": ("char", "*", None),
104 "raw": ("unsignedByte", "*", None),
105 "bytea": ("unsignedByte", None, None),
106 "spoint": ("double", "2", "point"),
107 "scircle": ("double", "3", "circle"),
108 "spoly": ("double", "*", "polygon"),
109 "smoc": ("char", "*", "moc"),
110 "sbox": ("double", "4", "x:box"),
111 "unicode": ("unicodeChar", "*", None),
112 "int4range": ("int", "2", "interval"),
113 }
114
116 if length=='':
117 length = '*'
118
119 if type in self._charTypes:
120 return "char", length, None
121
122
123 if type=="char" and length=='1':
124 length = None
125
126 if length is not None:
127
128 if type=="bytea":
129 return ("unsignedByte", '*', None)
130
131
132
133 t, l, xtype = self.simpleMap[type]
134 if l is None:
135 newLength = length
136 else:
137 newLength = "%sx%s"%(l, length)
138
139 return (t, newLength, xtype)
140
141 raise NotImplementedError(
142 "VOTable mapComplex cannot handle %s[%s]"%(type, length))
143
144
146 typeSystem = "db"
147
148 simpleMap = {
149 ("short", '1'): "smallint",
150 ("int", '1'): "integer",
151 ("long", '1'): "bigint",
152 ("float", '1'): "real",
153 ("boolean", '1'): "boolean",
154 ("double", '1'): "double precision",
155 ("char", "*"): "text",
156 ("char", '1'): "char",
157 ("unsignedByte", '1'): "smallint",
158 ("raw", '1'): "raw",
159 }
160
161 xtypeMap = {
162 "adql:POINT": "spoint",
163 "adql:REGION": "spoly",
164 "adql:TIMESTAMP": "timestamp",
165 "timestamp": "timestamp",
166 "point": "spoint",
167 "circle": "scircle",
168 "polygon": "spoly",
169 "x:box": "sbox",
170 }
171
172 - def convert(self, type, arraysize, xtype=None):
181
183 if arraysize=="*":
184 arraysize = ""
185 if type=="char":
186 return "text"
187 if type=="unicodeChar":
188 return "unicode"
189 if type=="unsignedByte" and arraysize!="1":
190 return "bytea[]"
191 if (type, '1') in self.simpleMap:
192 return "%s[%s]"%(self.simpleMap[type, '1'], arraysize)
193 raise ConversionError("No SQL type for %s, %s"%(type, arraysize))
194
195
196 sqltypeToVOTable = ToVOTableConverter().convert
197 voTableToSQLType = FromVOTableConverter().convert
198