388 lines
12 KiB
Python
388 lines
12 KiB
Python
##########################################################################
|
|
#
|
|
# pgAdmin 4 - PostgreSQL Tools
|
|
#
|
|
# Copyright (C) 2013 - 2026, The pgAdmin Development Team
|
|
# This software is released under the PostgreSQL Licence
|
|
#
|
|
##########################################################################
|
|
|
|
"""Section definitions for report generation pipeline.
|
|
|
|
Each report type has a set of sections that can be analyzed independently.
|
|
Sections are mapped to SQL queries and have descriptions for LLM guidance.
|
|
"""
|
|
|
|
from pgadmin.llm.reports.models import Section
|
|
|
|
# =============================================================================
|
|
# SECURITY REPORT SECTIONS
|
|
# =============================================================================
|
|
|
|
SECURITY_SECTIONS = [
|
|
Section(
|
|
id='authentication',
|
|
name='Authentication Configuration',
|
|
description=(
|
|
'Password policies, SSL/TLS settings, authentication methods, '
|
|
'and connection security settings.'
|
|
),
|
|
queries=['security_settings', 'hba_rules'],
|
|
scope=['server']
|
|
),
|
|
Section(
|
|
id='access_control',
|
|
name='Access Control & Roles',
|
|
description=(
|
|
'Superuser accounts, privileged roles, login roles, '
|
|
'and role privilege assignments.'
|
|
),
|
|
queries=['superusers', 'privileged_roles', 'roles_no_expiry'],
|
|
scope=['server', 'database']
|
|
),
|
|
Section(
|
|
id='network_security',
|
|
name='Network Security',
|
|
description=(
|
|
'Network exposure settings, listen addresses, connection limits, '
|
|
'and pg_hba.conf rules.'
|
|
),
|
|
queries=['security_settings', 'hba_rules'],
|
|
scope=['server']
|
|
),
|
|
Section(
|
|
id='encryption',
|
|
name='Encryption & SSL',
|
|
description=(
|
|
'SSL/TLS configuration, password encryption method, '
|
|
'and data-at-rest encryption settings.'
|
|
),
|
|
queries=['security_settings'],
|
|
scope=['server']
|
|
),
|
|
Section(
|
|
id='object_permissions',
|
|
name='Object Permissions',
|
|
description=(
|
|
'Schema, table, and function access control lists (ACLs), '
|
|
'default privileges, and ownership.'
|
|
),
|
|
queries=['database_settings', 'schema_acls', 'table_acls',
|
|
'default_privileges'],
|
|
scope=['database']
|
|
),
|
|
Section(
|
|
id='rls_policies',
|
|
name='Row-Level Security',
|
|
description=(
|
|
'Row-level security policies, RLS-enabled tables, '
|
|
'and policy coverage analysis.'
|
|
),
|
|
queries=['rls_enabled_tables', 'rls_policies'],
|
|
scope=['database', 'schema']
|
|
),
|
|
Section(
|
|
id='security_definer',
|
|
name='Security Definer Functions',
|
|
description=(
|
|
'Functions running with elevated privileges (SECURITY DEFINER), '
|
|
'their ownership, and permissions.'
|
|
),
|
|
queries=['security_definer_functions'],
|
|
scope=['database', 'schema']
|
|
),
|
|
Section(
|
|
id='audit_logging',
|
|
name='Audit & Logging',
|
|
description=(
|
|
'Connection logging, statement logging, error logging, '
|
|
'and audit trail configuration.'
|
|
),
|
|
queries=['logging_settings'],
|
|
scope=['server']
|
|
),
|
|
Section(
|
|
id='extensions',
|
|
name='Extensions',
|
|
description=(
|
|
'Installed extensions and their security implications.'
|
|
),
|
|
queries=['extensions'],
|
|
scope=['server', 'database']
|
|
),
|
|
]
|
|
|
|
# =============================================================================
|
|
# PERFORMANCE REPORT SECTIONS
|
|
# =============================================================================
|
|
|
|
PERFORMANCE_SECTIONS = [
|
|
Section(
|
|
id='memory_config',
|
|
name='Memory Configuration',
|
|
description=(
|
|
'shared_buffers, work_mem, effective_cache_size, '
|
|
'maintenance_work_mem, and other memory settings.'
|
|
),
|
|
queries=['memory_settings'],
|
|
scope=['server']
|
|
),
|
|
Section(
|
|
id='checkpoint_wal',
|
|
name='Checkpoint & WAL',
|
|
description=(
|
|
'Checkpoint settings, WAL configuration, background writer stats, '
|
|
'and write-ahead log tuning.'
|
|
),
|
|
queries=['checkpoint_settings', 'wal_settings', 'bgwriter_stats'],
|
|
scope=['server']
|
|
),
|
|
Section(
|
|
id='autovacuum',
|
|
name='Autovacuum Configuration',
|
|
description=(
|
|
'Autovacuum settings, tables needing vacuum, '
|
|
'dead tuple accumulation, and maintenance status.'
|
|
),
|
|
queries=['autovacuum_settings', 'tables_needing_vacuum'],
|
|
scope=['server', 'database']
|
|
),
|
|
Section(
|
|
id='query_planner',
|
|
name='Query Planner Settings',
|
|
description=(
|
|
'Cost parameters, statistics targets, JIT compilation, '
|
|
'and planner optimization settings.'
|
|
),
|
|
queries=['planner_settings'],
|
|
scope=['server']
|
|
),
|
|
Section(
|
|
id='parallelism',
|
|
name='Parallelism & Workers',
|
|
description=(
|
|
'Parallel query configuration, worker processes, '
|
|
'and parallel maintenance settings.'
|
|
),
|
|
queries=['parallel_settings'],
|
|
scope=['server']
|
|
),
|
|
Section(
|
|
id='connection_pooling',
|
|
name='Connection Management',
|
|
description=(
|
|
'Max connections, reserved connections, timeouts, '
|
|
'and current connection status.'
|
|
),
|
|
queries=['connection_settings', 'active_connections'],
|
|
scope=['server']
|
|
),
|
|
Section(
|
|
id='cache_efficiency',
|
|
name='Cache Efficiency',
|
|
description=(
|
|
'Buffer cache hit ratios, database-level cache stats, '
|
|
'and table-level I/O patterns.'
|
|
),
|
|
queries=['database_stats', 'table_cache_stats'],
|
|
scope=['server', 'database']
|
|
),
|
|
Section(
|
|
id='index_usage',
|
|
name='Index Analysis',
|
|
description=(
|
|
'Index utilization, unused indexes, tables needing indexes, '
|
|
'and index size analysis.'
|
|
),
|
|
queries=['table_stats', 'unused_indexes', 'tables_needing_indexes',
|
|
'table_sizes'],
|
|
scope=['database']
|
|
),
|
|
Section(
|
|
id='slow_queries',
|
|
name='Query Performance',
|
|
description=(
|
|
'Slowest queries, most frequent queries, '
|
|
'and query execution statistics (requires pg_stat_statements).'
|
|
),
|
|
queries=['stat_statements_check', 'top_queries_by_time',
|
|
'top_queries_by_calls'],
|
|
scope=['server', 'database']
|
|
),
|
|
Section(
|
|
id='replication',
|
|
name='Replication Status',
|
|
description=(
|
|
'Replication lag, standby status, and WAL sender statistics.'
|
|
),
|
|
queries=['replication_status'],
|
|
scope=['server']
|
|
),
|
|
]
|
|
|
|
# =============================================================================
|
|
# DESIGN REPORT SECTIONS
|
|
# =============================================================================
|
|
|
|
DESIGN_SECTIONS = [
|
|
Section(
|
|
id='table_structure',
|
|
name='Table Structure',
|
|
description=(
|
|
'Table definitions, column counts, sizes, ownership, '
|
|
'and documentation coverage.'
|
|
),
|
|
queries=['tables_overview', 'columns_info'],
|
|
scope=['database', 'schema']
|
|
),
|
|
Section(
|
|
id='primary_keys',
|
|
name='Primary Key Analysis',
|
|
description=(
|
|
'Primary key design, tables without primary keys, '
|
|
'and key column choices.'
|
|
),
|
|
queries=['primary_keys', 'tables_without_pk'],
|
|
scope=['database', 'schema']
|
|
),
|
|
Section(
|
|
id='foreign_keys',
|
|
name='Referential Integrity',
|
|
description=(
|
|
'Foreign key relationships, orphan references, '
|
|
'and relationship coverage.'
|
|
),
|
|
queries=['foreign_keys'],
|
|
scope=['database', 'schema']
|
|
),
|
|
Section(
|
|
id='indexes',
|
|
name='Index Strategy',
|
|
description=(
|
|
'Index definitions, duplicate indexes, index types, '
|
|
'and coverage analysis.'
|
|
),
|
|
queries=['indexes_info', 'duplicate_indexes'],
|
|
scope=['database', 'schema']
|
|
),
|
|
Section(
|
|
id='constraints',
|
|
name='Constraints',
|
|
description=(
|
|
'Check constraints, unique constraints, '
|
|
'and data validation coverage.'
|
|
),
|
|
queries=['check_constraints', 'unique_constraints'],
|
|
scope=['database', 'schema']
|
|
),
|
|
Section(
|
|
id='normalization',
|
|
name='Normalization Analysis',
|
|
description=(
|
|
'Repeated column patterns, potential denormalization issues, '
|
|
'and data redundancy.'
|
|
),
|
|
queries=['repeated_column_names'],
|
|
scope=['database']
|
|
),
|
|
Section(
|
|
id='naming_conventions',
|
|
name='Naming Conventions',
|
|
description=(
|
|
'Table and column naming patterns, consistency analysis, '
|
|
'and naming standard compliance.'
|
|
),
|
|
queries=['object_names'],
|
|
scope=['database', 'schema']
|
|
),
|
|
Section(
|
|
id='data_types',
|
|
name='Data Type Review',
|
|
description=(
|
|
'Data type usage patterns, type consistency, '
|
|
'and type appropriateness.'
|
|
),
|
|
queries=['column_types'],
|
|
scope=['database']
|
|
),
|
|
]
|
|
|
|
# =============================================================================
|
|
# SECTION LOOKUPS
|
|
# =============================================================================
|
|
|
|
# Convert lists to dictionaries for quick lookup
|
|
SECURITY_SECTIONS_DICT = {s.id: s for s in SECURITY_SECTIONS}
|
|
PERFORMANCE_SECTIONS_DICT = {s.id: s for s in PERFORMANCE_SECTIONS}
|
|
DESIGN_SECTIONS_DICT = {s.id: s for s in DESIGN_SECTIONS}
|
|
|
|
# Combined lookup by report type
|
|
SECTIONS_BY_TYPE = {
|
|
'security': SECURITY_SECTIONS,
|
|
'performance': PERFORMANCE_SECTIONS,
|
|
'design': DESIGN_SECTIONS,
|
|
}
|
|
|
|
SECTIONS_DICT_BY_TYPE = {
|
|
'security': SECURITY_SECTIONS_DICT,
|
|
'performance': PERFORMANCE_SECTIONS_DICT,
|
|
'design': DESIGN_SECTIONS_DICT,
|
|
}
|
|
|
|
|
|
def get_sections_for_report(report_type: str) -> list[Section]:
|
|
"""Get all sections for a report type.
|
|
|
|
Args:
|
|
report_type: One of 'security', 'performance', 'design'.
|
|
|
|
Returns:
|
|
List of Section objects.
|
|
|
|
Raises:
|
|
ValueError: If report_type is invalid.
|
|
"""
|
|
sections = SECTIONS_BY_TYPE.get(report_type)
|
|
if sections is None:
|
|
raise ValueError(f"Invalid report type: {report_type}")
|
|
return sections
|
|
|
|
|
|
def get_sections_for_scope(
|
|
report_type: str,
|
|
scope: str
|
|
) -> list[Section]:
|
|
"""Get sections applicable to a specific scope.
|
|
|
|
Args:
|
|
report_type: One of 'security', 'performance', 'design'.
|
|
scope: One of 'server', 'database', 'schema'.
|
|
|
|
Returns:
|
|
List of Section objects applicable to the scope.
|
|
"""
|
|
all_sections = get_sections_for_report(report_type)
|
|
return [s for s in all_sections if scope in s.scope]
|
|
|
|
|
|
def get_section(report_type: str, section_id: str) -> Section:
|
|
"""Get a specific section by ID.
|
|
|
|
Args:
|
|
report_type: One of 'security', 'performance', 'design'.
|
|
section_id: The section identifier.
|
|
|
|
Returns:
|
|
Section object.
|
|
|
|
Raises:
|
|
ValueError: If section not found.
|
|
"""
|
|
sections_dict = SECTIONS_DICT_BY_TYPE.get(report_type, {})
|
|
section = sections_dict.get(section_id)
|
|
if section is None:
|
|
raise ValueError(
|
|
f"Section '{section_id}' not found in {report_type} report"
|
|
)
|
|
return section
|