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 години
преди 14 години
преди 14 години
преди 14 години
преди 13 години
преди 13 години
преди 13 години
преди 13 години
преди 14 години
преди 13 години
преди 13 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. # Database Module
  2. # --------------------
  3. import MySQLdb
  4. from webnotes import defs
  5. import webnotes
  6. class Database:
  7. """
  8. Open a database connection with the given parmeters, if use_default is True, use the
  9. login details from `defs.py`. This is called by the request handler and is accessible using
  10. the `conn` global variable. the `sql` method is also global to run queries
  11. """
  12. def __init__(self, host=None, user=None, password=None, ac_name=None, use_default = 0):
  13. self.host = host or 'localhost'
  14. self.user = user or getattr(defs, 'default_db_name', '')
  15. if ac_name:
  16. self.user = self.get_db_login(ac_name) or defs.default_db_name
  17. if use_default:
  18. self.user = defs.default_db_name
  19. self.is_testing = 0
  20. self.in_transaction = 0
  21. self.transaction_writes = 0
  22. self.testing_tables = []
  23. self.auto_commit_on_many_writes = 0
  24. self.password = self.get_db_password(self.user, password)
  25. self.connect()
  26. if self.user != 'root':
  27. self.use(self.user)
  28. def get_db_password(self, db_name, password):
  29. """
  30. Return db password. order of importance:
  31. 1. password
  32. 2. defs.get_db_password()
  33. 3. defs.db_password
  34. """
  35. # password can be empty string
  36. if password:
  37. return password
  38. if hasattr(defs, 'get_db_password'):
  39. return defs.get_db_password(db_name)
  40. if hasattr(defs, 'db_password'):
  41. return defs.db_password
  42. else:
  43. return ''
  44. def get_db_login(self, ac_name):
  45. if hasattr(defs, 'db_name_map'):
  46. return getattr(defs,'db_name_map').get(ac_name, getattr(defs,'default_db_name'))
  47. else:
  48. return ac_name
  49. def connect(self):
  50. """
  51. Connect to a database
  52. """
  53. self._conn = MySQLdb.connect(user=self.user, host=self.host, passwd=self.password, use_unicode=True)
  54. self._conn.converter[246]=float
  55. self._conn.set_character_set('utf8')
  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 check_transaction_status(self, query):
  64. """
  65. Update *in_transaction* and check if "START TRANSACTION" is not called twice
  66. """
  67. if self.in_transaction and query and query.strip().split()[0].lower() in ['start', 'alter', 'drop', 'create']:
  68. raise Exception, 'This statement can cause implicit commit'
  69. if query and query.strip().lower()=='start transaction':
  70. self.in_transaction = 1
  71. self.transaction_writes = 0
  72. if query and query.strip().split()[0].lower() in ['commit', 'rollback']:
  73. self.in_transaction = 0
  74. if self.in_transaction and query[:6].lower() in ['update', 'insert']:
  75. self.transaction_writes += 1
  76. if self.transaction_writes > 5000:
  77. if self.auto_commit_on_many_writes:
  78. webnotes.conn.commit()
  79. webnotes.conn.begin()
  80. else:
  81. webnotes.msgprint('A very long query was encountered. If you are trying to import data, please do so using smaller files')
  82. raise Exception, 'Bad Query!!! Too many writes'
  83. def fetch_as_dict(self, formatted=0):
  84. """
  85. Internal - get results as dictionary
  86. """
  87. result = self._cursor.fetchall()
  88. ret = []
  89. for r in result:
  90. dict = {}
  91. for i in range(len(r)):
  92. dict[self._cursor.description[i][0]] = self.convert_to_simple_type(r[i], formatted)
  93. ret.append(dict)
  94. return ret
  95. def validate_query(self, q):
  96. cmd = q.strip().lower().split()[0]
  97. if cmd in ['alter', 'drop', 'truncate'] and webnotes.user.name != 'Administrator':
  98. webnotes.msgprint('Not allowed to execute query')
  99. raise Execption
  100. # ======================================================================================
  101. def sql(self, query, values=(), as_dict = 0, as_list = 0, formatted = 0, ignore_no_table = 1, debug=0, ignore_ddl=0):
  102. """
  103. * Execute a `query`, with given `values`
  104. * returns as a dictionary if as_dict = 1
  105. * returns as a list of lists (with cleaned up dates) if as_list = 1
  106. """
  107. # in transaction validations
  108. self.check_transaction_status(query)
  109. # execute
  110. try:
  111. if values!=():
  112. if debug: webnotes.msgprint(query % values)
  113. self._cursor.execute(query, values)
  114. else:
  115. if debug: webnotes.msgprint(query)
  116. self._cursor.execute(query)
  117. except Exception, e:
  118. # ignore data definition errors
  119. if ignore_ddl and e.args[0] in (1146,1054,1091):
  120. pass
  121. else:
  122. raise e
  123. # scrub output if required
  124. if as_dict:
  125. return self.fetch_as_dict(formatted)
  126. elif as_list:
  127. return self.convert_to_lists(self._cursor.fetchall(), formatted)
  128. else:
  129. return self._cursor.fetchall()
  130. def get_description(self):
  131. """
  132. Get metadata of the last query
  133. """
  134. return self._cursor.description
  135. # ======================================================================================
  136. def convert_to_simple_type(self, v, formatted=0):
  137. import datetime
  138. from webnotes.utils import formatdate, fmt_money
  139. # date
  140. if type(v)==datetime.date:
  141. v = str(v)
  142. if formatted:
  143. v = formatdate(v)
  144. # time
  145. elif type(v)==datetime.timedelta:
  146. h = int(v.seconds/60/60)
  147. v = str(h) + ':' + str(v.seconds/60 - h*60)
  148. if v[1]==':':
  149. v='0'+v
  150. # datetime
  151. elif type(v)==datetime.datetime:
  152. v = str(v)
  153. # long
  154. elif type(v)==long:
  155. v=int(v)
  156. # convert to strings... (if formatted)
  157. if formatted:
  158. if type(v)==float:
  159. v=fmt_money(v)
  160. if type(v)==int:
  161. v=str(v)
  162. return v
  163. # ======================================================================================
  164. def convert_to_lists(self, res, formatted=0):
  165. """
  166. Convert the given result set to a list of lists (with cleaned up dates and decimals)
  167. """
  168. nres = []
  169. for r in res:
  170. nr = []
  171. for c in r:
  172. nr.append(self.convert_to_simple_type(c, formatted))
  173. nres.append(nr)
  174. return nres
  175. # ======================================================================================
  176. def replace_tab_by_test(self, query):
  177. """
  178. Relace all ``tab`` + doctype to ``test`` + doctype
  179. """
  180. if self.is_testing:
  181. tl = self.get_testing_tables()
  182. for t in tl:
  183. query = query.replace(t, 'test' + t[3:])
  184. return query
  185. def get_testing_tables(self):
  186. """
  187. Get list of all tables for which `tab` is to be replaced by `test` before a query is executed
  188. """
  189. if not self.testing_tables:
  190. 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)]
  191. testing_tables+=['tabSeries','tabSingles'] # tabSessions is not included here
  192. return self.testing_tables
  193. # ======================================================================================
  194. # get a single value from a record
  195. def get_value(self, doctype, docname, fieldname, ignore=None):
  196. """
  197. Get a single / multiple value from a record.
  198. For Single DocType, let docname be = None
  199. """
  200. fl = fieldname
  201. if docname and (docname!=doctype or docname=='DocType'):
  202. if type(fieldname) in (list, tuple):
  203. fl = '`, `'.join(fieldname)
  204. try:
  205. r = self.sql("select `%s` from `tab%s` where name='%s'" % (fl, doctype, docname))
  206. except Exception, e:
  207. if e.args[0]==1054 and ignore:
  208. return None
  209. else:
  210. raise e
  211. return r and (len(r[0]) > 1 and r[0] or r[0][0]) or None
  212. else:
  213. if type(fieldname) in (list, tuple):
  214. fl = "', '".join(fieldname)
  215. r = self.sql("select value from tabSingles where field in ('%s') and doctype='%s'" % (fieldname, doctype))
  216. return r and (len(r) > 1 and (i[0] for i in r) or r[0][0]) or None
  217. def set_value(self, dt, dn, field, val):
  218. from webnotes.utils import now
  219. if dn and dt!=dn:
  220. self.sql("update `tab"+dt+"` set `"+field+"`=%s, modified=%s where name=%s", (val, now(), dn))
  221. else:
  222. if self.sql("select value from tabSingles where field=%s and doctype=%s", (field, dt)):
  223. self.sql("update tabSingles set value=%s where field=%s and doctype=%s", (val, field, dt))
  224. else:
  225. self.sql("insert into tabSingles(doctype, field, value) values (%s, %s, %s)", (dt, field, val))
  226. def set(self, doc, field, val):
  227. self.set_value(doc.doctype, doc.name, field, val)
  228. doc.fields[field] = val
  229. # ======================================================================================
  230. def set_global(self, key, val, user='__global'):
  231. res = self.sql('select defkey from `tabDefaultValue` where defkey=%s and parent=%s', (key, user))
  232. if res:
  233. self.sql('update `tabDefaultValue` set defvalue=%s where parent=%s and defkey=%s', (str(val), user, key))
  234. else:
  235. self.sql('insert into `tabDefaultValue` (name, defkey, defvalue, parent) values (%s,%s,%s,%s)', (user+'_'+key, key, str(val), user))
  236. def get_global(self, key, user='__global'):
  237. g = self.sql("select defvalue from tabDefaultValue where defkey=%s and parent=%s", (key, user))
  238. return g and g[0][0] or None
  239. # ======================================================================================
  240. def set_default(self, key, val):
  241. """set control panel default (tabDefaultVal)"""
  242. if self.sql("""select defkey from `tabDefaultValue` where
  243. defkey=%s and parent = "Control Panel" """, key):
  244. # update
  245. self.sql("""update `tabDefaultValue` set defvalue=%s
  246. where parent = "Control Panel" and defkey=%s""", (val, key))
  247. else:
  248. from webnotes.model.doc import Document
  249. d = Document('DefaultValue')
  250. d.parent = 'Control Panel'
  251. d.parenttype = 'Control Panel'
  252. d.parentfield = 'system_defaults'
  253. d.defkey = key
  254. d.defvalue = val
  255. d.save(1)
  256. def get_default(self, key):
  257. """get default value"""
  258. ret = self.sql("""select defvalue from tabDefaultValue where defkey=%s""", key)
  259. return ret and ret[0][0] or None
  260. def get_defaults(self, key=None):
  261. """get all defaults"""
  262. if key:
  263. return self.get_default(key)
  264. else:
  265. res = self.sql("""select defkey, defvalue from `tabDefaultValue`
  266. where parent = "Control Panel" """)
  267. d = {}
  268. for rec in res:
  269. d[rec[0]] = rec[1] or ''
  270. return d
  271. def begin(self):
  272. if not self.in_transaction:
  273. self.sql("start transaction")
  274. def commit(self):
  275. self.sql("commit")
  276. def rollback(self):
  277. self.sql("ROLLBACK")
  278. # ======================================================================================
  279. def field_exists(self, dt, fn):
  280. """
  281. Returns True if `fn` exists in `DocType` `dt`
  282. """
  283. return self.sql("select name from tabDocField where fieldname=%s and parent=%s", (dt, fn))
  284. def exists(self, dt, dn):
  285. """
  286. Returns true if the record exists
  287. """
  288. try:
  289. return self.sql('select name from `tab%s` where name=%s' % (dt, '%s'), dn)
  290. except:
  291. return None
  292. # ======================================================================================
  293. def close(self):
  294. """
  295. Close my connection
  296. """
  297. if self._conn:
  298. self._cursor.close()
  299. self._conn.close()
  300. self._cursor = None
  301. self._conn = None