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.

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