Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 
 

171 righe
5.2 KiB

  1. import frappe
  2. from frappe import _
  3. from frappe.utils import cint, flt
  4. from frappe.database.schema import DBTable, get_definition
  5. from frappe.database.sequence import create_sequence
  6. from frappe.model import log_types
  7. class PostgresTable(DBTable):
  8. def create(self):
  9. varchar_len = frappe.db.VARCHAR_LEN
  10. name_column = f"name varchar({varchar_len}) primary key"
  11. additional_definitions = ""
  12. # columns
  13. column_defs = self.get_column_definitions()
  14. if column_defs:
  15. additional_definitions += ",\n".join(column_defs)
  16. # child table columns
  17. if self.meta.get("istable") or 0:
  18. if column_defs:
  19. additional_definitions += ",\n"
  20. additional_definitions += ",\n".join(
  21. (
  22. f"parent varchar({varchar_len})",
  23. f"parentfield varchar({varchar_len})",
  24. f"parenttype varchar({varchar_len})",
  25. )
  26. )
  27. # creating sequence(s)
  28. if (not self.meta.issingle and self.meta.autoname == "autoincrement")\
  29. or self.doctype in log_types:
  30. # The sequence cache is per connection.
  31. # Since we're opening and closing connections for every transaction this results in skipping the cache
  32. # to the next non-cached value hence not using cache in postgres.
  33. # ref: https://stackoverflow.com/questions/21356375/postgres-9-0-4-sequence-skipping-numbers
  34. create_sequence(self.doctype, check_not_exists=True)
  35. name_column = "name bigint primary key"
  36. # TODO: set docstatus length
  37. # create table
  38. frappe.db.sql(f"""create table `{self.table_name}` (
  39. {name_column},
  40. creation timestamp(6),
  41. modified timestamp(6),
  42. modified_by varchar({varchar_len}),
  43. owner varchar({varchar_len}),
  44. docstatus smallint not null default '0',
  45. idx bigint not null default '0',
  46. {additional_definitions}
  47. )"""
  48. )
  49. self.create_indexes()
  50. frappe.db.commit()
  51. def create_indexes(self):
  52. create_index_query = ""
  53. for key, col in self.columns.items():
  54. if (col.set_index
  55. and col.fieldtype in frappe.db.type_map
  56. and frappe.db.type_map.get(col.fieldtype)[0]
  57. not in ('text', 'longtext')):
  58. create_index_query += 'CREATE INDEX IF NOT EXISTS "{index_name}" ON `{table_name}`(`{field}`);'.format(
  59. index_name=col.fieldname,
  60. table_name=self.table_name,
  61. field=col.fieldname
  62. )
  63. if create_index_query:
  64. # nosemgrep
  65. frappe.db.sql(create_index_query)
  66. def alter(self):
  67. for col in self.columns.values():
  68. col.build_for_alter_table(self.current_columns.get(col.fieldname.lower()))
  69. query = []
  70. for col in self.add_column:
  71. query.append("ADD COLUMN `{}` {}".format(col.fieldname, col.get_definition()))
  72. for col in self.change_type:
  73. using_clause = ""
  74. if col.fieldtype in ("Datetime"):
  75. # The USING option of SET DATA TYPE can actually specify any expression
  76. # involving the old values of the row
  77. # read more https://www.postgresql.org/docs/9.1/sql-altertable.html
  78. using_clause = "USING {}::timestamp without time zone".format(col.fieldname)
  79. elif col.fieldtype in ("Check"):
  80. using_clause = "USING {}::smallint".format(col.fieldname)
  81. query.append("ALTER COLUMN `{0}` TYPE {1} {2}".format(
  82. col.fieldname,
  83. get_definition(col.fieldtype, precision=col.precision, length=col.length),
  84. using_clause
  85. ))
  86. for col in self.set_default:
  87. if col.fieldname=="name":
  88. continue
  89. if col.fieldtype in ("Check", "Int"):
  90. col_default = cint(col.default)
  91. elif col.fieldtype in ("Currency", "Float", "Percent"):
  92. col_default = flt(col.default)
  93. elif not col.default:
  94. col_default = "NULL"
  95. else:
  96. col_default = "{}".format(frappe.db.escape(col.default))
  97. query.append("ALTER COLUMN `{}` SET DEFAULT {}".format(col.fieldname, col_default))
  98. create_contraint_query = ""
  99. for col in self.add_index:
  100. # if index key not exists
  101. create_contraint_query += 'CREATE INDEX IF NOT EXISTS "{index_name}" ON `{table_name}`(`{field}`);'.format(
  102. index_name=col.fieldname,
  103. table_name=self.table_name,
  104. field=col.fieldname)
  105. for col in self.add_unique:
  106. # if index key not exists
  107. create_contraint_query += 'CREATE UNIQUE INDEX IF NOT EXISTS "unique_{index_name}" ON `{table_name}`(`{field}`);'.format(
  108. index_name=col.fieldname,
  109. table_name=self.table_name,
  110. field=col.fieldname
  111. )
  112. drop_contraint_query = ""
  113. for col in self.drop_index:
  114. # primary key
  115. if col.fieldname != 'name':
  116. # if index key exists
  117. drop_contraint_query += 'DROP INDEX IF EXISTS "{}" ;'.format(col.fieldname)
  118. for col in self.drop_unique:
  119. # primary key
  120. if col.fieldname != 'name':
  121. # if index key exists
  122. drop_contraint_query += 'DROP INDEX IF EXISTS "unique_{}" ;'.format(col.fieldname)
  123. try:
  124. if query:
  125. final_alter_query = "ALTER TABLE `{}` {}".format(self.table_name, ", ".join(query))
  126. # nosemgrep
  127. frappe.db.sql(final_alter_query)
  128. if create_contraint_query:
  129. # nosemgrep
  130. frappe.db.sql(create_contraint_query)
  131. if drop_contraint_query:
  132. # nosemgrep
  133. frappe.db.sql(drop_contraint_query)
  134. except Exception as e:
  135. # sanitize
  136. if frappe.db.is_duplicate_fieldname(e):
  137. frappe.throw(str(e))
  138. elif frappe.db.is_duplicate_entry(e):
  139. fieldname = str(e).split("'")[-2]
  140. frappe.throw(
  141. _("{0} field cannot be set as unique in {1}, as there are non-unique existing values")
  142. .format(fieldname, self.table_name)
  143. )
  144. else:
  145. raise e