PageRenderTime 25ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/gluon/dal/adapters/sqlite.py

https://code.google.com/p/web2py/
Python | 280 lines | 268 code | 11 blank | 1 comment | 16 complexity | 0d699f0a7b791b8ae25853ad442449f3 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-2-Clause, MIT, BSD-3-Clause, Apache-2.0
  1. # -*- coding: utf-8 -*-
  2. import copy
  3. import datetime
  4. import locale
  5. import platform
  6. import re
  7. import sys
  8. import time
  9. from .._compat import PY2, pjoin
  10. from .._globals import IDENTITY
  11. from .base import BaseAdapter
  12. class SQLiteAdapter(BaseAdapter):
  13. drivers = ('sqlite2','sqlite3')
  14. can_select_for_update = None # support ourselves with BEGIN TRANSACTION
  15. def EXTRACT(self,field,what):
  16. return "web2py_extract('%s',%s)" % (what, self.expand(field))
  17. @staticmethod
  18. def web2py_extract(lookup, s):
  19. table = {
  20. 'year': (0, 4),
  21. 'month': (5, 7),
  22. 'day': (8, 10),
  23. 'hour': (11, 13),
  24. 'minute': (14, 16),
  25. 'second': (17, 19),
  26. }
  27. try:
  28. if lookup != 'epoch':
  29. (i, j) = table[lookup]
  30. return int(s[i:j])
  31. else:
  32. return time.mktime(datetime.datetime.strptime(s, '%Y-%m-%d %H:%M:%S').timetuple())
  33. except:
  34. return None
  35. @staticmethod
  36. def web2py_regexp(expression, item):
  37. return re.compile(expression).search(item) is not None
  38. def __init__(self, db, uri, pool_size=0, folder=None, db_codec ='UTF-8',
  39. credential_decoder=IDENTITY, driver_args={},
  40. adapter_args={}, do_connect=True, after_connection=None):
  41. self.db = db
  42. self.dbengine = "sqlite"
  43. self.uri = uri
  44. self.adapter_args = adapter_args
  45. if do_connect: self.find_driver(adapter_args)
  46. self.pool_size = 0
  47. self.folder = folder
  48. self.db_codec = db_codec
  49. self._after_connection = after_connection
  50. self.find_or_make_work_folder()
  51. path_encoding = sys.getfilesystemencoding() \
  52. or locale.getdefaultlocale()[1] or 'utf8'
  53. if uri.startswith('sqlite:memory'):
  54. self.dbpath = ':memory:'
  55. else:
  56. self.dbpath = uri.split('://',1)[1]
  57. if self.dbpath[0] != '/':
  58. if PY2:
  59. self.dbpath = pjoin(
  60. self.folder.decode(path_encoding).encode('utf8'), self.dbpath)
  61. else:
  62. self.dbpath = pjoin(self.folder, self.dbpath)
  63. if not 'check_same_thread' in driver_args:
  64. driver_args['check_same_thread'] = False
  65. if not 'detect_types' in driver_args and do_connect:
  66. driver_args['detect_types'] = self.driver.PARSE_DECLTYPES
  67. def connector(dbpath=self.dbpath, driver_args=driver_args):
  68. return self.driver.Connection(dbpath, **driver_args)
  69. self.connector = connector
  70. if do_connect: self.reconnect()
  71. def after_connection(self):
  72. self.connection.create_function('web2py_extract', 2,
  73. SQLiteAdapter.web2py_extract)
  74. self.connection.create_function("REGEXP", 2,
  75. SQLiteAdapter.web2py_regexp)
  76. if self.adapter_args.get('foreign_keys',True):
  77. self.execute('PRAGMA foreign_keys=ON;')
  78. def _truncate(self, table, mode=''):
  79. tablename = table._tablename
  80. return ['DELETE FROM %s;' % tablename,
  81. "DELETE FROM sqlite_sequence WHERE name='%s';" % tablename]
  82. def lastrowid(self, table):
  83. return self.cursor.lastrowid
  84. def REGEXP(self,first,second):
  85. return '(%s REGEXP %s)' % (self.expand(first),
  86. self.expand(second,'string'))
  87. def delete(self, tablename, query):
  88. # SQLite requires its own delete to handle CASCADE
  89. db = self.db
  90. table = db[tablename]
  91. deleted = [x[table._id.name] for x in db(query).select(table._id)]
  92. counter = super(SQLiteAdapter, self).delete(tablename, query)
  93. if counter:
  94. for field in table._referenced_by:
  95. if field.type == 'reference '+ tablename \
  96. and field.ondelete == 'CASCADE':
  97. db(field.belongs(deleted)).delete()
  98. return counter
  99. def select(self, query, fields, attributes):
  100. """
  101. Simulate `SELECT ... FOR UPDATE` with `BEGIN IMMEDIATE TRANSACTION`.
  102. Note that the entire database, rather than one record, is locked
  103. (it will be locked eventually anyway by the following UPDATE).
  104. """
  105. if attributes.get('for_update', False) and not 'cache' in attributes:
  106. self.execute('BEGIN IMMEDIATE TRANSACTION;')
  107. return super(SQLiteAdapter, self).select(query, fields, attributes)
  108. SPATIALLIBS = {
  109. 'Windows':'libspatialite',
  110. 'Linux':'libspatialite.so',
  111. 'Darwin':'libspatialite.dylib'
  112. }
  113. class SpatiaLiteAdapter(SQLiteAdapter):
  114. drivers = ('sqlite3','sqlite2')
  115. types = copy.copy(BaseAdapter.types)
  116. types.update(geometry='GEOMETRY')
  117. def __init__(self, db, uri, pool_size=0, folder=None, db_codec ='UTF-8',
  118. credential_decoder=IDENTITY, driver_args={},
  119. adapter_args={}, do_connect=True, srid=4326, after_connection=None):
  120. self.db = db
  121. self.dbengine = "spatialite"
  122. self.uri = uri
  123. if do_connect: self.find_driver(adapter_args)
  124. self.pool_size = 0
  125. self.folder = folder
  126. self.db_codec = db_codec
  127. self._after_connection = after_connection
  128. self.find_or_make_work_folder()
  129. self.srid = srid
  130. path_encoding = sys.getfilesystemencoding() \
  131. or locale.getdefaultlocale()[1] or 'utf8'
  132. if uri.startswith('spatialite:memory'):
  133. self.dbpath = ':memory:'
  134. else:
  135. self.dbpath = uri.split('://',1)[1]
  136. if self.dbpath[0] != '/':
  137. self.dbpath = pjoin(
  138. self.folder.decode(path_encoding).encode('utf8'), self.dbpath)
  139. if not 'check_same_thread' in driver_args:
  140. driver_args['check_same_thread'] = False
  141. if not 'detect_types' in driver_args and do_connect:
  142. driver_args['detect_types'] = self.driver.PARSE_DECLTYPES
  143. def connector(dbpath=self.dbpath, driver_args=driver_args):
  144. return self.driver.Connection(dbpath, **driver_args)
  145. self.connector = connector
  146. if do_connect: self.reconnect()
  147. def after_connection(self):
  148. self.connection.enable_load_extension(True)
  149. # for Windows, rename libspatialite-2.dll to libspatialite.dll
  150. # Linux uses libspatialite.so
  151. # Mac OS X uses libspatialite.dylib
  152. libspatialite = SPATIALLIBS[platform.system()]
  153. self.execute(r'SELECT load_extension("%s");' % libspatialite)
  154. self.connection.create_function('web2py_extract', 2,
  155. SQLiteAdapter.web2py_extract)
  156. self.connection.create_function("REGEXP", 2,
  157. SQLiteAdapter.web2py_regexp)
  158. # GIS functions
  159. def ST_ASGEOJSON(self, first, second):
  160. return 'AsGeoJSON(%s,%s,%s)' %(self.expand(first),
  161. second['precision'], second['options'])
  162. def ST_ASTEXT(self, first):
  163. return 'AsText(%s)' %(self.expand(first))
  164. def ST_CONTAINS(self, first, second):
  165. return 'Contains(%s,%s)' %(self.expand(first),
  166. self.expand(second, first.type))
  167. def ST_DISTANCE(self, first, second):
  168. return 'Distance(%s,%s)' %(self.expand(first),
  169. self.expand(second, first.type))
  170. def ST_EQUALS(self, first, second):
  171. return 'Equals(%s,%s)' %(self.expand(first),
  172. self.expand(second, first.type))
  173. def ST_INTERSECTS(self, first, second):
  174. return 'Intersects(%s,%s)' %(self.expand(first),
  175. self.expand(second, first.type))
  176. def ST_OVERLAPS(self, first, second):
  177. return 'Overlaps(%s,%s)' %(self.expand(first),
  178. self.expand(second, first.type))
  179. def ST_SIMPLIFY(self, first, second):
  180. return 'Simplify(%s,%s)' %(self.expand(first),
  181. self.expand(second, 'double'))
  182. def ST_TOUCHES(self, first, second):
  183. return 'Touches(%s,%s)' %(self.expand(first),
  184. self.expand(second, first.type))
  185. def ST_WITHIN(self, first, second):
  186. return 'Within(%s,%s)' %(self.expand(first),
  187. self.expand(second, first.type))
  188. def represent(self, obj, fieldtype):
  189. field_is_type = fieldtype.startswith
  190. if field_is_type('geo'):
  191. srid = 4326 # Spatialite default srid for geometry
  192. geotype, parms = fieldtype[:-1].split('(')
  193. parms = parms.split(',')
  194. if len(parms) >= 2:
  195. schema, srid = parms[:2]
  196. # if field_is_type('geometry'):
  197. value = "ST_GeomFromText('%s',%s)" %(obj, srid)
  198. # elif field_is_type('geography'):
  199. # value = "ST_GeogFromText('SRID=%s;%s')" %(srid, obj)
  200. # else:
  201. # raise SyntaxError, 'Invalid field type %s' %fieldtype
  202. return value
  203. return BaseAdapter.represent(self, obj, fieldtype)
  204. class JDBCSQLiteAdapter(SQLiteAdapter):
  205. drivers = ('zxJDBC_sqlite',)
  206. def __init__(self, db, uri, pool_size=0, folder=None, db_codec='UTF-8',
  207. credential_decoder=IDENTITY, driver_args={},
  208. adapter_args={}, do_connect=True, after_connection=None):
  209. self.db = db
  210. self.dbengine = "sqlite"
  211. self.uri = uri
  212. if do_connect: self.find_driver(adapter_args)
  213. self.pool_size = pool_size
  214. self.folder = folder
  215. self.db_codec = db_codec
  216. self._after_connection = after_connection
  217. self.find_or_make_work_folder()
  218. path_encoding = sys.getfilesystemencoding() \
  219. or locale.getdefaultlocale()[1] or 'utf8'
  220. if uri.startswith('sqlite:memory'):
  221. self.dbpath = ':memory:'
  222. else:
  223. self.dbpath = uri.split('://',1)[1]
  224. if self.dbpath[0] != '/':
  225. self.dbpath = pjoin(
  226. self.folder.decode(path_encoding).encode('utf8'), self.dbpath)
  227. def connector(dbpath=self.dbpath,driver_args=driver_args):
  228. return self.driver.connect(
  229. self.driver.getConnection('jdbc:sqlite:'+dbpath),
  230. **driver_args)
  231. self.connector = connector
  232. if do_connect: self.reconnect()
  233. def after_connection(self):
  234. # FIXME http://www.zentus.com/sqlitejdbc/custom_functions.html for UDFs
  235. self.connection.create_function('web2py_extract', 2,
  236. SQLiteAdapter.web2py_extract)
  237. def execute(self, a):
  238. return self.log_execute(a)