diff --git a/pgmanage/app/migrations/0031_userdetails_show_system_catalogs.py b/pgmanage/app/migrations/0031_userdetails_show_system_catalogs.py new file mode 100644 index 000000000..10f59a1ff --- /dev/null +++ b/pgmanage/app/migrations/0031_userdetails_show_system_catalogs.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.23 on 2025-11-14 12:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0030_populate_mssql-technology'), + ] + + operations = [ + migrations.AddField( + model_name='userdetails', + name='show_system_catalogs', + field=models.BooleanField(default=False), + ), + ] diff --git a/pgmanage/app/models/main.py b/pgmanage/app/models/main.py index e37b81590..b7122e040 100644 --- a/pgmanage/app/models/main.py +++ b/pgmanage/app/models/main.py @@ -22,6 +22,7 @@ class UserDetails(models.Model): pigz_path = models.CharField(max_length=256, null=True) restore_tabs = models.BooleanField(default=True) scroll_tree = models.BooleanField(default=True) + show_system_catalogs = models.BooleanField(default=False) def get_pigz_path(self): diff --git a/pgmanage/app/static/pgmanage_frontend/src/components/SettingsModal.vue b/pgmanage/app/static/pgmanage_frontend/src/components/SettingsModal.vue index 31c04be29..99bbe364a 100644 --- a/pgmanage/app/static/pgmanage_frontend/src/components/SettingsModal.vue +++ b/pgmanage/app/static/pgmanage_frontend/src/components/SettingsModal.vue @@ -102,6 +102,15 @@ +
+
+
+ + +
+
+
+
@@ -358,6 +367,14 @@ export default { settingsStore.setScrollTree(value) } }, + showSystemCatalogs: { + get() { + return settingsStore.showSystemCatalogs + }, + set(value) { + settingsStore.setShowSystemCatalogs(value) + } + }, shortcuts() { return settingsStore.shortcuts }, diff --git a/pgmanage/app/static/pgmanage_frontend/src/stores/settings.js b/pgmanage/app/static/pgmanage_frontend/src/stores/settings.js index b1a8a7445..e3c6965b8 100644 --- a/pgmanage/app/static/pgmanage_frontend/src/stores/settings.js +++ b/pgmanage/app/static/pgmanage_frontend/src/stores/settings.js @@ -22,6 +22,7 @@ const useSettingsStore = defineStore("settings", { shortcuts: {}, currentOS: "Unknown OS", max_upload_size: "", + showSystemCatalogs: "", }), actions: { async getSettings() { @@ -43,6 +44,7 @@ const useSettingsStore = defineStore("settings", { ? "YYYY-MM-DD, HH:mm:ss" : userSettings.date_format, max_upload_size: userSettings.max_upload_size, + showSystemCatalogs: userSettings.show_system_catalogs, }); this.shortcuts = Object.assign( @@ -74,6 +76,7 @@ const useSettingsStore = defineStore("settings", { pigz_path: this.pigzPath, restore_tabs: this.restoreTabs, scroll_tree: this.scrollTree, + show_system_catalogs: this.showSystemCatalogs, }, }); @@ -121,6 +124,9 @@ const useSettingsStore = defineStore("settings", { setScrollTree(value) { this.scrollTree = value; }, + setShowSystemCatalogs(value) { + this.showSystemCatalogs = value; + }, showModal() { Modal.getOrCreateInstance("#modal_settings", { backdrop: "static", diff --git a/pgmanage/app/tests/test_postgresql12.py b/pgmanage/app/tests/test_postgresql12.py index 3453c9431..9b9aa41fa 100644 --- a/pgmanage/app/tests/test_postgresql12.py +++ b/pgmanage/app/tests/test_postgresql12.py @@ -987,15 +987,34 @@ def test_get_schemas_postgresql_nosession(self): response = self.client_nosession.post('/get_schemas_postgresql/') assert 401 == response.status_code - def test_get_schemas_postgresql_session(self): + def test_get_schemas_postgresql_session_with_system_catalogs(self): + user = User.objects.get(username="admin") + user.userdetails.show_system_catalogs = True + user.userdetails.save() + response = self.client_session.post('/get_schemas_postgresql/', {'data': '{"database_index": 0, "workspace_id": 0}'}) assert 200 == response.status_code - data = json.loads(response.content.decode()) + data = response.json() assert self.lists_equal([a['name'] for a in data], [ 'public', 'pg_catalog', 'information_schema' ]) + + def test_get_schemas_postgresql_session_without_system_catalogs(self): + user = User.objects.get(username="admin") + user.userdetails.show_system_catalogs = False + user.userdetails.save() + + response = self.client_session.post('/get_schemas_postgresql/', {'data': '{"database_index": 0, "workspace_id": 0}'}) + assert response.status_code == 200 + + data = json.loads(response.content.decode()) + + assert self.lists_equal( + [a['name'] for a in data], + ['public'] + ) def test_get_columns_postgresql_nosession(self): response = self.client_nosession.post('/get_columns_postgresql/') diff --git a/pgmanage/app/views/tree_mariadb.py b/pgmanage/app/views/tree_mariadb.py index 8ce7a355d..147e9d034 100644 --- a/pgmanage/app/views/tree_mariadb.py +++ b/pgmanage/app/views/tree_mariadb.py @@ -253,6 +253,7 @@ def get_indexes_columns(request, database): def get_databases(request, database): try: conn_object = Connection.objects.get(id=database.conn_id) + system_dbs = {"mysql", "information_schema", "performance_schema", "sys"} databases = database.QueryDatabases() list_databases = [ @@ -261,6 +262,7 @@ def get_databases(request, database): "pinned": db[0] in conn_object.pinned_databases, } for db in databases.Rows + if request.user.userdetails.show_system_catalogs or db[0] not in system_dbs ] except Exception as exc: return JsonResponse(data={"data": str(exc)}, status=400) diff --git a/pgmanage/app/views/tree_mssql.py b/pgmanage/app/views/tree_mssql.py index 99e5cd404..24ce42550 100644 --- a/pgmanage/app/views/tree_mssql.py +++ b/pgmanage/app/views/tree_mssql.py @@ -55,11 +55,14 @@ def get_tree_info(request, database): @database_required(check_timeout=True, open_connection=True) def get_databases(request, database): list_databases = [] + system_dbs = {"master", "model", "msdb", "tempdb"} try: conn_object = Connection.objects.get(id=database.conn_id) databases = database.QueryDatabases() for database_object in databases.Rows: + if not request.user.userdetails.show_system_catalogs and database_object[0] in system_dbs: + continue database_data = { "name": database_object[0], "pinned": database_object[0] in conn_object.pinned_databases, @@ -75,10 +78,13 @@ def get_databases(request, database): @database_required(check_timeout=True, open_connection=True) def get_schemas(request, database): schemas_list = [] + system_schemas = {"sys", "INFORMATION_SCHEMA"} try: schemas = database.QuerySchemas() for schema in schemas.Rows: + if not request.user.userdetails.show_system_catalogs and schema["schema_name"] in system_schemas: + continue schema_data = { "name": schema["schema_name"], } diff --git a/pgmanage/app/views/tree_mysql.py b/pgmanage/app/views/tree_mysql.py index be15704b1..6fc4193f9 100644 --- a/pgmanage/app/views/tree_mysql.py +++ b/pgmanage/app/views/tree_mysql.py @@ -282,6 +282,7 @@ def get_indexes_columns(request, database): def get_databases(request, database): try: conn_object = Connection.objects.get(id=database.conn_id) + system_dbs = {"mysql", "information_schema", "performance_schema", "sys"} databases = database.QueryDatabases() list_databases = [ @@ -290,6 +291,7 @@ def get_databases(request, database): "pinned": db[0] in conn_object.pinned_databases, } for db in databases.Rows + if request.user.userdetails.show_system_catalogs or db[0] not in system_dbs ] except Exception as exc: return JsonResponse(data={"data": str(exc)}, status=400) diff --git a/pgmanage/app/views/tree_postgresql.py b/pgmanage/app/views/tree_postgresql.py index 3231a9583..4942199a0 100644 --- a/pgmanage/app/views/tree_postgresql.py +++ b/pgmanage/app/views/tree_postgresql.py @@ -817,10 +817,12 @@ def get_mview_definition(request, database): @database_required(check_timeout=True, open_connection=True) def get_schemas(request, database): schemas_list = [] - + system_schemas = {"information_schema", "pg_catalog"} try: schemas = database.QuerySchemas() for schema in schemas.Rows: + if not request.user.userdetails.show_system_catalogs and schema["schema_name"] in system_schemas: + continue schema_data = { "name": schema["schema_name"], "oid": schema["oid"],