You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

13 jaren geleden
14 jaren geleden
14 jaren geleden
13 jaren geleden
13 jaren geleden
14 jaren geleden
13 jaren geleden
13 jaren geleden
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. # Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
  2. #
  3. # MIT License (MIT)
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a
  6. # copy of this software and associated documentation files (the "Software"),
  7. # to deal in the Software without restriction, including without limitation
  8. # the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. # and/or sell copies of the Software, and to permit persons to whom the
  10. # Software is furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  16. # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  17. # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  19. # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  20. # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. #
  22. # Database Module
  23. # --------------------
  24. import MySQLdb
  25. import webnotes
  26. import conf
  27. class Database:
  28. """
  29. Open a database connection with the given parmeters, if use_default is True, use the
  30. login details from `conf.py`. This is called by the request handler and is accessible using
  31. the `conn` global variable. the `sql` method is also global to run queries
  32. """
  33. def __init__(self, host=None, user=None, password=None, ac_name=None, use_default = 0):
  34. self.host = host or 'localhost'
  35. self.user = user or getattr(conf, 'default_db_name', '')
  36. if ac_name:
  37. self.user = self.get_db_login(ac_name) or conf.db_name
  38. if use_default:
  39. self.user = conf.db_name
  40. self.is_testing = 0
  41. self.in_transaction = 0
  42. self.transaction_writes = 0
  43. self.testing_tables = []
  44. self.auto_commit_on_many_writes = 0
  45. self.password = password or webnotes.get_db_password(self.user)
  46. self.connect()
  47. if self.user != 'root':
  48. self.use(self.user)
  49. def get_db_login(self, ac_name):
  50. return ac_name
  51. def connect(self):
  52. """
  53. Connect to a database
  54. """
  55. self._conn = MySQLdb.connect(user=self.user, host=self.host, passwd=self.password, use_unicode=True)
  56. self._conn.converter[246]=float
  57. self._conn.set_character_set('utf8')
  58. self._cursor = self._conn.cursor()
  59. def use(self, db_name):
  60. """
  61. `USE` db_name
  62. """
  63. self._conn.select_db(db_name)
  64. self.cur_db_name = db_name
  65. def check_transaction_status(self, query):
  66. """
  67. Update *in_transaction* and check if "START TRANSACTION" is not called twice
  68. """
  69. if self.in_transaction and query and query.strip().split()[0].lower() in ['start', 'alter', 'drop', 'create']:
  70. raise Exception, 'This statement can cause implicit commit'
  71. if query and query.strip().lower()=='start transaction':
  72. self.in_transaction = 1
  73. self.transaction_writes = 0
  74. if query and query.strip().split()[0].lower() in ['commit', 'rollback']:
  75. self.in_transaction = 0
  76. if self.in_transaction and query[:6].lower() in ['update', 'insert']:
  77. self.transaction_writes += 1
  78. if self.transaction_writes > 10000:
  79. if self.auto_commit_on_many_writes:
  80. webnotes.conn.commit()
  81. webnotes.conn.begin()
  82. else:
  83. webnotes.msgprint('A very long query was encountered. If you are trying to import data, please do so using smaller files')
  84. raise Exception, 'Bad Query!!! Too many writes'
  85. def fetch_as_dict(self, formatted=0):
  86. """
  87. Internal - get results as dictionary
  88. """
  89. result = self._cursor.fetchall()
  90. ret = []
  91. for r in result:
  92. dict = {}
  93. for i in range(len(r)):
  94. dict[self._cursor.description[i][0]] = self.convert_to_simple_type(r[i], formatted)
  95. ret.append(dict)
  96. return ret
  97. def validate_query(self, q):
  98. cmd = q.strip().lower().split()[0]
  99. if cmd in ['alter', 'drop', 'truncate'] and webnotes.user.name != 'Administrator':
  100. webnotes.msgprint('Not allowed to execute query')
  101. raise Execption
  102. # ======================================================================================
  103. def sql(self, query, values=(), as_dict = 0, as_list = 0, formatted = 0, ignore_no_table = 1, debug=0, ignore_ddl=0):
  104. """
  105. * Execute a `query`, with given `values`
  106. * returns as a dictionary if as_dict = 1
  107. * returns as a list of lists (with cleaned up dates) if as_list = 1
  108. """
  109. # in transaction validations
  110. self.check_transaction_status(query)
  111. # execute
  112. try:
  113. if values!=():
  114. if debug: webnotes.msgprint(query % values)
  115. self._cursor.execute(query, values)
  116. else:
  117. if debug: webnotes.msgprint(query)
  118. self._cursor.execute(query)
  119. except Exception, e:
  120. # ignore data definition errors
  121. if ignore_ddl and e.args[0] in (1146,1054,1091):
  122. pass
  123. else:
  124. raise e
  125. # scrub output if required
  126. if as_dict:
  127. return self.fetch_as_dict(formatted)
  128. elif as_list:
  129. return self.convert_to_lists(self._cursor.fetchall(), formatted)
  130. else:
  131. return self._cursor.fetchall()
  132. def get_description(self):
  133. """
  134. Get metadata of the last query
  135. """
  136. return self._cursor.description
  137. # ======================================================================================
  138. def convert_to_simple_type(self, v, formatted=0):
  139. import datetime
  140. from webnotes.utils import formatdate, fmt_money
  141. # date
  142. if type(v)==datetime.date:
  143. v = str(v)
  144. if formatted:
  145. v = formatdate(v)
  146. # time
  147. elif type(v)==datetime.timedelta:
  148. h = int(v.seconds/60/60)
  149. v = str(h) + ':' + str(v.seconds/60 - h*60)
  150. if v[1]==':':
  151. v='0'+v
  152. # datetime
  153. elif type(v)==datetime.datetime:
  154. v = str(v)
  155. # long
  156. elif type(v)==long:
  157. v=int(v)
  158. # convert to strings... (if formatted)
  159. if formatted:
  160. if type(v)==float:
  161. v=fmt_money(v)
  162. if type(v)==int:
  163. v=str(v)
  164. return v
  165. # ======================================================================================
  166. def convert_to_lists(self, res, formatted=0):
  167. """
  168. Convert the given result set to a list of lists (with cleaned up dates and decimals)
  169. """
  170. nres = []
  171. for r in res:
  172. nr = []
  173. for c in r:
  174. nr.append(self.convert_to_simple_type(c, formatted))
  175. nres.append(nr)
  176. return nres
  177. # ======================================================================================
  178. def replace_tab_by_test(self, query):
  179. """
  180. Relace all ``tab`` + doctype to ``test`` + doctype
  181. """
  182. if self.is_testing:
  183. tl = self.get_testing_tables()
  184. for t in tl:
  185. query = query.replace(t, 'test' + t[3:])
  186. return query
  187. def get_testing_tables(self):
  188. """
  189. Get list of all tables for which `tab` is to be replaced by `test` before a query is executed
  190. """
  191. if not self.testing_tables:
  192. testing_tables = ['tab'+r[0] for r in self.sql('SELECT name from tabDocType where docstatus<2 and (issingle=0 or issingle is null)', allow_testing = 0)]
  193. testing_tables+=['tabSeries','tabSingles'] # tabSessions is not included here
  194. return self.testing_tables
  195. # ======================================================================================
  196. # get a single value from a record
  197. def get_value(self, doctype, docname, fieldname, ignore=None):
  198. """
  199. Get a single / multiple value from a record.
  200. For Single DocType, let docname be = None
  201. """
  202. fl = fieldname
  203. if docname and (docname!=doctype or docname=='DocType'):
  204. if type(fieldname) in (list, tuple):
  205. fl = '`, `'.join(fieldname)
  206. try:
  207. r = self.sql("select `%s` from `tab%s` where name='%s'" % (fl, doctype, docname))
  208. except Exception, e:
  209. if e.args[0]==1054 and ignore:
  210. return None
  211. else:
  212. raise e
  213. return r and (len(r[0]) > 1 and r[0] or r[0][0]) or None
  214. else:
  215. if type(fieldname) in (list, tuple):
  216. fl = "', '".join(fieldname)
  217. r = self.sql("select value from tabSingles where field in ('%s') and doctype='%s'" % (fieldname, doctype))
  218. return r and (len(r) > 1 and (i[0] for i in r) or r[0][0]) or None
  219. def set_value(self, dt, dn, field, val):
  220. from webnotes.utils import now
  221. if dn and dt!=dn:
  222. self.sql("update `tab"+dt+"` set `"+field+"`=%s, modified=%s where name=%s", (val, now(), dn))
  223. else:
  224. if self.sql("select value from tabSingles where field=%s and doctype=%s", (field, dt)):
  225. self.sql("update tabSingles set value=%s where field=%s and doctype=%s", (val, field, dt))
  226. else:
  227. self.sql("insert into tabSingles(doctype, field, value) values (%s, %s, %s)", (dt, field, val))
  228. def set(self, doc, field, val):
  229. self.set_value(doc.doctype, doc.name, field, val)
  230. doc.fields[field] = val
  231. # ======================================================================================
  232. def set_global(self, key, val, user='__global'):
  233. res = self.sql('select defkey from `tabDefaultValue` where defkey=%s and parent=%s', (key, user))
  234. if res:
  235. self.sql('update `tabDefaultValue` set defvalue=%s where parent=%s and defkey=%s', (str(val), user, key))
  236. else:
  237. self.sql('insert into `tabDefaultValue` (name, defkey, defvalue, parent) values (%s,%s,%s,%s)', (user+'_'+key, key, str(val), user))
  238. def get_global(self, key, user='__global'):
  239. g = self.sql("select defvalue from tabDefaultValue where defkey=%s and parent=%s", (key, user))
  240. return g and g[0][0] or None
  241. # ======================================================================================
  242. def set_default(self, key, val):
  243. """set control panel default (tabDefaultVal)"""
  244. if self.sql("""select defkey from `tabDefaultValue` where
  245. defkey=%s and parent = "Control Panel" """, key):
  246. # update
  247. self.sql("""update `tabDefaultValue` set defvalue=%s
  248. where parent = "Control Panel" and defkey=%s""", (val, key))
  249. else:
  250. from webnotes.model.doc import Document
  251. d = Document('DefaultValue')
  252. d.parent = 'Control Panel'
  253. d.parenttype = 'Control Panel'
  254. d.parentfield = 'system_defaults'
  255. d.defkey = key
  256. d.defvalue = val
  257. d.save(1)
  258. def get_default(self, key):
  259. """get default value"""
  260. ret = self.sql("""select defvalue from tabDefaultValue where defkey=%s""", key)
  261. return ret and ret[0][0] or None
  262. def get_defaults(self, key=None):
  263. """get all defaults"""
  264. if key:
  265. return self.get_default(key)
  266. else:
  267. res = self.sql("""select defkey, defvalue from `tabDefaultValue`
  268. where parent = "Control Panel" """)
  269. d = {}
  270. for rec in res:
  271. d[rec[0]] = rec[1] or ''
  272. return d
  273. def begin(self):
  274. if not self.in_transaction:
  275. self.sql("start transaction")
  276. def commit(self):
  277. self.sql("commit")
  278. def rollback(self):
  279. self.sql("ROLLBACK")
  280. # ======================================================================================
  281. def field_exists(self, dt, fn):
  282. """
  283. Returns True if `fn` exists in `DocType` `dt`
  284. """
  285. return self.sql("select name from tabDocField where fieldname=%s and parent=%s", (dt, fn))
  286. def exists(self, dt, dn=None):
  287. """
  288. Returns true if the record exists
  289. """
  290. if isinstance(dt, basestring):
  291. try:
  292. return self.sql('select name from `tab%s` where name=%s' % (dt, '%s'), dn)
  293. except:
  294. return None
  295. elif isinstance(dt, dict) and dt.get('doctype'):
  296. try:
  297. conditions = []
  298. for d in dt:
  299. if d == 'doctype': continue
  300. conditions.append('`%s` = "%s"' % (d, dt[d].replace('"', '\"')))
  301. return self.sql('select name from `tab%s` where %s' % \
  302. (dt['doctype'], " and ".join(conditions)))
  303. except:
  304. return None
  305. # ======================================================================================
  306. def close(self):
  307. """
  308. Close my connection
  309. """
  310. if self._conn:
  311. self._cursor.close()
  312. self._conn.close()
  313. self._cursor = None
  314. self._conn = None