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.

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