From 41006cd4a94e7d9985aa1c5f382a2f8a7efb6c18 Mon Sep 17 00:00:00 2001 From: git-avc Date: Sun, 23 Nov 2025 11:57:11 +0100 Subject: [PATCH 1/5] fix: show rows number copied to clipboard --- frappe/public/js/frappe/list/list_view.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 6bf010699d..61aa03f0c8 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -2412,7 +2412,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { .join("\t"); }); const clipboard_data = [headers, ...rows].join("\n"); // Copy to clipboard - frappe.utils.copy_to_clipboard(clipboard_data); + const message = __("Copied {0} rows to clipboard", [selected_items.length]); + frappe.utils.copy_to_clipboard(clipboard_data, message); }, standard: true, }; From 5fecde5a6ae66b35dfeece6100b1312cfd5531c4 Mon Sep 17 00:00:00 2001 From: Aarol D'Souza <98270103+AarDG10@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:05:20 +0530 Subject: [PATCH 2/5] test(postgres): enable test_unique_index_on_alter for postgres (#34719) * test(postgres): enable test_unique_index_on_alter for postgres * perf(postgres): Prevent redundant unique index creation on new columns --- frappe/database/postgres/schema.py | 9 ++++++--- frappe/tests/test_db_update.py | 1 - 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/frappe/database/postgres/schema.py b/frappe/database/postgres/schema.py index 75b944f74a..aca6a778f3 100644 --- a/frappe/database/postgres/schema.py +++ b/frappe/database/postgres/schema.py @@ -76,6 +76,8 @@ class PostgresTable(DBTable): query = [f"ADD COLUMN `{col.fieldname}` {col.get_definition()}" for col in self.add_column] + new_column_names = {col.fieldname for col in self.add_column} + for col in self.change_type: using_clause = "" if col.fieldtype in ("Datetime"): @@ -124,9 +126,10 @@ class PostgresTable(DBTable): 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 - ) + if col.fieldname not in new_column_names: + 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: diff --git a/frappe/tests/test_db_update.py b/frappe/tests/test_db_update.py index a6e483e29e..5deb94cccb 100644 --- a/frappe/tests/test_db_update.py +++ b/frappe/tests/test_db_update.py @@ -139,7 +139,6 @@ class TestDBUpdate(IntegrationTestCase): with self.subTest(f"Checking index {doctype.name} - {field.fieldname}"): self.check_unique_indexes(doctype.name, field.fieldname) - @run_only_if(db_type_is.MARIADB) def test_unique_index_on_alter(self): """Only one unique index should be added""" From bd044e520c1b6b78ceeb62da2d39376ebcecd4c9 Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Mon, 24 Nov 2025 11:13:43 +0530 Subject: [PATCH 3/5] fix(query): don't allow using `Document` as a filter value (#34823) Signed-off-by: Akhil Narang --- frappe/database/query.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/database/query.py b/frappe/database/query.py index b034aa7812..94f992ed5e 100644 --- a/frappe/database/query.py +++ b/frappe/database/query.py @@ -20,6 +20,7 @@ from frappe.database.utils import ( ) from frappe.model import get_permitted_fields from frappe.model.base_document import DOCTYPES_FOR_DOCTYPE +from frappe.model.document import Document from frappe.query_builder import Criterion, Field, Order, functions from frappe.query_builder.custom import Month, MonthName, Quarter from frappe.query_builder.utils import PseudoColumnMapper @@ -454,6 +455,9 @@ class Engine: else: # Regular value processing for literal comparisons like: table.field = 'value' _value = convert_to_value(value) + + if isinstance(value, Document): + frappe.throw(_("Document cannot be used as a filter value")) _operator = operator # For Date fields with datetime values, convert to date to match db_query behavior From c57444bc846e57a680e26397a90ebb6314a92f18 Mon Sep 17 00:00:00 2001 From: Aarol D'Souza <98270103+AarDG10@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:22:21 +0530 Subject: [PATCH 4/5] fix(postgres): fix query for pg compatibility in clear_log_table (#34706) --- frappe/commands/test_commands.py | 7 +++-- .../core/doctype/log_settings/log_settings.py | 28 +++++++++++++------ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/frappe/commands/test_commands.py b/frappe/commands/test_commands.py index ad00bb0933..cbac71a030 100644 --- a/frappe/commands/test_commands.py +++ b/frappe/commands/test_commands.py @@ -733,12 +733,14 @@ class TestBackups(BaseTestCommands): self.assertIsNotNone(after_backup["public"]) self.assertIsNotNone(after_backup["private"]) - @run_only_if(db_type_is.MARIADB) def test_clear_log_table(self): d = frappe.get_doc(doctype="Error Log", title="Something").insert() d.db_set("creation", "2010-01-01", update_modified=False) frappe.db.commit() - + frappe.db.sql_ddl( + IntegrationTestCase.normalize_sql("DROP TABLE IF EXISTS `tabError Log backup_table`") + ) # drop old tables if exists (Maintain Sanity) + frappe.db.sql_ddl(IntegrationTestCase.normalize_sql("DROP TABLE IF EXISTS `tabError Log temp_table`")) tables_before = frappe.db.get_tables(cached=False) self.execute("bench --site {site} clear-log-table --days=30 --doctype='Error Log'") @@ -747,7 +749,6 @@ class TestBackups(BaseTestCommands): self.assertFalse(frappe.db.exists("Error Log", d.name)) tables_after = frappe.db.get_tables(cached=False) - self.assertEqual(set(tables_before), set(tables_after)) def test_backup_with_custom_path(self): diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py index d3be04ced2..afbccc853f 100644 --- a/frappe/core/doctype/log_settings/log_settings.py +++ b/frappe/core/doctype/log_settings/log_settings.py @@ -164,18 +164,30 @@ def clear_log_table(doctype, days=90): backup = f"{original} backup_table" try: - frappe.db.sql_ddl(f"CREATE TABLE `{temporary}` LIKE `{original}`") + if frappe.db.db_type == "postgres": + frappe.db.sql_ddl(f'CREATE TABLE "{temporary}" (LIKE "{original}" INCLUDING ALL)') - # Copy all recent data to new table - frappe.db.sql( - f"""INSERT INTO `{temporary}` - SELECT * FROM `{original}` - WHERE `{original}`.`creation` > NOW() - INTERVAL '{days}' DAY""" - ) - frappe.db.sql_ddl(f"RENAME TABLE `{original}` TO `{backup}`, `{temporary}` TO `{original}`") + frappe.db.sql( + f"""INSERT INTO "{temporary}" + SELECT * FROM "{original}" + WHERE "{original}"."creation" > NOW() - INTERVAL '{days}' DAY""" + ) + frappe.db.sql_ddl(f'ALTER TABLE "{original}" RENAME TO "{backup}"') + frappe.db.sql_ddl(f'ALTER TABLE "{temporary}" RENAME TO "{original}"') + elif frappe.db.db_type == "mariadb": + frappe.db.sql_ddl(f"CREATE TABLE `{temporary}` LIKE `{original}`") + + # Copy all recent data to new table + frappe.db.sql( + f"""INSERT INTO `{temporary}` + SELECT * FROM `{original}` + WHERE `{original}`.`creation` > NOW() - INTERVAL '{days}' DAY""" + ) + frappe.db.sql_ddl(f"RENAME TABLE `{original}` TO `{backup}`, `{temporary}` TO `{original}`") except Exception: frappe.db.rollback() frappe.db.sql_ddl(f"DROP TABLE IF EXISTS `{temporary}`") raise else: frappe.db.sql_ddl(f"DROP TABLE `{backup}`") + frappe.db.commit() From ef5d2213e0bbb8fd664df279f60053646730a2c2 Mon Sep 17 00:00:00 2001 From: Aarol D'Souza <98270103+AarDG10@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:23:24 +0530 Subject: [PATCH 5/5] test(postgres): enable test_restore for postgres (#34768) --- frappe/commands/test_commands.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/commands/test_commands.py b/frappe/commands/test_commands.py index cbac71a030..a7974e5b61 100644 --- a/frappe/commands/test_commands.py +++ b/frappe/commands/test_commands.py @@ -244,7 +244,10 @@ class TestCommands(BaseTestCommands): self.assertEqual(self.returncode, 0) self.assertEqual(self.stdout, frappe.bold(text="DocType")) - @run_only_if(db_type_is.MARIADB) + @skipIf( + frappe.conf.db_type == "sqlite", + "Not for SQLite for now", + ) def test_restore(self): # step 0: create a site to run the test on global_config = {