Contact: fumanchu@aminus.org

Log in as guest/geniusql to create tickets

root/trunk/geniusql/providers/sqlite.py

Revision 326 (checked in by lakin, 2 months ago)

fixing the postgresql connection tests and the sqlite join sql writer

  • Property svn:eol-style set to native
Line 
1 import datetime
2 import os
3 import sys
4 import time
5
6 import geniusql
7 from geniusql import adapters, dbtypes, conns, deparse, errors, providers, sqlwriters, typerefs
8 from geniusql import isolation as _isolation
9
10 # Use _sqlite3 directly to avoid all of the DB-API overhead.
11 # This assumes the one built into Python 2.5+
12 import _sqlite3 as _sqlite
13 _version = providers.Version(_sqlite.sqlite_version)
14
15 _driver_version = providers.Version(getattr(_sqlite, "version", "1"))
16 if _driver_version >= 2:
17     _cursor_required = True
18     _fetchall_required = True
19     _lastrowid_support = True
20 else:
21     _cursor_required = True
22     _fetchall_required = True
23     _lastrowid_support = True
24
25 # ESCAPE keyword was added Nov 2004, 1 month after 3.0.8 release.
26 _escape_support = (_version > providers.Version([3, 0, 8]))
27 if not _escape_support:
28     _escape_warning = ("Version %s of sqlite does not support "
29                        "wildcard literals." % _version)
30     errors.warn(_escape_warning)
31
32 _add_column_support = (_version >= providers.Version([3, 2, 0]))
33 _rename_table_support = (_version >= providers.Version([3, 1, 0]))
34 _autoincrement_support = (_version >= providers.Version([3, 1, 0]))
35 _cast_support = (_version >= providers.Version([3, 2, 3]))
36 _trim_support = (_version >= providers.Version([3, 3, 14]))
37
38 # ------------------------------ Adapters ------------------------------ #
39
40
41 def DATEADD(d, days):
42     """Return the SQL to add a number of days to a date."""
43     return "date(%s, '%s days')" % (d, days)
44
45 def DATEDIFF(d1, d2):
46     """Return the SQL to subtract one date from another."""
47     # "The julianday() function returns the number of days since
48     # noon in Greenwich on November 24, 4714 B.C. The julian day
49     # number is the preferred internal representation of dates."
50     return "((julianday(%s) * 86400) - (julianday(%s) * 86400))" % (d1, d2)
51
52 def DATETIMEADD(dt, td):
53     """Return the SQL to add a timedelta to a datetime."""
54     return "datetime(%s, '%s days', '%s seconds')" % (dt, td.days, td.seconds)
55
56 def DATETIMEDIFF(d1, d2):
57     """Return the SQL to subtract one datetime from another."""
58     return "((julianday(%s) * 86400) - (julianday(%s) * 86400))" % (d1, d2)
59
60
61 class SQLite_datetime_to_TEXT(adapters.datetime_to_SQL92TIMESTAMP):
62    
63     def binary_op(self, op1, op, sqlop, op2):
64         if op2.pytype is datetime.datetime:
65             if op == "-":
66                 return DATETIMEDIFF(op1.sql, op2.sql)
67         elif op2.pytype is datetime.timedelta:
68             if op == "+":
69                 return DATETIMEADD(op1.sql, op2.value)
70             elif op == "-":
71                 return DATETIMEADD(op1.sql, -op2.value)
72         raise TypeError("unsupported operand type(s) for %s: "
73                         "%r and %r" % (op, op1.pytype, op2.pytype))
74
75 class SQLite_date_to_TEXT(adapters.date_to_SQL92DATE):
76    
77     def binary_op(self, op1, op, sqlop, op2):
78         if op2.pytype is datetime.date:
79             if op == "-":
80                 return DATEDIFF(op1.sql, op2.sql)
81         elif op2.pytype is datetime.timedelta:
82             if op == "+":
83                 return DATEADD(op1.sql, op2.value.days)
84             elif op == "-":
85                 return DATEADD(op1.sql, -op2.value.days)
86         raise TypeError("unsupported operand type(s) for %s: "
87                         "%r and %r" % (op, op1.pytype, op2.pytype))
88
89 class SQLite_timedelta_to_TEXT(adapters.timedelta_to_SQL92DECIMAL):
90    
91     def binary_op(self, op1, op, sqlop, op2):
92         if op2.pytype is datetime.timedelta:
93             return "(%s %s %s)" % (op1.sql, op, op2.sql)
94         else:
95             if op == "+":
96                 if op2.pytype is datetime.date:
97                     return DATEADD(op2.sql, op1.value.days)
98                 elif op2.pytype is datetime.datetime:
99                     return DATETIMEADD(op2.sql, op1.value)
100         raise TypeError("unsupported operand type(s) for %s: "
101                         "%r and %r" % (op, op1.pytype, op2.pytype))
102
103
104 class SQLite_String_Mixin(object):
105     # C-style backslash escapes are not supported.
106     # See http://www.sqlite.org/lang_expr.html
107     escapes = [("'", "''")]
108    
109     # Notice these are ordered pairs. Escape \ before introducing new ones.
110     # Values in these two lists should be strings encoded with self.encoding.
111     like_escapes = [("%", "\%"), ("_", "\_")]
112    
113     def escape_like(self, sql):
114         """Prepare a string value for use in a LIKE comparison."""
115         # Notice we strip leading and trailing quote-marks.
116         sql = sql.strip("'\"")
117         if _escape_support:
118             for pat, repl in self.like_escapes:
119                 sql = sql.replace(pat, repl)
120         elif "%" in sql or "_" in sql:
121             raise ValueError(_escape_warning)
122         return sql
123    
124     def like_op(self, op1, op2, ignore_case=False,
125                 start_only=False, end_only=False):
126         """Return the SQL for 'op1 LIKE op2' (or raise TypeError).
127         
128         op1 and op2 will be SQLExpression objects.
129         
130         If 'ignore_case' is False (the default), then the LIKE comparison
131         will be performed in a case-sensitive manner; otherwise (if
132         ignore_case is True), the LIKE comparison will be performed in
133         a case-INsensitive manner.
134         
135         If 'start_only' is True, then op2 will be matched only at the start
136         of op1. If False (the default), then op2 will be matched anywhere.
137         
138         If 'end_only' is True, then op2 will be matched only at the end
139         of op1. If False (the default), then op2 will be matched anywhere.
140         
141         If both 'start_only' and 'end_only' are True, then op2 will only
142         match op1 if they are identical.
143         """
144         likeexpr = self.escape_like(op2.sql)
145        
146         if start_only:
147             start = ''
148         else:
149             start = '%'
150         if end_only:
151             end = ''
152         else:
153             end = '%'
154        
155         if ignore_case:
156             sql = "LOWER(" + op1.sql + ") LIKE '" + start + likeexpr.lower() + end + "'"
157         else:
158             sql = op1.sql + " LIKE '" + start + likeexpr + end + r"'"
159        
160         if _escape_support:
161             sql += r" ESCAPE '\'"
162        
163         return sql
164
165
166 class SQLite_str_to_TEXT(SQLite_String_Mixin, adapters.str_to_SQL92VARCHAR):
167     pass
168
169 class SQLite_unicode_to_TEXT(SQLite_String_Mixin, adapters.unicode_to_SQL92VARCHAR):
170     pass
171
172 class SQLite_Pickler(SQLite_String_Mixin, adapters.Pickler):
173     pass
174
175
176 # --------------------------- Database Types --------------------------- #
177
178
179 class INTEGER(dbtypes.SQL92INTEGER):
180     _bytes = max_bytes = 2 ** 30
181     synonyms = ['BOOL', 'SMALLINT UNSIGNED', 'INTEGER UNSIGNED', 'BOOLEAN']
182
183 class TEXT(dbtypes.TEXT):
184     # "A single row can hold up to 2 ** 30 bytes of data
185     #   in the current implementation."
186     _bytes = max_bytes = 2 ** 30
187     synonyms = ['NUMERIC', 'VARCHAR', 'CHAR', 'DECIMAL']
188    
189     default_adapters = dbtypes.TEXT.default_adapters.copy()
190     default_adapters.update({str: SQLite_str_to_TEXT(),
191                              unicode: SQLite_unicode_to_TEXT(),
192                              datetime.datetime: SQLite_datetime_to_TEXT(),
193                              datetime.date: SQLite_date_to_TEXT(),
194                              datetime.timedelta: SQLite_timedelta_to_TEXT(),
195                              None: SQLite_Pickler(),
196                              })
197
198 class DATETIME(dbtypes.SQL92TIMESTAMP):
199     """
200     A date and time.
201     """
202
203     # TODO: This was copied from TEXT above. Probably need to carefully
204     #       consider each adapter listed in here. In fact, understanding
205     #       what these default_adapters do and then getting these right
206     #       is likely step #1 in fixing all of this.
207     default_adapters = dbtypes.TEXT.default_adapters.copy()
208     default_adapters.update({str: SQLite_str_to_TEXT(),
209                              unicode: SQLite_unicode_to_TEXT(),
210                              datetime.datetime: SQLite_datetime_to_TEXT(),
211                              datetime.date: SQLite_date_to_TEXT(),
212                              datetime.timedelta: SQLite_timedelta_to_TEXT(),
213                              None: SQLite_Pickler(),
214                              })
215
216 class DATE(dbtypes.SQL92DATE):
217     """A calendar date (year, month, day)."""
218     # TODO: This was copied from TEXT above. Probably need to carefully
219     #       consider each adapter listed in here. In fact, understanding
220     #       what these default_adapters do and then getting these right
221     #       is likely step #1 in fixing all of this.
222     default_adapters = dbtypes.TEXT.default_adapters.copy()
223     default_adapters.update({str: SQLite_str_to_TEXT(),
224                              unicode: SQLite_unicode_to_TEXT(),
225                              datetime.datetime: SQLite_datetime_to_TEXT(),
226                              datetime.date: SQLite_date_to_TEXT(),
227                              datetime.timedelta: SQLite_timedelta_to_TEXT(),
228                              None: SQLite_Pickler(),
229                              })
230
231 class TIME(dbtypes.SQL92TIME):
232     """A time of day."""
233     # TODO: This was copied from TEXT above. Probably need to carefully
234     #       consider each adapter listed in here. In fact, understanding
235     #       what these default_adapters do and then getting these right
236     #       is likely step #1 in fixing all of this.
237     default_adapters = dbtypes.TEXT.default_adapters.copy()
238     default_adapters.update({str: SQLite_str_to_TEXT(),
239                              unicode: SQLite_unicode_to_TEXT(),
240                              datetime.datetime: SQLite_datetime_to_TEXT(),
241                              datetime.date: SQLite_date_to_TEXT(),
242                              datetime.timedelta: SQLite_timedelta_to_TEXT(),
243                              None: SQLite_Pickler(),
244                              })
245
246
247 class REAL(dbtypes.SQL92DOUBLE):
248     pass
249
250 class NUMERIC(dbtypes.SQL92DECIMAL):
251     # numeric precision is in decimal digits.
252     #
253     # SQLite uses 64-bit floats for all numbers;
254     # 53 of those bits are significant; 2 ** 53 = 9007199254740992L
255     # = almost-but-not-quite-16 decimal digits = 15 decimal digits.
256     # Use one digit for the sign, and you've got 14 decimal digits.
257     _precision = max_precision = 14
258     scale = 14
259     max_scale = 14
260
261 class NONE(dbtypes.DatabaseType):
262     pass
263
264
265 class SQLiteTypeSet(dbtypes.DatabaseTypeSet):
266     """For a column and Python type, return a database type.
267     
268     From http://www.sqlite.org/datatype3.html:
269         
270         "The type affinity of a column is determined by the declared
271         type of the column, according to the following rules:
272         1. If the datatype contains the string "INT" then it is
273            assigned INTEGER affinity.
274         2. If the datatype of the column contains any of the strings
275            "CHAR", "CLOB", or "TEXT" then that column has TEXT affinity.
276            Notice that the type VARCHAR contains the string "CHAR" and
277            is thus assigned TEXT affinity.
278         3. If the datatype for a column contains the string "BLOB" or
279            if no datatype is specified then the column has affinity NONE.
280         4. If the datatype for a column contains any of the strings
281            "REAL", "FLOA", or "DOUB" then the column has REAL affinity.
282         5. Otherwise, the affinity is NUMERIC."
283     """
284    
285     known_types = {
286         'int': [INTEGER],
287         'integer': [INTEGER],
288         'smallint unsigned': [INTEGER],
289         'float': [REAL],
290        
291         # &^%$#@! SQLite tries to convert NUMERIC values to REAL, so e.g.
292         # INSERT INTO x VALUES 1111.1111 will result in 1111.1111000000001
293         # Therefore, we must *always* use TEXT.
294         # See "Manifest Typing" in the SQLite docs.
295         'numeric': [],
296        
297         'varchar': [TEXT],
298         'bool': [INTEGER],
299         'datetime': [DATETIME],
300         'date': [DATE],
301         'time': [TIME],
302         'timedelta': [],
303         'other': [],
304         }
305    
306     # These are not adapter.push(bool) (which are used on one side of
307     # a comparison). Instead, these are used when the whole (sub)expression
308     # is True or False, e.g. "WHERE TRUE", or "WHERE TRUE and 'a'.'b' = 3".
309     expr_true = "1"
310     expr_false = "0"
311
312
313
314 class SQLiteDeparser(deparse.SQLDeparser):
315    
316     def builtins_now(self):
317         """Return a datetime.datetime for the current time in the local TZ."""
318         # The 'localtime' modifier is not thread-safe.
319         # Manually modify the time.
320         neg, h, m = adapters.localtime_offset()
321         sign = "+"
322         if neg:
323             sign = "-"
324         e = ("datetime('now', '%s%s hours', '%s%s minutes')"
325              % (sign, h, sign, m))
326         return self.get_expr(e, datetime.datetime)
327    
328     def builtins_utcnow(self):
329         return self.get_expr("datetime('now')", datetime.datetime)
330    
331     def builtins_today(self):
332         # The 'localtime' modifier is not thread-safe.
333         # Manually modify the time.
334         neg, h, m = adapters.localtime_offset()
335         sign = "+"
336         if neg:
337             sign = "-"
338         e = ("date('now', '%s%s hours', '%s%s minutes')"
339              % (sign, h, sign, m))
340         return self.get_expr(e, datetime.date)
341    
342     def builtins_year(self, x):
343         return self.get_expr("CAST(strftime('%Y', " + x.sql +
344                              ") AS NUMERIC)", int)
345    
346     def builtins_month(self, x):
347         return self.get_expr("CAST(strftime('%m', " + x.sql +
348                              ") AS NUMERIC)", int)
349    
350     def builtins_day(self, x):
351         return self.get_expr("CAST(strftime('%d', " + x.sql +
352                              ") AS NUMERIC)", int)
353
354
355
356 class SQLiteTable(geniusql.Table):
357     """A table in a database; a dict of Column objects.
358     
359     Values in this dict must be instances of Column (or a subclass of it).
360     Keys should be consumer-friendly names for each Column value.
361     
362     name: the SQL name for this table (unquoted).
363     qname: the SQL name for this table (quoted).
364     schema: the schema for this table.
365     created: whether or not this Table has a concrete implementation in the
366         database. If False (the default), then changes to Table items can be
367         made with impunity. If True, then appropriate ALTER TABLE commands
368         are executed whenever a consumer adds or deletes items from the
369         Table, or calls methods like 'rename'.
370     indices: a dict-like IndexSet of Index objects.
371     references: a dict of the form:
372         {name: (near Column key, far Table key, far Column key)}.
373     
374     Various versions of SQLite have limited support for ALTER TABLE.
375     When necessary, this class will compensate with the following process:
376     
377       1. Create a temp table which has the desired new schema.
378       2. Copy the entire dataset from the original table to the temp table.
379       3. Drop the original table.
380       4. Re-create the original table with the desired new schema.
381       5. Copy the entire dataset from the temp table to the re-created table.
382       6. Drop the temporary table.
383     
384     Needless to say, this can take a LOT longer than most other stores.
385     """
386    
387     def create(self, skip_indices=False):
388         # Set table.created to True, which should "turn on"
389         # any future ALTER TABLE statements.
390         self.created = True
391        
392         fields = []
393         pk = []
394         autoincr_col = None
395         for key, col in self.iteritems():
396             fields.append(self.schema.columnclause(col))
397            
398             if col.autoincrement:
399                 # MUST create the sequence after the table is created,
400                 # or we get into a "no such table" loop inside execute.
401                 autoincr_col = (key, col)
402            
403             if col.key:
404                 pk.append(col.qname)
405        
406         if (autoincr_col is None) and pk:
407             # Seems we can't have both an AUTOINCREMENT and another PK
408             pk = ", PRIMARY KEY (%s)" % ", ".join(pk)
409         else:
410             pk = ""
411         self.schema.db.execute_ddl('CREATE TABLE %s (%s%s);' %
412                                    (self.qname, ", ".join(fields), pk))
413        
414         if autoincr_col:
415             # Columns created using schema.column() can't make their own
416             # sequence names because the tablename isn't available.
417             # So we do it here if needed.
418             key, col = autoincr_col
419             if col.sequence_name is None:
420                 col.sequence_name = self.schema.sequence_name(self.name, key)
421             self.schema.create_sequence(self, col)
422        
423         if not skip_indices:
424             for index in self.indices.itervalues():
425                 self.schema.db.execute_ddl(
426                     'CREATE INDEX %s ON %s (%s);' %
427                     (index.qname, self.qname,
428                      self.schema.db.quote(index.colname)))
429    
430     def _start_temp(self):
431         """Convert self into a temporary table. Not thread-safe."""
432         self.origname = self.name
433         self.origqname = self.qname
434         self.name = "temp_" + self.name
435         self.qname = self.schema.db.quote(self.name)
436    
437     def _finish_temp(self, selfields=None):
438         """Convert self from a temporary table. Not thread-safe."""
439         # CREATE the temporary TABLE.
440         self.create(skip_indices=True)
441        
442         tempqname = self.qname
443        
444         # Copy data from the original table to the temp table.
445         if selfields is None:
446             selfields = [c.qname for c in self.itervalues()]
447         self.schema.db.execute_ddl("INSERT INTO %s SELECT %s FROM %s;" %
448                                    (tempqname, ", ".join(selfields),
449                                     self.origqname))
450        
451         # DROP the original TABLE.
452         self.schema.db.execute_ddl('DROP TABLE %s;' % self.origqname)
453        
454         # CREATE the new TABLE. Note we do not skip indices here;
455         # SQLite dropped the old ones when we dropped the original table.
456         self.name = self.origname
457         self.qname = self.origqname
458         self.create()
459        
460         # Copy data from the temp table to the final table.
461         # For some odd reason, using "SELECT *"
462         # mixes up the fields (during rename, at least).
463         selfields = ", ".join([c.qname for c in self.values()])
464         self.schema.db.execute("INSERT INTO %s (%s) SELECT %s FROM %s;"
465                                % (self.qname, selfields, selfields,
466                                   tempqname))
467        
468         # DROP the temporary TABLE.
469         self.schema.db.execute_ddl('DROP TABLE %s;' % tempqname)
470    
471     if not _add_column_support:
472         def __setitem__(self, key, column):
473             if not self.created:
474                 super(SQLiteTable, self).__setitem__(key, column)
475                 return
476            
477             if key in self:
478                 del self[key]
479            
480             self._start_temp()
481             super(SQLiteTable, self).__setitem__(key, column)
482            
483             selfields = []
484             for k, c in self.iteritems():
485                 qname = c.qname
486                 if k == key:
487                     # This is a new column. Populate with NULL.
488                     qname = "NULL AS %s" % qname
489                 selfields.append(qname)
490             self._finish_temp(selfields)
491    
492     def __delitem__(self, key):
493         if key in self.indices:
494             del self.indices[key]
495        
496         if not self.created:
497             dict.__delitem__(self, key)
498             return
499        
500         column = self[key]
501         if column.autoincrement:
502             # This may or may not be a no-op, depending on the DB.
503             self.schema.drop_sequence(column)
504        
505         self._start_temp()
506         dict.__delitem__(self, key)
507         self._finish_temp()
508    
509     def rename(self, oldkey, newkey):
510         """Rename a Column."""
511         oldcol = self[oldkey]
512         oldname = oldcol.name
513         newname = self.schema.column_name(self.name, newkey)
514        
515         if oldname != newname:
516             self._start_temp()
517            
518             dict.__delitem__(self, oldkey)
519             dict.__setitem__(self, newkey, oldcol)
520             oldcol.name = newname
521             oldcol.qname = self.schema.db.quote(newname)
522            
523             selfields = []
524             for k, c in self.iteritems():
525                 qname = c.qname
526                 if k == newkey:
527                     qname = "%s AS %s" % (self.schema.db.quote(oldname), qname)
528                 selfields.append(qname)
529             self._finish_temp(selfields)
530    
531     def _grab_new_ids(self, idkeys, conn):
532         if _lastrowid_support:
533             new_id = conn.lastrowid
534         else:
535             new_id = conn.sqlite_last_insert_rowid()
536         return {idkeys[0]: new_id}
537    
538     def set_primary(self):
539         """Assert the PRIMARY KEY for this Table, using its Column.key values."""
540         self._start_temp()
541         self._finish_temp()
542    
543     def drop_primary(self):
544         """Remove any PRIMARY KEY for this Table."""
545         pk_cols = [col for col in self.itervalues() if col.key]
546         self._start_temp()
547         for col in pk_cols:
548             col.key = False
549         self._finish_temp()
550         for col in pk_cols:
551             col.key = True
552
553
554
555 class SQLiteSelectWriter(sqlwriters.SelectWriter):
556    
557     def _joinclause(self, join):
558        
559         t1, t2 = join.table1, join.table2
560         flip = False
561         if join.leftbiased is None:
562             join_type_middle = "INNER JOIN"
563         elif join.leftbiased is True:
564             join_type_middle = "LEFT JOIN"
565         else:
566             # My version (3.7.7) of SQLite says:
567             # "RIGHT and FULL OUTER JOINs are not currently supported".
568             join_type_middle = "LEFT JOIN"
569             flip = True
570
571         join_list_left = join_list_right = []
572         table_name_left = table_name_right = None
573         table_list_left = table_list_right = []
574
575         if isinstance(t1, geniusql.Join):
576             join_list_left = self._joinclause(t1)
577             table_list_left = iter(t1)
578         else:
579             # t1 is a Table class wrapper.
580             table_name_left = self.joinname(t1)
581             table_list_left = [t1]
582
583         if isinstance(t2, geniusql.Join):
584             join_list_right = self._joinclause(t2)
585             table_list_right = iter(t2)
586         else:
587             # t2 is a Table class wrapper.
588             table_name_right = self.joinname(t2)
589             table_list_right = [t2]
590
591         if flip:
592             table_name_left, table_name_right = table_name_right, table_name_left
593             join_list_left, join_list_right = join_list_right, join_list_left
594             table_list_left, table_list_right = table_list_right, table_list_left
595
596
597         # Find an association between the two halves.
598         for A in table_list_left:
599             for B in table_list_right:
600                 on_clause_middle = self.onclause(A, B, join.path)
601                 if not on_clause_middle:
602                     on_clause_middle = self.onclause(B, A, join.path)
603
604                 if on_clause_middle:
605                     if join_list_left and join_list_right:
606                         # The lists will have a form of:
607                         # [ ... table_name_left_1, join_type_1, table_name_right_1, on_clause_1 ]
608                         # [ table_name_left_2, join_type_2, table_name_right_2, on_clause_2 ... ]
609                         # and we have a new join type: join_type_middle, and on_clause_middle
610                         # and we will weave it so that it looks like:
611                         # [ ... table_name_left_1, join_type_1, table_name_right_1, on_clause_1,
612                         #       join_type_middle, table_name_left_2, on_clause_middle,
613                         #       join_type_2, table_name_right_2, on_clause_2, ... ]
614                         new_list = join_list_left
615                         new_list.extend([
616                             join_type_middle, join_list_right[0], "ON", on_clause_middle,
617                         ])
618                         new_list.extend(join_list_right[1:])
619                         return new_list
620                     elif join_list_left:
621                         # The join_list_left will have a form of:
622                         # [ ... table_name_left_1, join_type_1, table_name_right_1, on_clause_1 ]
623                         # and we have a table_name_right, join_type_middle, on_clause_middle
624                         # and we will weave it so that it looks like:
625                         # [ ... table_name_left_1, join_type_1, table_name_right_1, on_clause_1,
626                         #       join_type_middle, table_name_right, on_clause_middle ]
627                         new_list = join_list_left
628                         new_list.extend([
629                             join_type_middle, table_name_right, "ON", on_clause_middle,
630                         ])
631                         return new_list
632                     elif join_list_right:
633                         # The join_list_right will have a form of:
634                         # [ table_name_left_2, join_type_2, table_name_right_2, on_clause_2, ... ]
635                         # and we have a table_name_left, join_type_middle, on_clause_middle
636                         # and we will weave it so that it looks like:
637                         # [ table_name_left, join_type_middle, table_name_left_2, on_clause_middle,
638                         #       join_type_2, table_name_right_2, on_clause_2, ... ]
639                         new_list = [
640                                 table_name_left, join_type_middle, join_list_right[0], "ON", on_clause_middle,
641                             ]
642                         new_list.extend(join_list_right[1:])
643                         return new_list
644                     else:
645                         # and we have a table_name_left, join_type_middle, on_clause_middle
646                         # and we have a table_name_right
647                         # and we will weave it so that it looks like:
648                         # [ table_name_left, join_type_middle, table_name_right, on_clause_middle ]
649                         new_list = [
650                                 table_name_left, join_type_middle, table_name_right, "ON", on_clause_middle,
651                             ]
652                         return new_list
653
654         raise ReferenceError("No reference found between %s and %s."
655                              % (table_name_left, table_name_right))
656    
657     def joinclause(self, join):
658         return " ".join(self._joinclause(join))
659
660
661 class SQLiteConnectionManager(conns.ConnectionManager):
662    
663     default_isolation = "SERIALIZABLE"
664     isolation_levels = ["SERIALIZABLE"]
665    
666     def _set_factory(self):
667         if self.db.name == ":memory:":
668             # "Multiple connections to ":memory:" within a single process
669             # create a fresh database each time"
670             # http://www.sqlite.org/cvstrac/wiki?p=InMemoryDatabase
671             # So we need to give :memory: databases a SingleConnection.
672             self._factory = conns.SingleConnection(self._get_conn, self._del_conn,
673                                                    self.retry)
674         elif not self.db.threadsafe:
675             self._factory = conns.ConnectionPerThread(self._get_conn, self._del_conn,
676                                                       self.retry)
677         else:
678             # Use the default behavior (pool)
679             conns.ConnectionManager._set_factory(self)
680    
681     if _cursor_required:
682         def _get_conn(self):
683             # SQLite should create the DB if missing.
684             # valid _sqlite3 kwargs: "database", "timeout", "detect_types",
685             # "isolation_level", "check_same_thread", "factory",
686             # "cached_statements".
687             # Instead of "timeout", we re-use the old
688             # deadlock_timeout code inside execute.
689             conn = _sqlite.connect(database=self.db.name, check_same_thread=False)
690            
691             # None sets "autocommit mode" on. This turns off the silly
692             # PySQLite behavior of trying to handle transactions for you
693             # behind the scenes, and returns to the default SQLite behavior.
694             conn.isolation_level = None
695            
696             conn.text_factory = str
697             c = conn.cursor()
698             if self.initial_sql:
699                 c.execute(self.initial_sql)
700             return c
701     else:
702         def _get_conn(self):
703             conn = _sqlite.connect(self.db.name, self.db.mode)
704             if self.initial_sql:
705                 conn.execute(self.initial_sql)
706             return conn
707    
708     def isolate(self, conn, isolation=None):
709         """Set the isolation level of the given connection.
710         
711         If 'isolation' is None, our default_isolation will be used for new
712         connections. Valid values for the 'isolation' argument may be native
713         values for your particular database. However, it is recommended you
714         pass items from the global 'levels' list instead; these will be
715         automatically replaced with native values.
716         
717         For many databases, this must be executed after START TRANSACTION.
718         """
719         if isolation is None:
720             isolation = self.default_isolation
721        
722         if isinstance(isolation, _isolation.IsolationLevel):
723             # Map the given IsolationLevel object to a native value.
724             # This base class uses the four ANSI names as native values.
725             isolation = isolation.name
726        
727         if isolation not in self.isolation_levels:
728             raise ValueError("IsolationLevel %r not allowed by %s. "
729                              "Try one of %r instead."
730                              % (isolation, self.__class__.__name__,
731                                 self.isolation_levels))
732        
733         # Nothing to do here, since we only allow one level.
734         pass
735    
736     def _start_transaction(self, conn, isolation=None):
737         """Start a transaction."""
738         self.db.execute("BEGIN;", conn)
739         self.isolate(conn, isolation)
740    
741     def rollback(self):
742         """Roll back the current transaction."""
743         key = self.id()
744         if key in self.transactions:
745             self.db.execute("ROLLBACK;", self.transactions[key])
746             del self.transactions[key]
747    
748     def commit(self):
749         """Commit the current transaction."""
750         key = self.id()
751         if key in self.transactions:
752             self.db.execute("COMMIT;", self.transactions[key])
753             del self.transactions[key]
754
755
756 class SQLiteSchema(geniusql.Schema):
757    
758     tableclass = SQLiteTable
759    
760     def _get_tables(self, conn=None):
761         data, _ = self.db.fetch("SELECT name FROM sqlite_master "
762                                 "WHERE type = 'table' AND "
763                                 "name != 'sqlite_sequence';", conn)
764         # Note that we set Table.created here, since these already exist in the DB.
765         return [self.tableclass(row[0], self.db.quote(row[0]),
766                                 self, created=True)
767                 for row in data]
768    
769     def _get_table(self, tablename, conn=None):
770         data, _ = self.db.fetch("SELECT name FROM sqlite_master WHERE name = "
771                                 "'%s' AND type = 'table';" % tablename, conn)
772         # Note that we set Table.created here, since these already exist in the DB.
773         for name, in data:
774             if name == tablename:
775                 return self.tableclass(name, self.db.quote(name),
776                                        self, created=True)
777         raise errors.MappingError("Table %r not found." % tablename)
778    
779     def _get_columns(self, table, conn=None):
780         # cid, name, type, notnull, dflt_value, pk
781         data, _ = self.db.fetch("PRAGMA table_info(%s);" % table.qname,
782                                 conn=conn)
783        
784         cols = []
785         for row in data:
786             cid, name, dbtypename, notnull, default, pk = row
787             dbtypename = dbtypename.split("(")[0].upper()
788             dbtypetype = self.db.typeset.canonicalize(dbtypename)
789             dbtype = dbtypetype()
790            
791             c = geniusql.Column(dbtype.default_pytype, dbtype,
792                                 default, key=bool(pk),
793                                 name=name, qname=self.db.quote(name))
794             c.adapter = dbtype.default_adapter(c.pytype)
795            
796             # !@#$%^&. SQLite actually FORCES any "INTEGER PRIMARY KEY"
797             # column to autoincrement when you insert NULL.
798             # See http://sqlite.org/faq.html#q1.
799             if isinstance(dbtype, INTEGER) and c.key:
800                 c.autoincrement = True
801            
802             cols.append(c)
803        
804         return cols
805    
806     def _get_indices(self, table, conn=None):
807         data, _ = self.db.fetch(
808             "SELECT name, tbl_name, sql FROM sqlite_master "
809             "WHERE type = 'index' AND tbl_name = '%s';" % table.name, conn)
810        
811         indices = []
812         for row in data:
813             if row[2]:
814                 colname = row[2].split("(")[-1]
815                 i = geniusql.Index(row[0], self.db.quote(row[0]),
816                                    row[1], colname[1:-2])
817                 indices.append(i)
818         return indices
819    
820     def sequence_name(self, tablename, columnkey):
821         "Return the SQL sequence name for the given table name and column key."
822         # If you want to use a map from your ORM's property names
823         # to DB sequence names, override this method (that's why
824         # the tablename must be included in the args).
825         return self.db.sql_name(tablename)
826    
827     def create_sequence(self, table, column):
828         """Create a SEQUENCE for the given column."""
829         if column.sequence_name is not None:
830             # SQLite AUTOINCREMENT columns start at 1 by default.
831             # Manhandle the special SQLITE_SEQUENCE table to include
832             # the value of sequencer.initial - 1.
833             prev = column.initial - 1
834             data, _ = self.db.fetch(
835                 "SELECT * FROM SQLITE_SEQUENCE WHERE name = '%s';" %
836                 column.sequence_name)
837             if data:
838                 self.db.execute(
839                     "UPDATE SQLITE_SEQUENCE SET seq = %s WHERE name = '%s';" %
840                     (prev, column.sequence_name))
841             else:
842                 self.db.execute(
843                     "INSERT INTO SQLITE_SEQUENCE (seq, name) VALUES (%s, '%s');" %
844                     (prev, column.sequence_name))
845    
846     def drop_sequence(self, column):
847         """Drop a SEQUENCE for the given column."""
848         if column.sequence_name is not None:
849             self.db.execute("DELETE FROM SQLITE_SEQUENCE WHERE name = '%s';"
850                             % column.sequence_name)
851    
852     def columnclause(self, column):
853         """Return a clause for the given column for CREATE or ALTER TABLE.
854         
855         This will be of the form:
856             "name type [DEFAULT x | PRIMARY KEY AUTOINCREMENT]"
857         """
858         if column.autoincrement:
859             # From http://www.sqlite.org/datatypes.html:
860             # "One exception to the typelessness of SQLite is a column whose
861             # type is INTEGER PRIMARY KEY. (And you must use "INTEGER" not
862             # "INT". A column of type INT PRIMARY KEY is typeless just like
863             # any other.) INTEGER PRIMARY KEY columns must contain a 32-bit
864             # signed integer. Any attempt to insert non-integer data will
865             # result in an error."
866             coldef = "INTEGER PRIMARY KEY AUTOINCREMENT"
867         else:
868             coldef = column.dbtype.ddl()
869            
870             default = column.default or ""
871             if not isinstance(default, str):
872                 default = column.adapter.push(default, column.dbtype)
873             if default:
874                 coldef += " DEFAULT %s" % default
875         return '%s %s' % (column.qname, coldef)
876    
877     def _rename(self, oldtable, newtable):
878         if _rename_table_support:
879             self.db.execute_ddl("ALTER TABLE %s RENAME TO %s" %
880                                 (oldtable.qname, newtable.qname))
881         else:
882             raise NotImplementedError
883    
884     def drop(self):
885         """Drop this schema from the database."""
886         if self.db.name == ':memory:':
887             # Not much we can do, here. If we try to drop a table, we end
888             # up stuck in "database schema has changed"-land forever.
889             # Just assume for now we're going to drop the whole database.
890             self.db.drop()
891             self.db.create()
892         else:
893             # Must shut down all connections to avoid
894             # "being accessed by other users" error.
895             self.db.connections.shutdown()
896            
897             seen = {}
898             for tkey, table in self.items():
899                 # We might have multiple keys pointing at the same table.
900                 if table.name not in seen:
901                     del self[tkey]
902                     seen[table.name] = None
903
904
905 class SQLiteDatabase(geniusql.Database):
906    
907     sql_name_max_length = 0
908    
909     deparser = SQLiteDeparser
910     selectwriter = SQLiteSelectWriter
911     typeset = SQLiteTypeSet()
912     connectionmanager = SQLiteConnectionManager
913    
914     schemaclass = SQLiteSchema
915     multischema = False
916    
917     pks_must_be_indexed = False
918    
919     # Based on SQLite FAQ: http://www.sqlite.org/faq.html#q6
920     # Override as needed.
921     threadsafe = (_version >= providers.Version([3, 3, 1]))
922    
923     def __init__(self, **kwargs):
924         kwargs['mode'] = int(kwargs.pop('mode', '0755'), 8)
925        
926         if kwargs['name'] != ':memory:':
927             if not os.path.isabs(kwargs['name']):
928                 kwargs['name'] = os.path.join(os.getcwd(), kwargs['name'])
929        
930         geniusql.Database.__init__(self, **kwargs)
931    
932 ##    def _get_dbinfo(self, conn=None):
933 ##        dbinfo = {}
934 ##        for pragma in ('encoding', 'case_sensitive_like', 'locking_mode',
935 ##                       'page_size', 'read_uncommitted'):
936 ##            dbinfo[pragma] = self.get_pragma(pragma, conn)
937 ##        return dbinfo
938    
939     def get_pragma(self, name, conn=None):
940         data, _ = self.fetch("PRAGMA %s;" % name, conn=conn)
941         return data[0][0]
942    
943     def create(self):
944         self.connections._set_factory()
945         self.connections.get()
946    
947     def exists(self):
948         if self.name == ":memory:":
949             # Assume we're using a SingleConnection; creating the conn
950             # creates the DB.
951             return hasattr(self.connections, "_factory")
952         else:
953             # This should accept relative or absolute paths
954             return os.path.exists(self.name)
955    
956     def drop(self):
957         self.connections.shutdown()
958         if self.name != ":memory:":
959             # This should accept relative or absolute paths
960             os.remove(self.name)
961    
962     def quote(self, name):
963         """Return name, quoted for use in an SQL statement.
964         
965         From the SQLite docs:
966             Keywords can be used as identifiers in three ways:
967             'keyword'   Interpreted as a literal string if it occurs in a
968                         legal string context, otherwise as an identifier.
969             "keyword"   Interpreted as an identifier if it matches a known
970                         identifier and occurs in a legal identifier context,
971                         otherwise as a string.
972             [keyword]   Always interpreted as an identifier.
973         
974         ...we'll use the third option (square brackets).
975         """
976         return "[" + name + "]"
977    
978     def is_timeout_error(self, exc):
979         if not isinstance(exc, _sqlite.OperationalError):
980             return False
981         return exc.args[0] == 'database is locked'
982    
983     # If you get "OperationalError: ('database is locked',
984     #   'Waited 60 seconds for deadlock to clear.', 'SOME SQL;')",
985     # then you need to either increase the deadlock_timeout value
986     # until you stop getting that error, redesign your SQL commands
987     # to avoid deadlocks, or get an enterprise-class database.
988     deadlock_timeout = 20
989    
990     def execute(self, sql, conn=None):
991         """Return a native response for the given SQL."""
992         try:
993             if conn is None:
994                 conn = self.connections.get()
995             if isinstance(sql, unicode):
996                 sql = sql.encode(self.encoding)
997             self.log(sql)
998             start = time.time()
999             while True:
1000                 try:
1001                     return conn.execute(sql)
1002                 except (_sqlite.OperationalError, _sqlite.DatabaseError), x:
1003                     msg = x.args[0]
1004                     if ((msg.startswith("no such table") or
1005                          msg == "database schema has changed")):
1006                         if not self.connections.in_transaction():
1007                             # Bah. Shut down all connections and get a new one,
1008                             # since some previous connection changed the schema.
1009                             self.connections.shutdown()
1010                             conn = self.connections._factory()
1011                             continue
1012                     if self.is_timeout_error(x) and self.deadlock_timeout:
1013                         if time.time() - start < self.deadlock_timeout:
1014                             time.sleep(0.000001)
1015                             continue
1016                         else:
1017                             x.args += (("Waited %s seconds for deadlock to "
1018                                         "clear." % self.deadlock_timeout),)
1019                 raise
1020         except Exception, x:
1021             x.args += (sql,)
1022             # Dereference the connection so that release() is called back.
1023             conn = None
1024             raise
1025    
1026     def fetch(self, sql, conn=None):
1027         """Return rowdata, columns(name, type) for the given sql.
1028         
1029         sql should be a SQL string.
1030         
1031         rowdata will be an iterable of iterables containing the result values.
1032         columns will be an iterable of (column name, data type) pairs.
1033         """
1034         if _fetchall_required:
1035             cursor = self.execute(sql, conn)
1036             data = cursor.fetchall()
1037             if cursor.description is None:
1038                 # PySQLite has the odd behavior of returning a .description
1039                 # of None when the result set has no rows. Not much we can
1040                 # do about that for the general case. Fortunately, no part
1041                 # of geniusql reads this value.
1042                 coldefs = []
1043             else:
1044                 coldefs = [(c[0], c[1]) for c in cursor.description]
1045             return data, coldefs
1046         else:
1047             res = self.execute(sql, conn)
1048             return res.row_list, res.col_defs
1049    
1050     def version(self):
1051         return ("SQLite Version: %s\nPySQLite version: %s" %
1052                 (_version, _driver_version))
1053
Note: See TracBrowser for help on using the browser.