Metadata-Version: 2.4
Name: plananalyze
Version: 0.1.0
Summary: PostgreSQL EXPLAIN Plan Analyzer - Extract insights from execution plans
Home-page: https://github.com/GujaLomsadze/PlanAnalyzer
Author: Guja Lomsadze
Author-email: Guja Lomsadze <lomsadze.guja@gmail.com>
License: Apache-2.0
Project-URL: Homepage, https://github.com/GujaLomsadze/PlanAnalyzer
Project-URL: Repository, https://github.com/GujaLomsadze/PlanAnalyzer.git
Project-URL: Bug Tracker, https://github.com/GujaLomsadze/PlanAnalyzer/issues
Keywords: postgresql,explain,query,performance,database,analysis,optimizer,sql,plan,bottleneck
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: PyYAML>=6.0
Requires-Dist: lxml>=4.9.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: isort>=5.12.0; extra == "dev"
Requires-Dist: flake8>=6.0.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
Requires-Dist: tox>=4.0.0; extra == "dev"
Provides-Extra: docs
Requires-Dist: sphinx>=5.0.0; extra == "docs"
Requires-Dist: sphinx-rtd-theme>=1.2.0; extra == "docs"
Requires-Dist: myst-parser>=1.0.0; extra == "docs"
Provides-Extra: test
Requires-Dist: pytest>=7.0.0; extra == "test"
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
Requires-Dist: pytest-mock>=3.10.0; extra == "test"
Requires-Dist: coverage[toml]>=7.0.0; extra == "test"
Dynamic: author
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python

# `plananalyze`

# PostgreSQL `EXPLAIN` Plan Analyzer - Extract insights from execution plans Query

## For best results, run `EXPLAIN` like so `EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS)`

``

# Quickstart

1. Run "ANALYZE" on your Query
2. Copy-paste output from PostgreSQL
3. Review details :)

```python
from plananalyze import analyze_plan

plan = """
Hash Join  (cost=12.70..30.50 rows=120 width=743)
  Hash Cond: ((d.name)::text = (employees.department)::text)
  ->  Seq Scan on departments d  (cost=0.00..14.80 rows=480 width=142)
  ->  Hash  (cost=11.20..11.20 rows=120 width=601)
        ->  Seq Scan on employees  (cost=0.00..11.20 rows=120 width=601)
"""

result = analyze_plan(plan, format_type="summary")
print(result)
```

Output:

```shell
📊 EXECUTION OVERVIEW:
   Total Cost: 30.50
   Node Count: 5

🔍 OPERATIONS:
   Sequential Scans: 2
   Index Scans: 0
   Joins: 1
   Sorts: 0
```

# Detailed Response

```python
# Use format_type="detailed"
result = analyze_plan(plan, format_type="detailed")
```

Output:

```shell
📈 EXECUTION METRICS:
   Total Cost: 30.50
   Root Operation: Hash Join

🏗️  PLAN STRUCTURE:
   Total Nodes: 5
   Plan Depth: 0

🔍 OPERATION BREAKDOWN:
   Sequential Scans: 2
   Index Scans: 0
   Index Only Scans: 0
   Bitmap Scans: 0
   Join Operations: 1
   Sort Operations: 0
   Hash Operations: 2
   Aggregate Operations: 0

💰 MOST EXPENSIVE OPERATIONS:
   1. Hash Join - Cost: 30.50 (100.0%)
   2. ->  Seq Scan on departments - Cost: 14.80 (48.5%)
   3. ->  Hash - Cost: 11.20 (36.7%)
```

# JSON output

```python
# Use format_type="json"
result = analyze_plan(plan, format_type="json")
```

# JSON Input

### Run "explain" on PostgreSQL query using `EXPLAIN (FORMAT JSON)`

```python
from plananalyze import analyze_plan

plan_json = """
[
  {
    "Plan": {
      "Node Type": "Hash Join",
      "Parallel Aware": false,
      "Async Capable": false,
      "Join Type": "Inner",
      "Startup Cost": 12.70,
      "Total Cost": 30.50,
      "Plan Rows": 120,
      "Plan Width": 743,
      "Inner Unique": false,
      "Hash Cond": "((d.name)::text = (employees.department)::text)",
      "Plans": [
        {
          "Node Type": "Seq Scan",
          "Parent Relationship": "Outer",
          "Parallel Aware": false,
          "Async Capable": false,
          "Relation Name": "departments",
          "Alias": "d",
          "Startup Cost": 0.00,
          "Total Cost": 14.80,
          "Plan Rows": 480,
          "Plan Width": 142
        },
        {
          "Node Type": "Hash",
          "Parent Relationship": "Inner",
          "Parallel Aware": false,
          "Async Capable": false,
          "Startup Cost": 11.20,
          "Total Cost": 11.20,
          "Plan Rows": 120,
          "Plan Width": 601,
          "Plans": [
            {
              "Node Type": "Seq Scan",
              "Parent Relationship": "Outer",
              "Parallel Aware": false,
              "Async Capable": false,
              "Relation Name": "employees",
              "Alias": "employees",
              "Startup Cost": 0.00,
              "Total Cost": 11.20,
              "Plan Rows": 120,
              "Plan Width": 601
            }
          ]
        }
      ]
    }
  }
]
"""

result = analyze_plan(plan_json, format_type="summary")
print(result)
```
