Replace a for append loop with list extend. Create the list with values instead of creating an empty list and extending it with another list.
169 lines
4.9 KiB
Python
169 lines
4.9 KiB
Python
import frappe
|
|
from frappe import _
|
|
from frappe.database.schema import DBTable, get_definition
|
|
from frappe.model import log_types
|
|
from frappe.utils import cint, flt
|
|
|
|
|
|
class PostgresTable(DBTable):
|
|
def create(self):
|
|
varchar_len = frappe.db.VARCHAR_LEN
|
|
name_column = f"name varchar({varchar_len}) primary key"
|
|
|
|
additional_definitions = ""
|
|
# columns
|
|
column_defs = self.get_column_definitions()
|
|
if column_defs:
|
|
additional_definitions += ",\n".join(column_defs)
|
|
|
|
# child table columns
|
|
if self.meta.get("istable") or 0:
|
|
if column_defs:
|
|
additional_definitions += ",\n"
|
|
|
|
additional_definitions += ",\n".join(
|
|
(
|
|
f"parent varchar({varchar_len})",
|
|
f"parentfield varchar({varchar_len})",
|
|
f"parenttype varchar({varchar_len})",
|
|
)
|
|
)
|
|
|
|
# creating sequence(s)
|
|
if (
|
|
not self.meta.issingle and self.meta.autoname == "autoincrement"
|
|
) or self.doctype in log_types:
|
|
|
|
frappe.db.create_sequence(self.doctype, check_not_exists=True, cache=frappe.db.SEQUENCE_CACHE)
|
|
name_column = "name bigint primary key"
|
|
|
|
# TODO: set docstatus length
|
|
# create table
|
|
frappe.db.sql(
|
|
f"""create table `{self.table_name}` (
|
|
{name_column},
|
|
creation timestamp(6),
|
|
modified timestamp(6),
|
|
modified_by varchar({varchar_len}),
|
|
owner varchar({varchar_len}),
|
|
docstatus smallint not null default '0',
|
|
idx bigint not null default '0',
|
|
{additional_definitions}
|
|
)"""
|
|
)
|
|
|
|
self.create_indexes()
|
|
frappe.db.commit()
|
|
|
|
def create_indexes(self):
|
|
create_index_query = ""
|
|
for key, col in self.columns.items():
|
|
if (
|
|
col.set_index
|
|
and col.fieldtype in frappe.db.type_map
|
|
and frappe.db.type_map.get(col.fieldtype)[0] not in ("text", "longtext")
|
|
):
|
|
create_index_query += (
|
|
'CREATE INDEX IF NOT EXISTS "{index_name}" ON `{table_name}`(`{field}`);'.format(
|
|
index_name=col.fieldname, table_name=self.table_name, field=col.fieldname
|
|
)
|
|
)
|
|
if create_index_query:
|
|
# nosemgrep
|
|
frappe.db.sql(create_index_query)
|
|
|
|
def alter(self):
|
|
for col in self.columns.values():
|
|
col.build_for_alter_table(self.current_columns.get(col.fieldname.lower()))
|
|
|
|
query = [f"ADD COLUMN `{col.fieldname}` {col.get_definition()}" for col in self.add_column]
|
|
|
|
for col in self.change_type:
|
|
using_clause = ""
|
|
if col.fieldtype in ("Datetime"):
|
|
# The USING option of SET DATA TYPE can actually specify any expression
|
|
# involving the old values of the row
|
|
# read more https://www.postgresql.org/docs/9.1/sql-altertable.html
|
|
using_clause = f"USING {col.fieldname}::timestamp without time zone"
|
|
elif col.fieldtype in ("Check"):
|
|
using_clause = f"USING {col.fieldname}::smallint"
|
|
|
|
query.append(
|
|
"ALTER COLUMN `{}` TYPE {} {}".format(
|
|
col.fieldname,
|
|
get_definition(col.fieldtype, precision=col.precision, length=col.length),
|
|
using_clause,
|
|
)
|
|
)
|
|
|
|
for col in self.set_default:
|
|
if col.fieldname == "name":
|
|
continue
|
|
|
|
if col.fieldtype in ("Check", "Int"):
|
|
col_default = cint(col.default)
|
|
|
|
elif col.fieldtype in ("Currency", "Float", "Percent"):
|
|
col_default = flt(col.default)
|
|
|
|
elif not col.default:
|
|
col_default = "NULL"
|
|
|
|
else:
|
|
col_default = f"{frappe.db.escape(col.default)}"
|
|
|
|
query.append(f"ALTER COLUMN `{col.fieldname}` SET DEFAULT {col_default}")
|
|
|
|
create_contraint_query = ""
|
|
for col in self.add_index:
|
|
# if index key not exists
|
|
create_contraint_query += (
|
|
'CREATE INDEX IF NOT EXISTS "{index_name}" ON `{table_name}`(`{field}`);'.format(
|
|
index_name=col.fieldname, table_name=self.table_name, field=col.fieldname
|
|
)
|
|
)
|
|
|
|
for col in self.add_unique:
|
|
# if index key not exists
|
|
create_contraint_query += (
|
|
'CREATE UNIQUE INDEX IF NOT EXISTS "unique_{index_name}" ON `{table_name}`(`{field}`);'.format(
|
|
index_name=col.fieldname, table_name=self.table_name, field=col.fieldname
|
|
)
|
|
)
|
|
|
|
drop_contraint_query = ""
|
|
for col in self.drop_index:
|
|
# primary key
|
|
if col.fieldname != "name":
|
|
# if index key exists
|
|
drop_contraint_query += f'DROP INDEX IF EXISTS "{col.fieldname}" ;'
|
|
|
|
for col in self.drop_unique:
|
|
# primary key
|
|
if col.fieldname != "name":
|
|
# if index key exists
|
|
drop_contraint_query += f'DROP INDEX IF EXISTS "unique_{col.fieldname}" ;'
|
|
try:
|
|
if query:
|
|
final_alter_query = "ALTER TABLE `{}` {}".format(self.table_name, ", ".join(query))
|
|
# nosemgrep
|
|
frappe.db.sql(final_alter_query)
|
|
if create_contraint_query:
|
|
# nosemgrep
|
|
frappe.db.sql(create_contraint_query)
|
|
if drop_contraint_query:
|
|
# nosemgrep
|
|
frappe.db.sql(drop_contraint_query)
|
|
except Exception as e:
|
|
# sanitize
|
|
if frappe.db.is_duplicate_fieldname(e):
|
|
frappe.throw(str(e))
|
|
elif frappe.db.is_duplicate_entry(e):
|
|
fieldname = str(e).split("'")[-2]
|
|
frappe.throw(
|
|
_("{0} field cannot be set as unique in {1}, as there are non-unique existing values").format(
|
|
fieldname, self.table_name
|
|
)
|
|
)
|
|
else:
|
|
raise e
|