Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

13 роки тому
13 роки тому
14 роки тому
13 роки тому
14 роки тому
12 роки тому
13 роки тому
13 роки тому
13 роки тому
13 роки тому
13 роки тому
13 роки тому
13 роки тому
13 роки тому
13 роки тому
13 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
12 роки тому
13 роки тому
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  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. class Database:
  29. """
  30. Open a database connection with the given parmeters, if use_default is True, use the
  31. login details from `conf.py`. This is called by the request handler and is accessible using
  32. the `conn` global variable. the `sql` method is also global to run queries
  33. """
  34. def __init__(self, host=None, user=None, password=None, ac_name=None, use_default = 0):
  35. self.host = host or 'localhost'
  36. self.user = user or conf.db_name
  37. if ac_name:
  38. self.user = self.get_db_login(ac_name) or conf.db_name
  39. if use_default:
  40. self.user = conf.db_name
  41. self.in_transaction = 0
  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, use_unicode=True, charset='utf8')
  55. self._conn.converter[246]=float
  56. self._cursor = self._conn.cursor()
  57. def use(self, db_name):
  58. """
  59. `USE` db_name
  60. """
  61. self._conn.select_db(db_name)
  62. self.cur_db_name = db_name
  63. def validate_query(self, q):
  64. cmd = q.strip().lower().split()[0]
  65. if cmd in ['alter', 'drop', 'truncate'] and webnotes.user.name != 'Administrator':
  66. webnotes.msgprint('Not allowed to execute query')
  67. raise Execption
  68. def sql(self, query, values=(), as_dict = 0, as_list = 0, formatted = 0,
  69. debug=0, ignore_ddl=0, as_utf8=0, auto_commit=0, update=None):
  70. """
  71. * Execute a `query`, with given `values`
  72. * returns as a dictionary if as_dict = 1
  73. * returns as a list of lists (with cleaned up dates) if as_list = 1
  74. """
  75. # in transaction validations
  76. self.check_transaction_status(query)
  77. # autocommit
  78. if auto_commit and self.in_transaction: self.commit()
  79. if auto_commit: self.begin()
  80. # execute
  81. try:
  82. if values!=():
  83. if isinstance(values, dict):
  84. values = dict(values)
  85. if debug: webnotes.errprint(query % values)
  86. self._cursor.execute(query, values)
  87. else:
  88. if debug: webnotes.errprint(query)
  89. self._cursor.execute(query)
  90. except Exception, e:
  91. # ignore data definition errors
  92. if ignore_ddl and e.args[0] in (1146,1054,1091):
  93. pass
  94. else:
  95. raise e
  96. if auto_commit: self.commit()
  97. # scrub output if required
  98. if as_dict:
  99. ret = self.fetch_as_dict(formatted, as_utf8)
  100. if update:
  101. for r in ret:
  102. r.update(update)
  103. return ret
  104. elif as_list:
  105. return self.convert_to_lists(self._cursor.fetchall(), formatted, as_utf8)
  106. elif as_utf8:
  107. return self.convert_to_lists(self._cursor.fetchall(), formatted, as_utf8)
  108. else:
  109. return self._cursor.fetchall()
  110. def check_transaction_status(self, query):
  111. if self.in_transaction and query and query.strip().split()[0].lower() in ['start', 'alter', 'drop', 'create']:
  112. raise Exception, 'This statement can cause implicit commit'
  113. if query and query.strip().lower()=='start transaction':
  114. self.in_transaction = 1
  115. self.transaction_writes = 0
  116. if query and query.strip().split()[0].lower() in ['commit', 'rollback']:
  117. self.in_transaction = 0
  118. if self.in_transaction and query[:6].lower() in ['update', 'insert']:
  119. self.transaction_writes += 1
  120. if self.transaction_writes > 10000:
  121. if self.auto_commit_on_many_writes:
  122. webnotes.conn.commit()
  123. webnotes.conn.begin()
  124. else:
  125. webnotes.msgprint('A very long query was encountered. If you are trying to import data, please do so using smaller files')
  126. raise Exception, 'Bad Query!!! Too many writes'
  127. def fetch_as_dict(self, formatted=0, as_utf8=0):
  128. result = self._cursor.fetchall()
  129. ret = []
  130. for r in result:
  131. row_dict = webnotes._dict({})
  132. for i in range(len(r)):
  133. val = self.convert_to_simple_type(r[i], formatted)
  134. if as_utf8 and type(val) is unicode:
  135. val = val.encode('utf-8')
  136. row_dict[self._cursor.description[i][0]] = val
  137. ret.append(row_dict)
  138. return ret
  139. def get_description(self):
  140. return self._cursor.description
  141. def convert_to_simple_type(self, v, formatted=0):
  142. import datetime
  143. from webnotes.utils import formatdate, fmt_money
  144. # date
  145. if type(v)==datetime.date:
  146. v = unicode(v)
  147. if formatted:
  148. v = formatdate(v)
  149. # time
  150. elif type(v)==datetime.timedelta:
  151. v = unicode(v)
  152. # datetime
  153. elif type(v)==datetime.datetime:
  154. v = unicode(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. def convert_to_lists(self, res, formatted=0, as_utf8=0):
  166. nres = []
  167. for r in res:
  168. nr = []
  169. for c in r:
  170. val = self.convert_to_simple_type(c, formatted)
  171. if as_utf8 and type(val) is unicode:
  172. val = val.encode('utf-8')
  173. nr.append(val)
  174. nres.append(nr)
  175. return nres
  176. def convert_to_utf8(self, res, formatted=0):
  177. nres = []
  178. for r in res:
  179. nr = []
  180. for c in r:
  181. if type(c) is unicode:
  182. c = c.encode('utf-8')
  183. nr.append(self.convert_to_simple_type(c, formatted))
  184. nres.append(nr)
  185. return nres
  186. def build_conditions(self, filters):
  187. def _build_condition(key):
  188. """
  189. filter's key is passed by map function
  190. build conditions like:
  191. * ifnull(`fieldname`, default_value) = %(fieldname)s
  192. * `fieldname` = %(fieldname)s
  193. """
  194. if "[" in key:
  195. split_key = key.split("[")
  196. return "ifnull(`" + split_key[0] + "`, " + split_key[1][:-1] + ") = %(" + key + ")s"
  197. else:
  198. return "`" + key + "` = %(" + key + ")s"
  199. if isinstance(filters, basestring):
  200. filters = { "name": filters }
  201. conditions = map(_build_condition, filters)
  202. return " and ".join(conditions), filters
  203. def get_value(self, doctype, filters=None, fieldname="name", ignore=None, as_dict=False):
  204. """Get a single / multiple value from a record.
  205. For Single DocType, let filters be = None"""
  206. if filters is not None and (filters!=doctype or filters=='DocType'):
  207. fl = isinstance(fieldname, basestring) and fieldname or "`, `".join(fieldname)
  208. conditions, filters = self.build_conditions(filters)
  209. try:
  210. r = self.sql("select `%s` from `tab%s` where %s" % (fl, doctype,
  211. conditions), filters, as_dict)
  212. except Exception, e:
  213. if e.args[0]==1054 and ignore:
  214. return None
  215. else:
  216. raise e
  217. return r and (len(r[0]) > 1 and r[0] or r[0][0]) or None
  218. else:
  219. fieldname = isinstance(fieldname, basestring) and [fieldname] or fieldname
  220. r = self.sql("select field, value from tabSingles where field in (%s) and \
  221. doctype=%s" % (', '.join(['%s']*len(fieldname)), '%s'), tuple(fieldname) + (doctype,), as_dict=False)
  222. if as_dict:
  223. return r and webnotes._dict(r) or None
  224. else:
  225. return r and (len(r) > 1 and [i[0] for i in r] or r[0][1]) or None
  226. def set_value(self, dt, dn, field, val, modified=None, modified_by=None):
  227. from webnotes.utils import now
  228. if dn and dt!=dn:
  229. self.sql("""update `tab%s` set `%s`=%s, modified=%s, modified_by=%s
  230. where name=%s""" % (dt, field, "%s", "%s", "%s", "%s"),
  231. (val, modified or now(), modified_by or webnotes.session["user"], dn))
  232. else:
  233. if self.sql("select value from tabSingles where field=%s and doctype=%s", (field, dt)):
  234. self.sql("update tabSingles set value=%s where field=%s and doctype=%s", (val, field, dt))
  235. else:
  236. self.sql("insert into tabSingles(doctype, field, value) values (%s, %s, %s)", (dt, field, val))
  237. def set(self, doc, field, val):
  238. from webnotes.utils import now
  239. doc.modified = now()
  240. doc.modified_by = webnotes.session["user"]
  241. self.set_value(doc.doctype, doc.name, field, val, doc.modified, doc.modified_by)
  242. doc.fields[field] = val
  243. def set_global(self, key, val, user='__global'):
  244. res = self.sql('select defkey from `tabDefaultValue` where defkey=%s and parent=%s', (key, user))
  245. if res:
  246. self.sql('update `tabDefaultValue` set defvalue=%s where parent=%s and defkey=%s', (str(val), user, key))
  247. else:
  248. self.sql('insert into `tabDefaultValue` (name, defkey, defvalue, parent) values (%s,%s,%s,%s)', (user+'_'+key, key, str(val), user))
  249. def get_global(self, key, user='__global'):
  250. g = self.sql("select defvalue from tabDefaultValue where defkey=%s and parent=%s", (key, user))
  251. return g and g[0][0] or None
  252. def get_globals_like(self, key):
  253. return [g[0] for g in self.sql("""select defvalue from tabDefaultValue
  254. where defkey like %s and parent='__global'""", key)]
  255. def set_default(self, key, val, parent="Control Panel"):
  256. """set control panel default (tabDefaultVal)"""
  257. if self.sql("""select defkey from `tabDefaultValue` where
  258. defkey=%s and parent=%s """, (key, parent)):
  259. # update
  260. self.sql("""update `tabDefaultValue` set defvalue=%s
  261. where parent=%s and defkey=%s""", (val, parent, key))
  262. webnotes.clear_cache()
  263. else:
  264. self.add_default(key, val, parent)
  265. def add_default(self, key, val, parent="Control Panel"):
  266. d = webnotes.doc('DefaultValue')
  267. d.parent = parent
  268. d.parenttype = 'Control Panel' # does not matter
  269. d.parentfield = 'system_defaults'
  270. d.defkey = key
  271. d.defvalue = val
  272. d.save(1)
  273. webnotes.clear_cache()
  274. def get_default(self, key, parent="Control Panel"):
  275. """get default value"""
  276. ret = self.get_defaults_as_list(key, parent)
  277. return ret and ret[0] or None
  278. def get_defaults_as_list(self, key, parent="Control Panel"):
  279. ret = [r[0] for r in self.sql("""select defvalue from \
  280. tabDefaultValue where defkey=%s and parent=%s""", (key, parent))]
  281. if key in ["owner", "user"] and webnotes.session:
  282. ret.append(webnotes.session.user)
  283. return ret
  284. def get_defaults(self, key=None, parent="Control Panel"):
  285. """get all defaults"""
  286. if key:
  287. return self.get_default(key, parent)
  288. else:
  289. res = self.sql("""select defkey, defvalue from `tabDefaultValue`
  290. where parent = %s""", parent, as_dict=1)
  291. defaults = webnotes._dict({})
  292. for d in res:
  293. if d.defkey in defaults:
  294. # listify
  295. if isinstance(defaults[d.defkey], basestring):
  296. defaults[d.defkey] = [defaults[d.defkey]]
  297. defaults[d.defkey].append(d.defvalue)
  298. else:
  299. defaults[d.defkey] = d.defvalue
  300. return defaults
  301. def begin(self):
  302. if not self.in_transaction:
  303. self.sql("start transaction")
  304. def commit(self):
  305. self.sql("commit")
  306. def rollback(self):
  307. self.sql("ROLLBACK")
  308. def field_exists(self, dt, fn):
  309. return self.sql("select name from tabDocField where fieldname=%s and parent=%s", (dt, fn))
  310. def table_exists(self, tablename):
  311. return tablename in [d[0] for d in self.sql("show tables")]
  312. def exists(self, dt, dn=None):
  313. if isinstance(dt, basestring):
  314. try:
  315. return self.sql('select name from `tab%s` where name=%s' % (dt, '%s'), dn)
  316. except:
  317. return None
  318. elif isinstance(dt, dict) and dt.get('doctype'):
  319. try:
  320. conditions = []
  321. for d in dt:
  322. if d == 'doctype': continue
  323. conditions.append('`%s` = "%s"' % (d, dt[d].replace('"', '\"')))
  324. return self.sql('select name from `tab%s` where %s' % \
  325. (dt['doctype'], " and ".join(conditions)))
  326. except:
  327. return None
  328. def get_table_columns(self, doctype):
  329. return [r[0] for r in self.sql("DESC `tab%s`" % doctype)]
  330. def close(self):
  331. if self._conn:
  332. self._cursor.close()
  333. self._conn.close()
  334. self._cursor = None
  335. self._conn = None