1 | from .._compat import basestring |
---|
2 | from ..adapters.mssql import ( |
---|
3 | MSSQL, |
---|
4 | MSSQLN, |
---|
5 | MSSQL3, |
---|
6 | MSSQL4, |
---|
7 | MSSQL3N, |
---|
8 | MSSQL4N, |
---|
9 | Vertica, |
---|
10 | Sybase, |
---|
11 | ) |
---|
12 | from ..helpers.methods import varquote_aux |
---|
13 | from ..objects import Expression |
---|
14 | from .base import SQLDialect |
---|
15 | from . import dialects, sqltype_for |
---|
16 | |
---|
17 | |
---|
18 | @dialects.register_for(MSSQL) |
---|
19 | class MSSQLDialect(SQLDialect): |
---|
20 | true = 1 |
---|
21 | false = 0 |
---|
22 | true_exp = "1=1" |
---|
23 | false_exp = "1=0" |
---|
24 | dt_sep = "T" |
---|
25 | |
---|
26 | @sqltype_for("boolean") |
---|
27 | def type_boolean(self): |
---|
28 | return "BIT" |
---|
29 | |
---|
30 | @sqltype_for("blob") |
---|
31 | def type_blob(self): |
---|
32 | return "IMAGE" |
---|
33 | |
---|
34 | @sqltype_for("integer") |
---|
35 | def type_integer(self): |
---|
36 | return "INT" |
---|
37 | |
---|
38 | @sqltype_for("bigint") |
---|
39 | def type_bigint(self): |
---|
40 | return "BIGINT" |
---|
41 | |
---|
42 | @sqltype_for("double") |
---|
43 | def type_double(self): |
---|
44 | return "FLOAT" |
---|
45 | |
---|
46 | @sqltype_for("date") |
---|
47 | def type_date(self): |
---|
48 | return "DATE" |
---|
49 | |
---|
50 | @sqltype_for("time") |
---|
51 | def type_time(self): |
---|
52 | return "CHAR(8)" |
---|
53 | |
---|
54 | @sqltype_for("datetime") |
---|
55 | def type_datetime(self): |
---|
56 | return "DATETIME" |
---|
57 | |
---|
58 | @sqltype_for("id") |
---|
59 | def type_id(self): |
---|
60 | return "INT IDENTITY PRIMARY KEY" |
---|
61 | |
---|
62 | @sqltype_for("reference") |
---|
63 | def type_reference(self): |
---|
64 | return ( |
---|
65 | "INT%(null)s%(unique)s, CONSTRAINT %(constraint_name)s " |
---|
66 | + "FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s ON " |
---|
67 | + "DELETE %(on_delete_action)s" |
---|
68 | ) |
---|
69 | |
---|
70 | @sqltype_for("big-id") |
---|
71 | def type_big_id(self): |
---|
72 | return "BIGINT IDENTITY PRIMARY KEY" |
---|
73 | |
---|
74 | @sqltype_for("big-reference") |
---|
75 | def type_big_reference(self): |
---|
76 | return ( |
---|
77 | "BIGINT%(null)s%(unique)s, CONSTRAINT %(constraint_name)s" |
---|
78 | + " FOREIGN KEY (%(field_name)s) REFERENCES %(foreign_key)s " |
---|
79 | + "ON DELETE %(on_delete_action)s" |
---|
80 | ) |
---|
81 | |
---|
82 | @sqltype_for("reference FK") |
---|
83 | def type_reference_fk(self): |
---|
84 | return ( |
---|
85 | ", CONSTRAINT FK_%(constraint_name)s FOREIGN KEY " |
---|
86 | + "(%(field_name)s) REFERENCES %(foreign_key)s ON DELETE " |
---|
87 | + "%(on_delete_action)s" |
---|
88 | ) |
---|
89 | |
---|
90 | @sqltype_for("reference TFK") |
---|
91 | def type_reference_tfk(self): |
---|
92 | return ( |
---|
93 | " CONSTRAINT FK_%(constraint_name)s_PK FOREIGN KEY " |
---|
94 | + "(%(field_name)s) REFERENCES %(foreign_table)s " |
---|
95 | + "(%(foreign_key)s) ON DELETE %(on_delete_action)s" |
---|
96 | ) |
---|
97 | |
---|
98 | @sqltype_for("geometry") |
---|
99 | def type_geometry(self): |
---|
100 | return "geometry" |
---|
101 | |
---|
102 | @sqltype_for("geography") |
---|
103 | def type_geography(self): |
---|
104 | return "geography" |
---|
105 | |
---|
106 | def varquote(self, val): |
---|
107 | return varquote_aux(val, "[%s]") |
---|
108 | |
---|
109 | def update(self, table, values, where=None): |
---|
110 | tablename = self.writing_alias(table) |
---|
111 | whr = "" |
---|
112 | if where: |
---|
113 | whr = " %s" % self.where(where) |
---|
114 | return "UPDATE %s SET %s FROM %s%s;" % ( |
---|
115 | table.sql_shortref, |
---|
116 | values, |
---|
117 | tablename, |
---|
118 | whr, |
---|
119 | ) |
---|
120 | |
---|
121 | def delete(self, table, where=None): |
---|
122 | tablename = self.writing_alias(table) |
---|
123 | whr = "" |
---|
124 | if where: |
---|
125 | whr = " %s" % self.where(where) |
---|
126 | return "DELETE %s FROM %s%s;" % (table.sql_shortref, tablename, whr) |
---|
127 | |
---|
128 | def select( |
---|
129 | self, |
---|
130 | fields, |
---|
131 | tables, |
---|
132 | where=None, |
---|
133 | groupby=None, |
---|
134 | having=None, |
---|
135 | orderby=None, |
---|
136 | limitby=None, |
---|
137 | distinct=False, |
---|
138 | for_update=False, |
---|
139 | ): |
---|
140 | dst, whr, grp, order, limit, upd = "", "", "", "", "", "" |
---|
141 | if distinct is True: |
---|
142 | dst = " DISTINCT" |
---|
143 | elif distinct: |
---|
144 | dst = " DISTINCT ON (%s)" % distinct |
---|
145 | if where: |
---|
146 | whr = " %s" % self.where(where) |
---|
147 | if groupby: |
---|
148 | grp = " GROUP BY %s" % groupby |
---|
149 | if having: |
---|
150 | grp += " HAVING %s" % having |
---|
151 | if orderby: |
---|
152 | order = " ORDER BY %s" % orderby |
---|
153 | if limitby: |
---|
154 | (lmin, lmax) = limitby |
---|
155 | limit = " TOP %i" % lmax |
---|
156 | if for_update: |
---|
157 | upd = " FOR UPDATE" |
---|
158 | return "SELECT%s%s %s FROM %s%s%s%s%s;" % ( |
---|
159 | dst, |
---|
160 | limit, |
---|
161 | fields, |
---|
162 | tables, |
---|
163 | whr, |
---|
164 | grp, |
---|
165 | order, |
---|
166 | upd, |
---|
167 | ) |
---|
168 | |
---|
169 | def left_join(self, val, query_env={}): |
---|
170 | # Left join must always have an ON clause |
---|
171 | if not isinstance(val, basestring): |
---|
172 | val = self.expand(val, query_env=query_env) |
---|
173 | return "LEFT OUTER JOIN %s" % val |
---|
174 | |
---|
175 | def random(self): |
---|
176 | return "NEWID()" |
---|
177 | |
---|
178 | def cast(self, first, second, query_env={}): |
---|
179 | # apparently no cast necessary in MSSQL |
---|
180 | return first |
---|
181 | |
---|
182 | def _mssql_like_normalizer(self, term): |
---|
183 | term = term.replace("[", "[[]") |
---|
184 | return term |
---|
185 | |
---|
186 | def _like_escaper_default(self, term): |
---|
187 | if isinstance(term, Expression): |
---|
188 | return term |
---|
189 | return self._mssql_like_normalizer( |
---|
190 | super(MSSQLDialect, self)._like_escaper_default(term) |
---|
191 | ) |
---|
192 | |
---|
193 | def concat(self, *items, **kwargs): |
---|
194 | query_env = kwargs.get("query_env", {}) |
---|
195 | tmp = (self.expand(x, "string", query_env=query_env) for x in items) |
---|
196 | return "(%s)" % " + ".join(tmp) |
---|
197 | |
---|
198 | def regexp(self, first, second, query_env={}): |
---|
199 | second = self.expand(second, "string", query_env=query_env) |
---|
200 | second = second.replace("\\", "\\\\") |
---|
201 | second = second.replace(r"%", r"\%").replace("*", "%").replace(".", "_") |
---|
202 | return "(%s LIKE %s ESCAPE '\\')" % ( |
---|
203 | self.expand(first, query_env=query_env), |
---|
204 | second, |
---|
205 | ) |
---|
206 | |
---|
207 | def extract(self, first, what, query_env={}): |
---|
208 | return "DATEPART(%s,%s)" % (what, self.expand(first, query_env=query_env)) |
---|
209 | |
---|
210 | def epoch(self, val, query_env={}): |
---|
211 | return "DATEDIFF(second, '1970-01-01 00:00:00', %s)" % self.expand( |
---|
212 | val, query_env=query_env |
---|
213 | ) |
---|
214 | |
---|
215 | def length(self, val, query_env={}): |
---|
216 | return "LEN(%s)" % self.expand(val, query_env=query_env) |
---|
217 | |
---|
218 | def aggregate(self, first, what, query_env={}): |
---|
219 | if what == "LENGTH": |
---|
220 | what = "LEN" |
---|
221 | return super(MSSQLDialect, self).aggregate(first, what, query_env) |
---|
222 | |
---|
223 | @property |
---|
224 | def allow_null(self): |
---|
225 | return " NULL" |
---|
226 | |
---|
227 | def substring(self, field, parameters, query_env={}): |
---|
228 | return "SUBSTRING(%s,%s,%s)" % ( |
---|
229 | self.expand(field, query_env=query_env), |
---|
230 | parameters[0], |
---|
231 | parameters[1], |
---|
232 | ) |
---|
233 | |
---|
234 | def primary_key(self, key): |
---|
235 | return "PRIMARY KEY CLUSTERED (%s)" % key |
---|
236 | |
---|
237 | def concat_add(self, tablename): |
---|
238 | return "; ALTER TABLE %s ADD " % tablename |
---|
239 | |
---|
240 | def drop_index(self, name, table): |
---|
241 | return "DROP INDEX %s ON %s;" % (self.quote(name), table._rname) |
---|
242 | |
---|
243 | def st_astext(self, first, query_env={}): |
---|
244 | return "%s.STAsText()" % self.expand(first, query_env=query_env) |
---|
245 | |
---|
246 | def st_contains(self, first, second, query_env={}): |
---|
247 | return "%s.STContains(%s)=1" % ( |
---|
248 | self.expand(first, query_env=query_env), |
---|
249 | self.expand(second, first.type, query_env=query_env), |
---|
250 | ) |
---|
251 | |
---|
252 | def st_distance(self, first, second, query_env={}): |
---|
253 | return "%s.STDistance(%s)" % ( |
---|
254 | self.expand(first, query_env=query_env), |
---|
255 | self.expand(second, first.type, query_env=query_env), |
---|
256 | ) |
---|
257 | |
---|
258 | def st_equals(self, first, second, query_env={}): |
---|
259 | return "%s.STEquals(%s)=1" % ( |
---|
260 | self.expand(first, query_env=query_env), |
---|
261 | self.expand(second, first.type, query_env=query_env), |
---|
262 | ) |
---|
263 | |
---|
264 | def st_intersects(self, first, second, query_env={}): |
---|
265 | return "%s.STIntersects(%s)=1" % ( |
---|
266 | self.expand(first, query_env=query_env), |
---|
267 | self.expand(second, first.type, query_env=query_env), |
---|
268 | ) |
---|
269 | |
---|
270 | def st_overlaps(self, first, second, query_env={}): |
---|
271 | return "%s.STOverlaps(%s)=1" % ( |
---|
272 | self.expand(first, query_env=query_env), |
---|
273 | self.expand(second, first.type, query_env=query_env), |
---|
274 | ) |
---|
275 | |
---|
276 | def st_touches(self, first, second, query_env={}): |
---|
277 | return "%s.STTouches(%s)=1" % ( |
---|
278 | self.expand(first, query_env=query_env), |
---|
279 | self.expand(second, first.type, query_env=query_env), |
---|
280 | ) |
---|
281 | |
---|
282 | def st_within(self, first, second, query_env={}): |
---|
283 | return "%s.STWithin(%s)=1" % ( |
---|
284 | self.expand(first, query_env=query_env), |
---|
285 | self.expand(second, first.type, query_env=query_env), |
---|
286 | ) |
---|
287 | |
---|
288 | |
---|
289 | @dialects.register_for(MSSQLN) |
---|
290 | class MSSQLNDialect(MSSQLDialect): |
---|
291 | @sqltype_for("string") |
---|
292 | def type_string(self): |
---|
293 | return "NVARCHAR(%(length)s)" |
---|
294 | |
---|
295 | @sqltype_for("text") |
---|
296 | def type_text(self): |
---|
297 | return "NTEXT" |
---|
298 | |
---|
299 | def ilike(self, first, second, escape=None, query_env={}): |
---|
300 | if isinstance(second, Expression): |
---|
301 | second = self.expand(second, "string", query_env=query_env) |
---|
302 | else: |
---|
303 | second = self.expand(second, "string", query_env=query_env).lower() |
---|
304 | if escape is None: |
---|
305 | escape = "\\" |
---|
306 | second = second.replace(escape, escape * 2) |
---|
307 | if second.startswith("n'"): |
---|
308 | second = "N'" + second[2:] |
---|
309 | return "(%s LIKE %s ESCAPE '%s')" % ( |
---|
310 | self.lower(first, query_env), |
---|
311 | second, |
---|
312 | escape, |
---|
313 | ) |
---|
314 | |
---|
315 | |
---|
316 | @dialects.register_for(MSSQL3) |
---|
317 | class MSSQL3Dialect(MSSQLDialect): |
---|
318 | @sqltype_for("text") |
---|
319 | def type_text(self): |
---|
320 | return "VARCHAR(MAX)" |
---|
321 | |
---|
322 | @sqltype_for("time") |
---|
323 | def type_time(self): |
---|
324 | return "TIME(7)" |
---|
325 | |
---|
326 | def _rebuild_select_for_limit( |
---|
327 | self, fields, tables, dst, whr, grp, order, lmin, lmax |
---|
328 | ): |
---|
329 | f_outer = ["f_%s" % i for i in range(len(fields.split(",")))] |
---|
330 | f_inner = [field for field in fields.split(", ")] |
---|
331 | f_iproxy = ", ".join([self._as(o, n) for (o, n) in zip(f_inner, f_outer)]) |
---|
332 | f_oproxy = ", ".join(f_outer) |
---|
333 | interp = ( |
---|
334 | "SELECT%s %s FROM (" |
---|
335 | + "SELECT%s ROW_NUMBER() OVER (%s) AS w_row, %s FROM %s%s%s)" |
---|
336 | + " TMP WHERE w_row BETWEEN %i and %i;" |
---|
337 | ) |
---|
338 | return interp % ( |
---|
339 | dst, |
---|
340 | f_oproxy, |
---|
341 | dst, |
---|
342 | order, |
---|
343 | f_iproxy, |
---|
344 | tables, |
---|
345 | whr, |
---|
346 | grp, |
---|
347 | lmin, |
---|
348 | lmax, |
---|
349 | ) |
---|
350 | |
---|
351 | def select( |
---|
352 | self, |
---|
353 | fields, |
---|
354 | tables, |
---|
355 | where=None, |
---|
356 | groupby=None, |
---|
357 | having=None, |
---|
358 | orderby=None, |
---|
359 | limitby=None, |
---|
360 | distinct=False, |
---|
361 | for_update=False, |
---|
362 | ): |
---|
363 | dst, whr, grp, order, limit, offset, upd = "", "", "", "", "", "", "" |
---|
364 | if distinct is True: |
---|
365 | dst = " DISTINCT" |
---|
366 | elif distinct: |
---|
367 | dst = " DISTINCT ON (%s)" % distinct |
---|
368 | if where: |
---|
369 | whr = " %s" % self.where(where) |
---|
370 | if groupby: |
---|
371 | grp = " GROUP BY %s" % groupby |
---|
372 | if having: |
---|
373 | grp += " HAVING %s" % having |
---|
374 | if orderby: |
---|
375 | order = " ORDER BY %s" % orderby |
---|
376 | if limitby: |
---|
377 | (lmin, lmax) = limitby |
---|
378 | if lmin == 0: |
---|
379 | dst += " TOP %i" % lmax |
---|
380 | else: |
---|
381 | return self._rebuild_select_for_limit( |
---|
382 | fields, tables, dst, whr, grp, order, lmin, lmax |
---|
383 | ) |
---|
384 | if for_update: |
---|
385 | upd = " FOR UPDATE" |
---|
386 | return "SELECT%s %s FROM %s%s%s%s%s%s%s;" % ( |
---|
387 | dst, |
---|
388 | fields, |
---|
389 | tables, |
---|
390 | whr, |
---|
391 | grp, |
---|
392 | order, |
---|
393 | limit, |
---|
394 | offset, |
---|
395 | upd, |
---|
396 | ) |
---|
397 | |
---|
398 | |
---|
399 | @dialects.register_for(MSSQL4) |
---|
400 | class MSSQL4Dialect(MSSQL3Dialect): |
---|
401 | def select( |
---|
402 | self, |
---|
403 | fields, |
---|
404 | tables, |
---|
405 | where=None, |
---|
406 | groupby=None, |
---|
407 | having=None, |
---|
408 | orderby=None, |
---|
409 | limitby=None, |
---|
410 | distinct=False, |
---|
411 | for_update=False, |
---|
412 | ): |
---|
413 | dst, whr, grp, order, limit, offset, upd = "", "", "", "", "", "", "" |
---|
414 | if distinct is True: |
---|
415 | dst = " DISTINCT" |
---|
416 | elif distinct: |
---|
417 | dst = " DISTINCT ON (%s)" % distinct |
---|
418 | if where: |
---|
419 | whr = " %s" % self.where(where) |
---|
420 | if groupby: |
---|
421 | grp = " GROUP BY %s" % groupby |
---|
422 | if having: |
---|
423 | grp += " HAVING %s" % having |
---|
424 | if orderby: |
---|
425 | order = " ORDER BY %s" % orderby |
---|
426 | if limitby: |
---|
427 | (lmin, lmax) = limitby |
---|
428 | if lmin == 0: |
---|
429 | dst += " TOP %i" % lmax |
---|
430 | else: |
---|
431 | if not order: |
---|
432 | order = " ORDER BY %s" % self.random |
---|
433 | offset = " OFFSET %i ROWS FETCH NEXT %i ROWS ONLY" % ( |
---|
434 | lmin, |
---|
435 | (lmax - lmin), |
---|
436 | ) |
---|
437 | if for_update: |
---|
438 | upd = " FOR UPDATE" |
---|
439 | return "SELECT%s %s FROM %s%s%s%s%s%s%s;" % ( |
---|
440 | dst, |
---|
441 | fields, |
---|
442 | tables, |
---|
443 | whr, |
---|
444 | grp, |
---|
445 | order, |
---|
446 | limit, |
---|
447 | offset, |
---|
448 | upd, |
---|
449 | ) |
---|
450 | |
---|
451 | |
---|
452 | @dialects.register_for(MSSQL3N) |
---|
453 | class MSSQL3NDialect(MSSQLNDialect, MSSQL3Dialect): |
---|
454 | @sqltype_for("text") |
---|
455 | def type_text(self): |
---|
456 | return "NVARCHAR(MAX)" |
---|
457 | |
---|
458 | |
---|
459 | @dialects.register_for(MSSQL4N) |
---|
460 | class MSSQL4NDialect(MSSQLNDialect, MSSQL4Dialect): |
---|
461 | @sqltype_for("text") |
---|
462 | def type_text(self): |
---|
463 | return "NVARCHAR(MAX)" |
---|
464 | |
---|
465 | |
---|
466 | @dialects.register_for(Vertica) |
---|
467 | class VerticaDialect(MSSQLDialect): |
---|
468 | dt_sep = " " |
---|
469 | |
---|
470 | @sqltype_for("boolean") |
---|
471 | def type_boolean(self): |
---|
472 | return "BOOLEAN" |
---|
473 | |
---|
474 | @sqltype_for("text") |
---|
475 | def type_text(self): |
---|
476 | return "BYTEA" |
---|
477 | |
---|
478 | @sqltype_for("json") |
---|
479 | def type_json(self): |
---|
480 | return self.types["string"] |
---|
481 | |
---|
482 | @sqltype_for("blob") |
---|
483 | def type_blob(self): |
---|
484 | return "BYTEA" |
---|
485 | |
---|
486 | @sqltype_for("double") |
---|
487 | def type_double(self): |
---|
488 | return "DOUBLE PRECISION" |
---|
489 | |
---|
490 | @sqltype_for("time") |
---|
491 | def type_time(self): |
---|
492 | return "TIME" |
---|
493 | |
---|
494 | @sqltype_for("id") |
---|
495 | def type_id(self): |
---|
496 | return "IDENTITY" |
---|
497 | |
---|
498 | @sqltype_for("reference") |
---|
499 | def type_reference(self): |
---|
500 | return "INT REFERENCES %(foreign_key)s ON DELETE %(on_delete_action)s" |
---|
501 | |
---|
502 | @sqltype_for("big-reference") |
---|
503 | def type_big_reference(self): |
---|
504 | return "BIGINT REFERENCES %(foreign_key)s ON DELETE" + " %(on_delete_action)s" |
---|
505 | |
---|
506 | def extract(self, first, what, query_env={}): |
---|
507 | return "DATE_PART('%s', TIMESTAMP %s)" % ( |
---|
508 | what, |
---|
509 | self.expand(first, query_env=query_env), |
---|
510 | ) |
---|
511 | |
---|
512 | def truncate(self, table, mode=""): |
---|
513 | if mode: |
---|
514 | mode = " %s" % mode |
---|
515 | return ["TRUNCATE %s%s;" % (table._rname, mode)] |
---|
516 | |
---|
517 | def select(self, *args, **kwargs): |
---|
518 | return SQLDialect.select(self, *args, **kwargs) |
---|
519 | |
---|
520 | |
---|
521 | @dialects.register_for(Sybase) |
---|
522 | class SybaseDialect(MSSQLDialect): |
---|
523 | @sqltype_for("string") |
---|
524 | def type_string(self): |
---|
525 | return "CHAR VARYING(%(length)s)" |
---|
526 | |
---|
527 | @sqltype_for("date") |
---|
528 | def type_date(self): |
---|
529 | return "DATETIME" |
---|