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
13 jaren geleden
14 jaren geleden
13 jaren geleden
14 jaren geleden
12 jaren geleden
13 jaren geleden
13 jaren geleden
13 jaren geleden
13 jaren geleden
13 jaren geleden
13 jaren geleden
13 jaren geleden
13 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
13 jaren geleden
13 jaren geleden
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  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. from __future__ import unicode_literals
  25. import MySQLdb
  26. import webnotes
  27. import conf
  28. import datetime
  29. class Database:
  30. """
  31. Open a database connection with the given parmeters, if use_default is True, use the
  32. login details from `conf.py`. This is called by the request handler and is accessible using
  33. the `conn` global variable. the `sql` method is also global to run queries
  34. """
  35. def __init__(self, host=None, user=None, password=None, ac_name=None, use_default = 0):
  36. self.host = host or 'localhost'
  37. self.user = user or conf.db_name
  38. if ac_name:
  39. self.user = self.get_db_login(ac_name) or conf.db_name
  40. if use_default:
  41. self.user = conf.db_name
  42. self.transaction_writes = 0
  43. self.auto_commit_on_many_writes = 0
  44. self.password = password or webnotes.get_db_password(self.user)
  45. self.connect()
  46. if self.user != 'root':
  47. self.use(self.user)
  48. def get_db_login(self, ac_name):
  49. return ac_name
  50. def connect(self):
  51. """
  52. Connect to a database
  53. """
  54. self._conn = MySQLdb.connect(user=self.user, host=self.host, passwd=self.password,
  55. use_unicode=True, charset='utf8')
  56. self._conn.converter[246]=float
  57. self._cursor = self._conn.cursor()
  58. def use(self, db_name):
  59. """
  60. `USE` db_name
  61. """
  62. self._conn.select_db(db_name)
  63. self.cur_db_name = db_name
  64. def validate_query(self, q):
  65. cmd = q.strip().lower().split()[0]
  66. if cmd in ['alter', 'drop', 'truncate'] and webnotes.user.name != 'Administrator':
  67. webnotes.msgprint('Not allowed to execute query')
  68. raise Exception
  69. def sql(self, query, values=(), as_dict = 0, as_list = 0, formatted = 0,
  70. debug=0, ignore_ddl=0, as_utf8=0, auto_commit=0, update=None):
  71. """
  72. * Execute a `query`, with given `values`
  73. * returns as a dictionary if as_dict = 1
  74. * returns as a list of lists (with cleaned up dates) if as_list = 1
  75. """
  76. # in transaction validations
  77. self.check_transaction_status(query)
  78. # autocommit
  79. if auto_commit: self.commit()
  80. # execute
  81. try:
  82. if values!=():
  83. if isinstance(values, dict):
  84. values = dict(values)
  85. if debug:
  86. try:
  87. self.explain_query(query, values)
  88. webnotes.errprint(query % values)
  89. except TypeError:
  90. webnotes.errprint([query, values])
  91. if getattr(conf, "logging", False)==2:
  92. webnotes.log("<<<< query")
  93. webnotes.log(query)
  94. webnotes.log("with values:")
  95. webnotes.log(values)
  96. webnotes.log(">>>>")
  97. self._cursor.execute(query, values)
  98. else:
  99. if debug:
  100. self.explain_query(query)
  101. webnotes.errprint(query)
  102. if getattr(conf, "logging", False)==2:
  103. webnotes.log("<<<< query")
  104. webnotes.log(query)
  105. webnotes.log(">>>>")
  106. self._cursor.execute(query)
  107. except Exception, e:
  108. # ignore data definition errors
  109. if ignore_ddl and e.args[0] in (1146,1054,1091):
  110. pass
  111. else:
  112. raise e
  113. if auto_commit: self.commit()
  114. # scrub output if required
  115. if as_dict:
  116. ret = self.fetch_as_dict(formatted, as_utf8)
  117. if update:
  118. for r in ret:
  119. r.update(update)
  120. return ret
  121. elif as_list:
  122. return self.convert_to_lists(self._cursor.fetchall(), formatted, as_utf8)
  123. elif as_utf8:
  124. return self.convert_to_lists(self._cursor.fetchall(), formatted, as_utf8)
  125. else:
  126. return self._cursor.fetchall()
  127. def explain_query(self, query, values=None):
  128. try:
  129. webnotes.errprint("--- query explain ---")
  130. if values is None:
  131. self._cursor.execute("explain " + query)
  132. else:
  133. self._cursor.execute("explain " + query, values)
  134. import json
  135. webnotes.errprint(json.dumps(self.fetch_as_dict(), indent=1))
  136. webnotes.errprint("--- query explain end ---")
  137. except:
  138. webnotes.errprint("error in query explain")
  139. def sql_list(self, query, values=(), debug=False):
  140. return [r[0] for r in self.sql(query, values, debug=debug)]
  141. def sql_ddl(self, query, values=()):
  142. self.commit()
  143. self.sql(query)
  144. def check_transaction_status(self, query):
  145. if self.transaction_writes and query and query.strip().split()[0].lower() in ['start', 'alter', 'drop', 'create', "begin"]:
  146. raise Exception, 'This statement can cause implicit commit'
  147. if query and query.strip().lower()=='commit':
  148. self.transaction_writes = 0
  149. if query[:6].lower() in ['update', 'insert']:
  150. self.transaction_writes += 1
  151. if self.transaction_writes > 10000:
  152. if self.auto_commit_on_many_writes:
  153. webnotes.conn.commit()
  154. webnotes.conn.begin()
  155. else:
  156. webnotes.msgprint('A very long query was encountered. If you are trying to import data, please do so using smaller files')
  157. raise Exception, 'Bad Query!!! Too many writes'
  158. def fetch_as_dict(self, formatted=0, as_utf8=0):
  159. result = self._cursor.fetchall()
  160. ret = []
  161. needs_formatting = self.needs_formatting(result, formatted)
  162. for r in result:
  163. row_dict = webnotes._dict({})
  164. for i in range(len(r)):
  165. if needs_formatting:
  166. val = self.convert_to_simple_type(r[i], formatted)
  167. else:
  168. val = r[i]
  169. if as_utf8 and type(val) is unicode:
  170. val = val.encode('utf-8')
  171. row_dict[self._cursor.description[i][0]] = val
  172. ret.append(row_dict)
  173. return ret
  174. def needs_formatting(self, result, formatted):
  175. if result and result[0]:
  176. for v in result[0]:
  177. if isinstance(v, (datetime.date, datetime.timedelta, datetime.datetime, long)):
  178. return True
  179. if formatted and isinstance(v, (int, float)):
  180. return True
  181. return False
  182. def get_description(self):
  183. return self._cursor.description
  184. def convert_to_simple_type(self, v, formatted=0):
  185. from webnotes.utils import formatdate, fmt_money
  186. if isinstance(v, (datetime.date, datetime.timedelta, datetime.datetime, long)):
  187. if isinstance(v, datetime.date):
  188. v = unicode(v)
  189. if formatted:
  190. v = formatdate(v)
  191. # time
  192. elif isinstance(v, (datetime.timedelta, datetime.datetime)):
  193. v = unicode(v)
  194. # long
  195. elif isinstance(v, long):
  196. v=int(v)
  197. # convert to strings... (if formatted)
  198. if formatted:
  199. if isinstance(v, float):
  200. v=fmt_money(v)
  201. elif isinstance(v, int):
  202. v = unicode(v)
  203. return v
  204. def convert_to_lists(self, res, formatted=0, as_utf8=0):
  205. nres = []
  206. needs_formatting = self.needs_formatting(res, formatted)
  207. for r in res:
  208. nr = []
  209. for c in r:
  210. if needs_formatting:
  211. val = self.convert_to_simple_type(c, formatted)
  212. else:
  213. val = c
  214. if as_utf8 and type(val) is unicode:
  215. val = val.encode('utf-8')
  216. nr.append(val)
  217. nres.append(nr)
  218. return nres
  219. def convert_to_utf8(self, res, formatted=0):
  220. nres = []
  221. for r in res:
  222. nr = []
  223. for c in r:
  224. if type(c) is unicode:
  225. c = c.encode('utf-8')
  226. nr.append(self.convert_to_simple_type(c, formatted))
  227. nres.append(nr)
  228. return nres
  229. def build_conditions(self, filters):
  230. def _build_condition(key):
  231. """
  232. filter's key is passed by map function
  233. build conditions like:
  234. * ifnull(`fieldname`, default_value) = %(fieldname)s
  235. * `fieldname` [=, !=, >, >=, <, <=] %(fieldname)s
  236. """
  237. _operator = "="
  238. value = filters.get(key)
  239. if isinstance(value, (list, tuple)):
  240. _operator = value[0]
  241. filters[key] = value[1]
  242. if _operator not in ["=", "!=", ">", ">=", "<", "<=", "like"]:
  243. _operator = "="
  244. if "[" in key:
  245. split_key = key.split("[")
  246. return "ifnull(`" + split_key[0] + "`, " + split_key[1][:-1] + ") " \
  247. + _operator + " %(" + key + ")s"
  248. else:
  249. return "`" + key + "` " + _operator + " %(" + key + ")s"
  250. if isinstance(filters, basestring):
  251. filters = { "name": filters }
  252. conditions = map(_build_condition, filters)
  253. return " and ".join(conditions), filters
  254. def get(self, doctype, filters=None, as_dict=True):
  255. return self.get_value(doctype, filters, "*", as_dict=as_dict)
  256. def get_value(self, doctype, filters=None, fieldname="name", ignore=None, as_dict=False, debug=False):
  257. """Get a single / multiple value from a record.
  258. For Single DocType, let filters be = None"""
  259. ret = self.get_values(doctype, filters, fieldname, ignore, as_dict, debug)
  260. return ret and ((len(ret[0]) > 1 or as_dict) and ret[0] or ret[0][0]) or None
  261. def get_values(self, doctype, filters=None, fieldname="name", ignore=None, as_dict=False, debug=False):
  262. if isinstance(filters, list):
  263. return self.get_value_for_many_names(doctype, filters, fieldname)
  264. fields = fieldname
  265. if fieldname!="*":
  266. if isinstance(fieldname, basestring):
  267. fields = [fieldname]
  268. else:
  269. fields = fieldname
  270. if (filters is not None) and (filters!=doctype or doctype=="DocType"):
  271. try:
  272. return self.get_values_from_table(fields, filters, doctype, as_dict, debug)
  273. except Exception, e:
  274. if e.args[0]!=1146:
  275. raise e
  276. # not a table, try in singles
  277. return self.get_values_from_single(fields, filters, doctype, as_dict, debug)
  278. def get_values_from_single(self, fields, filters, doctype, as_dict, debug):
  279. if fields=="*" or isinstance(filters, dict):
  280. r = self.sql("""select field, value from tabSingles where doctype=%s""", doctype)
  281. # check if single doc matches with filters
  282. values = webnotes._dict(r)
  283. if isinstance(filters, dict):
  284. for key, value in filters.items():
  285. if values.get(key) != value:
  286. return []
  287. if as_dict:
  288. return values and [values] or []
  289. if isinstance(fields, list):
  290. return [map(lambda d: values.get(d), fields)]
  291. else:
  292. r = self.sql("""select field, value
  293. from tabSingles where field in (%s) and doctype=%s""" \
  294. % (', '.join(['%s'] * len(fields)), '%s'),
  295. tuple(fields) + (doctype,), as_dict=False, debug=debug)
  296. if as_dict:
  297. return r and [webnotes._dict(r)] or []
  298. else:
  299. return r and [[i[1] for i in r]] or []
  300. def get_values_from_table(self, fields, filters, doctype, as_dict, debug):
  301. fl = fields
  302. if fields!="*":
  303. fl = ("`" + "`, `".join(fields) + "`")
  304. conditions, filters = self.build_conditions(filters)
  305. r = self.sql("select %s from `tab%s` where %s" % (fl, doctype,
  306. conditions), filters, as_dict=as_dict, debug=debug)
  307. return r
  308. def get_value_for_many_names(self, doctype, names, field):
  309. names = filter(None, names)
  310. return dict(self.sql("select name, `%s` from `tab%s` where name in (%s)" \
  311. % (field, doctype, ", ".join(map(lambda n: "'%s'" % n.replace("'", "\'"), names)))))
  312. def set_value(self, dt, dn, field, val, modified=None, modified_by=None):
  313. from webnotes.utils import now
  314. if dn and dt!=dn:
  315. self.sql("""update `tab%s` set `%s`=%s, modified=%s, modified_by=%s
  316. where name=%s""" % (dt, field, "%s", "%s", "%s", "%s"),
  317. (val, modified or now(), modified_by or webnotes.session["user"], dn))
  318. else:
  319. if self.sql("select value from tabSingles where field=%s and doctype=%s", (field, dt)):
  320. self.sql("""update tabSingles set value=%s where field=%s and doctype=%s""",
  321. (val, field, dt))
  322. else:
  323. self.sql("""insert into tabSingles(doctype, field, value)
  324. values (%s, %s, %s)""", (dt, field, val, ))
  325. if field!="modified":
  326. self.set_value(dt, dn, "modified", modified or now())
  327. def set(self, doc, field, val):
  328. from webnotes.utils import now
  329. doc.modified = now()
  330. doc.modified_by = webnotes.session["user"]
  331. self.set_value(doc.doctype, doc.name, field, val, doc.modified, doc.modified_by)
  332. doc.fields[field] = val
  333. def set_global(self, key, val, user='__global'):
  334. self.set_default(key, val, user)
  335. def get_global(self, key, user='__global'):
  336. return self.get_default(key, user)
  337. def set_default(self, key, val, parent="Control Panel"):
  338. """set control panel default (tabDefaultVal)"""
  339. import webnotes.defaults
  340. webnotes.defaults.set_default(key, val, parent)
  341. def add_default(self, key, val, parent="Control Panel"):
  342. import webnotes.defaults
  343. webnotes.defaults.add_default(key, val, parent)
  344. def get_default(self, key, parent="Control Panel"):
  345. """get default value"""
  346. import webnotes.defaults
  347. d = webnotes.defaults.get_defaults(parent).get(key)
  348. return isinstance(d, list) and d[0] or d
  349. def get_defaults_as_list(self, key, parent="Control Panel"):
  350. import webnotes.defaults
  351. d = webnotes.defaults.get_default(key, parent)
  352. return isinstance(d, basestring) and [d] or d
  353. def get_defaults(self, key=None, parent="Control Panel"):
  354. """get all defaults"""
  355. import webnotes.defaults
  356. if key:
  357. return webnotes.defaults.get_defaults(parent).get(key)
  358. else:
  359. return webnotes.defaults.get_defaults(parent)
  360. def begin(self):
  361. return # not required
  362. def commit(self):
  363. self.sql("commit")
  364. def rollback(self):
  365. self.sql("ROLLBACK")
  366. self.transaction_writes = 0
  367. def field_exists(self, dt, fn):
  368. return self.sql("select name from tabDocField where fieldname=%s and parent=%s", (dt, fn))
  369. def table_exists(self, tablename):
  370. return tablename in [d[0] for d in self.sql("show tables")]
  371. def exists(self, dt, dn=None):
  372. if isinstance(dt, basestring):
  373. if dt==dn:
  374. return True # single always exists (!)
  375. try:
  376. return self.sql('select name from `tab%s` where name=%s' % (dt, '%s'), dn)
  377. except:
  378. return None
  379. elif isinstance(dt, dict) and dt.get('doctype'):
  380. try:
  381. conditions = []
  382. for d in dt:
  383. if d == 'doctype': continue
  384. conditions.append('`%s` = "%s"' % (d, dt[d].replace('"', '\"')))
  385. return self.sql('select name from `tab%s` where %s' % \
  386. (dt['doctype'], " and ".join(conditions)))
  387. except:
  388. return None
  389. def get_table_columns(self, doctype):
  390. return [r[0] for r in self.sql("DESC `tab%s`" % doctype)]
  391. def close(self):
  392. if self._conn:
  393. self._cursor.close()
  394. self._conn.close()
  395. self._cursor = None
  396. self._conn = None