@@ -62,7 +62,7 @@ class MariaDBTable(DBTable): | |||||
if not frappe.db.has_index(self.table_name, col.fieldname + '_index'): | if not frappe.db.has_index(self.table_name, col.fieldname + '_index'): | ||||
add_index_query.append("ADD INDEX `{}_index`(`{}`)".format(col.fieldname, col.fieldname)) | add_index_query.append("ADD INDEX `{}_index`(`{}`)".format(col.fieldname, col.fieldname)) | ||||
for col in self.drop_index: | |||||
for col in self.drop_index + self.drop_unique: | |||||
if col.fieldname != 'name': # primary key | if col.fieldname != 'name': # primary key | ||||
current_column = self.current_columns.get(col.fieldname.lower()) | current_column = self.current_columns.get(col.fieldname.lower()) | ||||
unique_constraint_changed = current_column.unique != col.unique | unique_constraint_changed = current_column.unique != col.unique | ||||
@@ -77,11 +77,11 @@ class PostgresDatabase(Database): | |||||
"""Escape quotes and percent in given string.""" | """Escape quotes and percent in given string.""" | ||||
if isinstance(s, bytes): | if isinstance(s, bytes): | ||||
s = s.decode('utf-8') | s = s.decode('utf-8') | ||||
# MariaDB's driver treats None as an empty string | # MariaDB's driver treats None as an empty string | ||||
# So Postgres should do the same | # So Postgres should do the same | ||||
if s is None: | |||||
if s is None: | |||||
s = '' | s = '' | ||||
if percent: | if percent: | ||||
@@ -308,18 +308,20 @@ class PostgresDatabase(Database): | |||||
WHEN 'timestamp without time zone' THEN 'timestamp' | WHEN 'timestamp without time zone' THEN 'timestamp' | ||||
ELSE a.data_type | ELSE a.data_type | ||||
END AS type, | END AS type, | ||||
COUNT(b.indexdef) AS Index, | |||||
BOOL_OR(b.index) AS index, | |||||
SPLIT_PART(COALESCE(a.column_default, NULL), '::', 1) AS default, | SPLIT_PART(COALESCE(a.column_default, NULL), '::', 1) AS default, | ||||
BOOL_OR(b.unique) AS unique | BOOL_OR(b.unique) AS unique | ||||
FROM information_schema.columns a | FROM information_schema.columns a | ||||
LEFT JOIN | LEFT JOIN | ||||
(SELECT indexdef, tablename, indexdef LIKE '%UNIQUE INDEX%' AS unique | |||||
(SELECT indexdef, tablename, | |||||
indexdef LIKE '%UNIQUE INDEX%' AS unique, | |||||
indexdef NOT LIKE '%UNIQUE INDEX%' AS index | |||||
FROM pg_indexes | FROM pg_indexes | ||||
WHERE tablename='{table_name}') b | WHERE tablename='{table_name}') b | ||||
ON SUBSTRING(b.indexdef, '\(.*\)') LIKE CONCAT('%', a.column_name, '%') | |||||
ON SUBSTRING(b.indexdef, '\(.*\)') LIKE CONCAT('%', a.column_name, '%') | |||||
WHERE a.table_name = '{table_name}' | WHERE a.table_name = '{table_name}' | ||||
GROUP BY a.column_name, a.data_type, a.column_default, a.character_maximum_length;''' | |||||
.format(table_name=table_name), as_dict=1) | |||||
GROUP BY a.column_name, a.data_type, a.column_default, a.character_maximum_length; | |||||
'''.format(table_name=table_name), as_dict=1) | |||||
def get_database_list(self, target): | def get_database_list(self, target): | ||||
return [d[0] for d in self.sql("SELECT datname FROM pg_database;")] | return [d[0] for d in self.sql("SELECT datname FROM pg_database;")] | ||||
@@ -52,8 +52,8 @@ class PostgresTable(DBTable): | |||||
query.append("ALTER COLUMN `{0}` TYPE {1} {2}".format( | query.append("ALTER COLUMN `{0}` TYPE {1} {2}".format( | ||||
col.fieldname, | col.fieldname, | ||||
get_definition(col.fieldtype, precision=col.precision, length=col.length), | get_definition(col.fieldtype, precision=col.precision, length=col.length), | ||||
using_clause) | |||||
) | |||||
using_clause | |||||
)) | |||||
for col in self.set_default: | for col in self.set_default: | ||||
if col.fieldname=="name": | if col.fieldname=="name": | ||||
@@ -73,37 +73,49 @@ class PostgresTable(DBTable): | |||||
query.append("ALTER COLUMN `{}` SET DEFAULT {}".format(col.fieldname, col_default)) | query.append("ALTER COLUMN `{}` SET DEFAULT {}".format(col.fieldname, col_default)) | ||||
create_index_query = "" | |||||
create_contraint_query = "" | |||||
for col in self.add_index: | for col in self.add_index: | ||||
# if index key not exists | # if index key not exists | ||||
create_index_query += 'CREATE INDEX IF NOT EXISTS "{index_name}" ON `{table_name}`(`{field}`);'.format( | |||||
create_contraint_query += 'CREATE INDEX IF NOT EXISTS "{index_name}" ON `{table_name}`(`{field}`);'.format( | |||||
index_name=col.fieldname, | index_name=col.fieldname, | ||||
table_name=self.table_name, | table_name=self.table_name, | ||||
field=col.fieldname) | field=col.fieldname) | ||||
drop_index_query = "" | |||||
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: | for col in self.drop_index: | ||||
# primary key | # primary key | ||||
if col.fieldname != 'name': | if col.fieldname != 'name': | ||||
# if index key exists | # if index key exists | ||||
if not frappe.db.has_index(self.table_name, col.fieldname): | |||||
drop_index_query += 'DROP INDEX IF EXISTS "{}" ;'.format(col.fieldname) | |||||
drop_contraint_query += 'DROP INDEX IF EXISTS "{}" ;'.format(col.fieldname) | |||||
if query: | |||||
try: | |||||
for col in self.drop_unique: | |||||
# primary key | |||||
if col.fieldname != 'name': | |||||
# if index key exists | |||||
drop_contraint_query += 'DROP INDEX IF EXISTS "unique_{}" ;'.format(col.fieldname) | |||||
try: | |||||
if query: | |||||
final_alter_query = "ALTER TABLE `{}` {}".format(self.table_name, ", ".join(query)) | final_alter_query = "ALTER TABLE `{}` {}".format(self.table_name, ", ".join(query)) | ||||
if final_alter_query: frappe.db.sql(final_alter_query) | if final_alter_query: frappe.db.sql(final_alter_query) | ||||
if create_index_query: frappe.db.sql(create_index_query) | |||||
if drop_index_query: frappe.db.sql(drop_index_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))) | |||||
raise e | |||||
else: | |||||
raise e | |||||
if create_contraint_query: frappe.db.sql(create_contraint_query) | |||||
if drop_contraint_query: 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))) | |||||
raise e | |||||
else: | |||||
raise e |
@@ -21,6 +21,7 @@ class DBTable: | |||||
self.change_name = [] | self.change_name = [] | ||||
self.add_unique = [] | self.add_unique = [] | ||||
self.add_index = [] | self.add_index = [] | ||||
self.drop_unique = [] | |||||
self.drop_index = [] | self.drop_index = [] | ||||
self.set_default = [] | self.set_default = [] | ||||
@@ -219,8 +220,10 @@ class DbColumn: | |||||
self.table.change_type.append(self) | self.table.change_type.append(self) | ||||
# unique | # unique | ||||
if((self.unique and not current_def['unique']) and column_type not in ('text', 'longtext')): | |||||
if ((self.unique and not current_def['unique']) and column_type not in ('text', 'longtext')): | |||||
self.table.add_unique.append(self) | self.table.add_unique.append(self) | ||||
elif (current_def['unique'] and not self.unique): | |||||
self.table.drop_unique.append(self) | |||||
# default | # default | ||||
if (self.default_changed(current_def) | if (self.default_changed(current_def) | ||||
@@ -230,9 +233,7 @@ class DbColumn: | |||||
self.table.set_default.append(self) | self.table.set_default.append(self) | ||||
# index should be applied or dropped irrespective of type change | # index should be applied or dropped irrespective of type change | ||||
if ((current_def['index'] and not self.set_index) | |||||
or (current_def['unique'] and not self.unique)): | |||||
# to drop unique you have to drop index | |||||
if (current_def['index'] and not self.set_index): | |||||
self.table.drop_index.append(self) | self.table.drop_index.append(self) | ||||
elif (not current_def['index'] and self.set_index) and not (column_type in ('text', 'longtext')): | elif (not current_def['index'] and self.set_index) and not (column_type in ('text', 'longtext')): | ||||