Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

11 лет назад
13 лет назад
11 лет назад
11 лет назад
11 лет назад
10 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
10 лет назад
10 лет назад
10 лет назад
Except and raise statement python 3 compatible style (#3216) * changes exception and raise statements to python 3 style * changes except statement to python 3 style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * adds six.reraise to fix python 3 style raise statements with traceback * fixes indentation
8 лет назад
10 лет назад
10 лет назад
11 лет назад
10 лет назад
11 лет назад
8 лет назад
Except and raise statement python 3 compatible style (#3216) * changes exception and raise statements to python 3 style * changes except statement to python 3 style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * adds six.reraise to fix python 3 style raise statements with traceback * fixes indentation
8 лет назад
10 лет назад
10 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
10 лет назад
10 лет назад
13 лет назад
11 лет назад
11 лет назад
11 лет назад
13 лет назад
11 лет назад
10 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
10 лет назад
10 лет назад
11 лет назад
11 лет назад
11 лет назад
Except and raise statement python 3 compatible style (#3216) * changes exception and raise statements to python 3 style * changes except statement to python 3 style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * adds six.reraise to fix python 3 style raise statements with traceback * fixes indentation
8 лет назад
10 лет назад
10 лет назад
10 лет назад
11 лет назад
10 лет назад
10 лет назад
11 лет назад
10 лет назад
11 лет назад
10 лет назад
11 лет назад
11 лет назад
10 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
13 лет назад
11 лет назад
Except and raise statement python 3 compatible style (#3216) * changes exception and raise statements to python 3 style * changes except statement to python 3 style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * adds six.reraise to fix python 3 style raise statements with traceback * fixes indentation
8 лет назад
13 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
13 лет назад
11 лет назад
13 лет назад
11 лет назад
Except and raise statement python 3 compatible style (#3216) * changes exception and raise statements to python 3 style * changes except statement to python 3 style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * changes except and raise statement to python 3 compatible style * adds six.reraise to fix python 3 style raise statements with traceback * fixes indentation
8 лет назад
11 лет назад
11 лет назад
11 лет назад
11 лет назад
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. """
  5. Syncs a database table to the `DocType` (metadata)
  6. .. note:: This module is only used internally
  7. """
  8. import re
  9. import os
  10. import frappe
  11. from frappe import _
  12. from frappe.utils import cstr, cint, flt
  13. import MySQLdb
  14. class InvalidColumnName(frappe.ValidationError): pass
  15. varchar_len = '140'
  16. standard_varchar_columns = ('name', 'owner', 'modified_by', 'parent', 'parentfield', 'parenttype')
  17. type_map = {
  18. 'Currency': ('decimal', '18,6')
  19. ,'Int': ('int', '11')
  20. ,'Float': ('decimal', '18,6')
  21. ,'Percent': ('decimal', '18,6')
  22. ,'Check': ('int', '1')
  23. ,'Small Text': ('text', '')
  24. ,'Long Text': ('longtext', '')
  25. ,'Code': ('longtext', '')
  26. ,'Text Editor': ('longtext', '')
  27. ,'Date': ('date', '')
  28. ,'Datetime': ('datetime', '6')
  29. ,'Time': ('time', '6')
  30. ,'Text': ('text', '')
  31. ,'Data': ('varchar', varchar_len)
  32. ,'Link': ('varchar', varchar_len)
  33. ,'Dynamic Link':('varchar', varchar_len)
  34. ,'Password': ('varchar', varchar_len)
  35. ,'Select': ('varchar', varchar_len)
  36. ,'Read Only': ('varchar', varchar_len)
  37. ,'Attach': ('text', '')
  38. ,'Attach Image':('text', '')
  39. ,'Signature': ('longtext', '')
  40. ,'Color': ('varchar', varchar_len)
  41. }
  42. default_columns = ['name', 'creation', 'modified', 'modified_by', 'owner',
  43. 'docstatus', 'parent', 'parentfield', 'parenttype', 'idx']
  44. optional_columns = ["_user_tags", "_comments", "_assign", "_liked_by"]
  45. default_shortcuts = ['_Login', '__user', '_Full Name', 'Today', '__today', "now", "Now"]
  46. def updatedb(dt, meta=None):
  47. """
  48. Syncs a `DocType` to the table
  49. * creates if required
  50. * updates columns
  51. * updates indices
  52. """
  53. res = frappe.db.sql("select issingle from tabDocType where name=%s", (dt,))
  54. if not res:
  55. raise Exception('Wrong doctype "%s" in updatedb' % dt)
  56. if not res[0][0]:
  57. tab = DbTable(dt, 'tab', meta)
  58. tab.validate()
  59. frappe.db.commit()
  60. tab.sync()
  61. frappe.db.begin()
  62. class DbTable:
  63. def __init__(self, doctype, prefix = 'tab', meta = None):
  64. self.doctype = doctype
  65. self.name = prefix + doctype
  66. self.columns = {}
  67. self.current_columns = {}
  68. self.meta = meta
  69. if not self.meta:
  70. self.meta = frappe.get_meta(self.doctype)
  71. # lists for change
  72. self.add_column = []
  73. self.change_type = []
  74. self.add_index = []
  75. self.drop_index = []
  76. self.set_default = []
  77. # load
  78. self.get_columns_from_docfields()
  79. def validate(self):
  80. """Check if change in varchar length isn't truncating the columns"""
  81. if self.is_new():
  82. return
  83. self.get_columns_from_db()
  84. columns = [frappe._dict({"fieldname": f, "fieldtype": "Data"}) for f in standard_varchar_columns]
  85. columns += self.columns.values()
  86. for col in columns:
  87. if len(col.fieldname) >= 64:
  88. frappe.throw(_("Fieldname is limited to 64 characters ({0})").format(frappe.bold(col.fieldname)))
  89. if col.fieldtype in type_map and type_map[col.fieldtype][0]=="varchar":
  90. # validate length range
  91. new_length = cint(col.length) or cint(varchar_len)
  92. if not (1 <= new_length <= 1000):
  93. frappe.throw(_("Length of {0} should be between 1 and 1000").format(col.fieldname))
  94. try:
  95. # check for truncation
  96. max_length = frappe.db.sql("""select max(char_length(`{fieldname}`)) from `tab{doctype}`"""\
  97. .format(fieldname=col.fieldname, doctype=self.doctype))
  98. except MySQLdb.OperationalError as e:
  99. if e.args[0]==1054:
  100. # Unknown column 'column_name' in 'field list'
  101. continue
  102. else:
  103. raise
  104. if max_length and max_length[0][0] > new_length:
  105. current_type = self.current_columns[col.fieldname]["type"]
  106. current_length = re.findall('varchar\(([\d]+)\)', current_type)
  107. if not current_length:
  108. # case when the field is no longer a varchar
  109. continue
  110. current_length = current_length[0]
  111. if col.fieldname in self.columns:
  112. self.columns[col.fieldname].length = current_length
  113. frappe.msgprint(_("Reverting length to {0} for '{1}' in '{2}'; Setting the length as {3} will cause truncation of data.")\
  114. .format(current_length, col.fieldname, self.doctype, new_length))
  115. def sync(self):
  116. if self.is_new():
  117. self.create()
  118. else:
  119. self.alter()
  120. def is_new(self):
  121. return self.name not in DbManager(frappe.db).get_tables_list(frappe.db.cur_db_name)
  122. def create(self):
  123. add_text = ''
  124. # columns
  125. column_defs = self.get_column_definitions()
  126. if column_defs: add_text += ',\n'.join(column_defs) + ',\n'
  127. # index
  128. index_defs = self.get_index_definitions()
  129. if index_defs: add_text += ',\n'.join(index_defs) + ',\n'
  130. # create table
  131. frappe.db.sql("""create table `%s` (
  132. name varchar({varchar_len}) not null primary key,
  133. creation datetime(6),
  134. modified datetime(6),
  135. modified_by varchar({varchar_len}),
  136. owner varchar({varchar_len}),
  137. docstatus int(1) not null default '0',
  138. parent varchar({varchar_len}),
  139. parentfield varchar({varchar_len}),
  140. parenttype varchar({varchar_len}),
  141. idx int(8) not null default '0',
  142. %sindex parent(parent))
  143. ENGINE={engine}
  144. ROW_FORMAT=COMPRESSED
  145. CHARACTER SET=utf8mb4
  146. COLLATE=utf8mb4_unicode_ci""".format(varchar_len=varchar_len,
  147. engine=self.meta.get("engine") or 'InnoDB') % (self.name, add_text))
  148. def get_column_definitions(self):
  149. column_list = [] + default_columns
  150. ret = []
  151. for k in self.columns.keys():
  152. if k not in column_list:
  153. d = self.columns[k].get_definition()
  154. if d:
  155. ret.append('`'+ k+ '` ' + d)
  156. column_list.append(k)
  157. return ret
  158. def get_index_definitions(self):
  159. ret = []
  160. for key, col in self.columns.items():
  161. if col.set_index and not col.unique and col.fieldtype in type_map and \
  162. type_map.get(col.fieldtype)[0] not in ('text', 'longtext'):
  163. ret.append('index `' + key + '`(`' + key + '`)')
  164. return ret
  165. def get_columns_from_docfields(self):
  166. """
  167. get columns from docfields and custom fields
  168. """
  169. fl = frappe.db.sql("SELECT * FROM tabDocField WHERE parent = %s", self.doctype, as_dict = 1)
  170. lengths = {}
  171. precisions = {}
  172. uniques = {}
  173. # optional fields like _comments
  174. if not self.meta.istable:
  175. for fieldname in optional_columns:
  176. fl.append({
  177. "fieldname": fieldname,
  178. "fieldtype": "Text"
  179. })
  180. # add _seen column if track_seen
  181. if getattr(self.meta, 'track_seen', False):
  182. fl.append({
  183. 'fieldname': '_seen',
  184. 'fieldtype': 'Text'
  185. })
  186. if not frappe.flags.in_install_db and frappe.flags.in_install != "frappe":
  187. custom_fl = frappe.db.sql("""\
  188. SELECT * FROM `tabCustom Field`
  189. WHERE dt = %s AND docstatus < 2""", (self.doctype,), as_dict=1)
  190. if custom_fl: fl += custom_fl
  191. # apply length, precision and unique from property setters
  192. for ps in frappe.get_all("Property Setter", fields=["field_name", "property", "value"],
  193. filters={
  194. "doc_type": self.doctype,
  195. "doctype_or_field": "DocField",
  196. "property": ["in", ["precision", "length", "unique"]]
  197. }):
  198. if ps.property=="length":
  199. lengths[ps.field_name] = cint(ps.value)
  200. elif ps.property=="precision":
  201. precisions[ps.field_name] = cint(ps.value)
  202. elif ps.property=="unique":
  203. uniques[ps.field_name] = cint(ps.value)
  204. for f in fl:
  205. self.columns[f['fieldname']] = DbColumn(self, f['fieldname'],
  206. f['fieldtype'], lengths.get(f["fieldname"]) or f.get('length'), f.get('default'), f.get('search_index'),
  207. f.get('options'), uniques.get(f["fieldname"], f.get('unique')), precisions.get(f['fieldname']) or f.get('precision'))
  208. def get_columns_from_db(self):
  209. self.show_columns = frappe.db.sql("desc `%s`" % self.name)
  210. for c in self.show_columns:
  211. self.current_columns[c[0].lower()] = {'name': c[0],
  212. 'type':c[1], 'index':c[3]=="MUL", 'default':c[4], "unique":c[3]=="UNI"}
  213. # GET foreign keys
  214. def get_foreign_keys(self):
  215. fk_list = []
  216. txt = frappe.db.sql("show create table `%s`" % self.name)[0][1]
  217. for line in txt.split('\n'):
  218. if line.strip().startswith('CONSTRAINT') and line.find('FOREIGN')!=-1:
  219. try:
  220. fk_list.append((line.split('`')[3], line.split('`')[1]))
  221. except IndexError:
  222. pass
  223. return fk_list
  224. # Drop foreign keys
  225. def drop_foreign_keys(self):
  226. if not self.drop_foreign_key:
  227. return
  228. fk_list = self.get_foreign_keys()
  229. # make dictionary of constraint names
  230. fk_dict = {}
  231. for f in fk_list:
  232. fk_dict[f[0]] = f[1]
  233. # drop
  234. for col in self.drop_foreign_key:
  235. frappe.db.sql("set foreign_key_checks=0")
  236. frappe.db.sql("alter table `%s` drop foreign key `%s`" % (self.name, fk_dict[col.fieldname]))
  237. frappe.db.sql("set foreign_key_checks=1")
  238. def alter(self):
  239. for col in self.columns.values():
  240. col.build_for_alter_table(self.current_columns.get(col.fieldname.lower(), None))
  241. query = []
  242. for col in self.add_column:
  243. query.append("add column `{}` {}".format(col.fieldname, col.get_definition()))
  244. for col in self.change_type:
  245. current_def = self.current_columns.get(col.fieldname.lower(), None)
  246. query.append("change `{}` `{}` {}".format(current_def["name"], col.fieldname, col.get_definition()))
  247. for col in self.add_index:
  248. # if index key not exists
  249. if not frappe.db.sql("show index from `%s` where key_name = %s" %
  250. (self.name, '%s'), col.fieldname):
  251. query.append("add index `{}`(`{}`)".format(col.fieldname, col.fieldname))
  252. for col in self.drop_index:
  253. if col.fieldname != 'name': # primary key
  254. # if index key exists
  255. if frappe.db.sql("""show index from `{0}`
  256. where key_name=%s
  257. and Non_unique=%s""".format(self.name), (col.fieldname, col.unique)):
  258. query.append("drop index `{}`".format(col.fieldname))
  259. for col in self.set_default:
  260. if col.fieldname=="name":
  261. continue
  262. if col.fieldtype in ("Check", "Int"):
  263. col_default = cint(col.default)
  264. elif col.fieldtype in ("Currency", "Float", "Percent"):
  265. col_default = flt(col.default)
  266. elif not col.default:
  267. col_default = "null"
  268. else:
  269. col_default = '"{}"'.format(col.default.replace('"', '\\"'))
  270. query.append('alter column `{}` set default {}'.format(col.fieldname, col_default))
  271. if query:
  272. try:
  273. frappe.db.sql("alter table `{}` {}".format(self.name, ", ".join(query)))
  274. except Exception as e:
  275. # sanitize
  276. if e.args[0]==1060:
  277. frappe.throw(str(e))
  278. elif e.args[0]==1062:
  279. fieldname = str(e).split("'")[-2]
  280. frappe.throw(_("{0} field cannot be set as unique in {1}, as there are non-unique existing values".format(fieldname, self.name)))
  281. else:
  282. raise e
  283. class DbColumn:
  284. def __init__(self, table, fieldname, fieldtype, length, default,
  285. set_index, options, unique, precision):
  286. self.table = table
  287. self.fieldname = fieldname
  288. self.fieldtype = fieldtype
  289. self.length = length
  290. self.set_index = set_index
  291. self.default = default
  292. self.options = options
  293. self.unique = unique
  294. self.precision = precision
  295. def get_definition(self, with_default=1):
  296. column_def = get_definition(self.fieldtype, precision=self.precision, length=self.length)
  297. if not column_def:
  298. return column_def
  299. if self.fieldtype in ("Check", "Int"):
  300. default_value = cint(self.default) or 0
  301. column_def += ' not null default {0}'.format(default_value)
  302. elif self.fieldtype in ("Currency", "Float", "Percent"):
  303. default_value = flt(self.default) or 0
  304. column_def += ' not null default {0}'.format(default_value)
  305. elif self.default and (self.default not in default_shortcuts) \
  306. and not self.default.startswith(":") and column_def not in ('text', 'longtext'):
  307. column_def += ' default "' + self.default.replace('"', '\"') + '"'
  308. if self.unique and (column_def not in ('text', 'longtext')):
  309. column_def += ' unique'
  310. return column_def
  311. def build_for_alter_table(self, current_def):
  312. column_def = get_definition(self.fieldtype, self.precision, self.length)
  313. # no columns
  314. if not column_def:
  315. return
  316. # to add?
  317. if not current_def:
  318. self.fieldname = validate_column_name(self.fieldname)
  319. self.table.add_column.append(self)
  320. return
  321. # type
  322. if (current_def['type'] != column_def) or\
  323. self.fieldname != current_def['name'] or\
  324. ((self.unique and not current_def['unique']) and column_def not in ('text', 'longtext')):
  325. self.table.change_type.append(self)
  326. else:
  327. # default
  328. if (self.default_changed(current_def) \
  329. and (self.default not in default_shortcuts) \
  330. and not cstr(self.default).startswith(":") \
  331. and not (column_def in ['text','longtext'])):
  332. self.table.set_default.append(self)
  333. # index should be applied or dropped irrespective of type change
  334. if ( (current_def['index'] and not self.set_index and not self.unique)
  335. or (current_def['unique'] and not self.unique) ):
  336. # to drop unique you have to drop index
  337. self.table.drop_index.append(self)
  338. elif (not current_def['index'] and self.set_index) and not (column_def in ('text', 'longtext')):
  339. self.table.add_index.append(self)
  340. def default_changed(self, current_def):
  341. if "decimal" in current_def['type']:
  342. return self.default_changed_for_decimal(current_def)
  343. else:
  344. return current_def['default'] != self.default
  345. def default_changed_for_decimal(self, current_def):
  346. try:
  347. if current_def['default'] in ("", None) and self.default in ("", None):
  348. # both none, empty
  349. return False
  350. elif current_def['default'] in ("", None):
  351. try:
  352. # check if new default value is valid
  353. float(self.default)
  354. return True
  355. except ValueError:
  356. return False
  357. elif self.default in ("", None):
  358. # new default value is empty
  359. return True
  360. else:
  361. # NOTE float() raise ValueError when "" or None is passed
  362. return float(current_def['default'])!=float(self.default)
  363. except TypeError:
  364. return True
  365. class DbManager:
  366. """
  367. Basically, a wrapper for oft-used mysql commands. like show tables,databases, variables etc...
  368. #TODO:
  369. 0. Simplify / create settings for the restore database source folder
  370. 0a. Merge restore database and extract_sql(from frappe_server_tools).
  371. 1. Setter and getter for different mysql variables.
  372. 2. Setter and getter for mysql variables at global level??
  373. """
  374. def __init__(self,db):
  375. """
  376. Pass root_conn here for access to all databases.
  377. """
  378. if db:
  379. self.db = db
  380. def get_current_host(self):
  381. return self.db.sql("select user()")[0][0].split('@')[1]
  382. def get_variables(self,regex):
  383. """
  384. Get variables that match the passed pattern regex
  385. """
  386. return list(self.db.sql("SHOW VARIABLES LIKE '%s'"%regex))
  387. def get_table_schema(self,table):
  388. """
  389. Just returns the output of Desc tables.
  390. """
  391. return list(self.db.sql("DESC `%s`"%table))
  392. def get_tables_list(self,target=None):
  393. """get list of tables"""
  394. if target:
  395. self.db.use(target)
  396. return [t[0] for t in self.db.sql("SHOW TABLES")]
  397. def create_user(self, user, password, host=None):
  398. #Create user if it doesn't exist.
  399. if not host:
  400. host = self.get_current_host()
  401. if password:
  402. self.db.sql("CREATE USER '%s'@'%s' IDENTIFIED BY '%s';" % (user[:16], host, password))
  403. else:
  404. self.db.sql("CREATE USER '%s'@'%s';" % (user[:16], host))
  405. def delete_user(self, target, host=None):
  406. if not host:
  407. host = self.get_current_host()
  408. try:
  409. self.db.sql("DROP USER '%s'@'%s';" % (target, host))
  410. except Exception as e:
  411. if e.args[0]==1396:
  412. pass
  413. else:
  414. raise
  415. def create_database(self,target):
  416. if target in self.get_database_list():
  417. self.drop_database(target)
  418. self.db.sql("CREATE DATABASE `%s` ;" % target)
  419. def drop_database(self,target):
  420. self.db.sql("DROP DATABASE IF EXISTS `%s`;"%target)
  421. def grant_all_privileges(self, target, user, host=None):
  422. if not host:
  423. host = self.get_current_host()
  424. self.db.sql("GRANT ALL PRIVILEGES ON `%s`.* TO '%s'@'%s';" % (target,
  425. user, host))
  426. def grant_select_privilges(self, db, table, user, host=None):
  427. if not host:
  428. host = self.get_current_host()
  429. if table:
  430. self.db.sql("GRANT SELECT ON %s.%s to '%s'@'%s';" % (db, table, user, host))
  431. else:
  432. self.db.sql("GRANT SELECT ON %s.* to '%s'@'%s';" % (db, user, host))
  433. def flush_privileges(self):
  434. self.db.sql("FLUSH PRIVILEGES")
  435. def get_database_list(self):
  436. """get list of databases"""
  437. return [d[0] for d in self.db.sql("SHOW DATABASES")]
  438. def restore_database(self,target,source,user,password):
  439. from frappe.utils import make_esc
  440. esc = make_esc('$ ')
  441. os.system("mysql -u %s -p%s -h%s %s < %s" % \
  442. (esc(user), esc(password), esc(frappe.db.host), esc(target), source))
  443. def drop_table(self,table_name):
  444. """drop table if exists"""
  445. if not table_name in self.get_tables_list():
  446. return
  447. self.db.sql("DROP TABLE IF EXISTS %s "%(table_name))
  448. def validate_column_name(n):
  449. n = n.replace(' ','_').strip().lower()
  450. special_characters = re.findall("[\W]", n, re.UNICODE)
  451. if special_characters:
  452. special_characters = ", ".join('"{0}"'.format(c) for c in special_characters)
  453. frappe.throw(_("Fieldname {0} cannot have special characters like {1}").format(cstr(n), special_characters), InvalidColumnName)
  454. return n
  455. def validate_column_length(fieldname):
  456. """ In MySQL maximum column length is 64 characters,
  457. ref: https://dev.mysql.com/doc/refman/5.5/en/identifiers.html"""
  458. if len(fieldname) > 64:
  459. frappe.throw(_("Fieldname is limited to 64 characters ({0})").format(fieldname))
  460. def remove_all_foreign_keys():
  461. frappe.db.sql("set foreign_key_checks = 0")
  462. frappe.db.commit()
  463. for t in frappe.db.sql("select name from tabDocType where issingle=0"):
  464. dbtab = DbTable(t[0])
  465. try:
  466. fklist = dbtab.get_foreign_keys()
  467. except Exception as e:
  468. if e.args[0]==1146:
  469. fklist = []
  470. else:
  471. raise
  472. for f in fklist:
  473. frappe.db.sql("alter table `tab%s` drop foreign key `%s`" % (t[0], f[1]))
  474. def get_definition(fieldtype, precision=None, length=None):
  475. d = type_map.get(fieldtype)
  476. if not d:
  477. return
  478. coltype = d[0]
  479. size = None
  480. if d[1]:
  481. size = d[1]
  482. if size:
  483. if fieldtype in ["Float", "Currency", "Percent"] and cint(precision) > 6:
  484. size = '21,9'
  485. if coltype == "varchar" and length:
  486. size = length
  487. if size is not None:
  488. coltype = "{coltype}({size})".format(coltype=coltype, size=size)
  489. return coltype
  490. def add_column(doctype, column_name, fieldtype, precision=None):
  491. if column_name in frappe.db.get_table_columns(doctype):
  492. # already exists
  493. return
  494. frappe.db.commit()
  495. frappe.db.sql("alter table `tab%s` add column %s %s" % (doctype,
  496. column_name, get_definition(fieldtype, precision)))