1 """
2 Helpers for building SQL expressions.
3
4 Some of this code is concerned with SQL factories. These are functions
5 with the signature::
6
7 func(field, val, outPars) -> fragment
8
9 outPars is a dictionary that is used to transmit literal values into SQL.
10 The result must be an SQL boolean expression for embedding into a WHERE clause
11 (use None to signal no constraint). Field is the field for which the
12 expression is being generated.
13
14 The factories currently are never called when val is a sequence; there's
15 special hard-coded behaviour for that in getSQLFactory.
16
17 To enter values in outPars, use getSQLKey. Its docstring contains
18 an example that shows how that would look like.
19 """
20
21
22
23
24
25
26
27 from gavo.stc import mjdToDateTime
28
29
30 plusInfinity = float("Inf")
31 minusInfinity = float("-Inf")
32
33
35 """filters empty operands and joins the rest using operator.
36
37 The function returns an expression string or None for the empty expression.
38 """
39 operands = filter(None, operands)
40 if not operands:
41 return None
42 elif len(operands)==1:
43 return operands[0]
44 else:
45 return operator.join([" (%s) "%op for op in operands]).strip()
46
47
49 """adds value to sqlPars and returns a key for inclusion in a SQL query.
50
51 This function is used to build parameter dictionaries for SQL queries,
52 avoiding overwriting parameters with accidental name clashes.
53
54 As an extra service, if value is a list, it is turned into a set
55 (rather than the default, which would be an array). We don't believe
56 there's a great need to match against arrays. If you must match against
57 arrays, use numpy arrays.
58
59 >>> sqlPars = {}
60 >>> getSQLKey("foo", 13, sqlPars)
61 'foo0'
62 >>> getSQLKey("foo", 14, sqlPars)
63 'foo1'
64 >>> getSQLKey("foo", 13, sqlPars)
65 'foo0'
66 >>> sqlPars["foo0"], sqlPars["foo1"]; sqlPars = {}
67 (13, 14)
68 >>> "WHERE foo<%%(%s)s OR foo>%%(%s)s"%(getSQLKey("foo", 1, sqlPars),
69 ... getSQLKey("foo", 15, sqlPars))
70 'WHERE foo<%(foo0)s OR foo>%(foo1)s'
71 """
72 if isinstance(value, list):
73 value = frozenset(value)
74
75 ct = 0
76 while True:
77 dataKey = "%s%d"%(key, ct)
78 if dataKey not in sqlPars or sqlPars[dataKey]==value:
79 break
80 ct += 1
81 sqlPars[dataKey] = value
82 return dataKey
83
84
85 _REGISTRED_SQL_FACTORIES = {}
90
91
93 if len(val)==0 or (len(val)==1 and val[0] is None):
94 return ""
95 return "%s IN %%(%s)s"%(field.name, getSQLKey(field.name,
96 set(val), sqlPars))
97
98
103
104
106 """returns SQL for DALI intervals.
107
108 This presumes that val is a 2-array of numbers and will return
109 an empty condition otherwise.
110 """
111 if len(val)!=2:
112 return ""
113
114 if field.hasProperty("database-column-is-date"):
115 val = [_convertIfFinite(v, mjdToDateTime) for v in val]
116
117 if val[1]==plusInfinity:
118 return "%s > %%(%s)s"%(field.name, getSQLKey(field.name,
119 val[0], sqlPars))
120
121 elif val[0]==minusInfinity:
122 return "%s < %%(%s)s"%(field.name, getSQLKey(field.name,
123 val[1], sqlPars))
124
125 else:
126 return "%s BETWEEN %%(%s)s AND %%(%s)s"%(field.name,
127 getSQLKey(field.name, val[0], sqlPars),
128 getSQLKey(field.name, val[1], sqlPars))
129
130
134
135
137 """returns an SQL factory for matching field's values against value.
138 """
139 if field.xtype=="interval":
140 return _getSQLForInterval
141
142 elif isinstance(value, (list, tuple)):
143 return _getSQLForSequence
144
145 elif field.type in _REGISTRED_SQL_FACTORIES:
146 return _REGISTRED_SQL_FACTORIES[field.type]
147
148 else:
149 return _getSQLForSimple
150
151
153 """returns an SQL fragment for a column-like thing.
154
155 This will be empty if no input in inPars is present. If it is, (a) new
156 key(s) will be left in sqlPars.
157
158 getSQLForField defines the default behaviour; in DBCore condDescs,
159 it can be overridden using phrase makers.
160
161 inPars is supposed to be "typed"; we do not catch general parse errors
162 here.
163 """
164 val = inPars.get(field.name)
165 if val is None:
166 return None
167 if isinstance(val, (list, set, tuple)) and len(val)==1:
168 val = val[0]
169
170 factory = _getSQLFactory(field, val)
171 return factory(field, val, sqlPars)
172
173
177
178
179 if __name__=="__main__":
180 _test()
181