natquery 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- natquery-1.0.0/LICENSE +21 -0
- natquery-1.0.0/PKG-INFO +147 -0
- natquery-1.0.0/README.md +108 -0
- natquery-1.0.0/pyproject.toml +75 -0
- natquery-1.0.0/setup.cfg +4 -0
- natquery-1.0.0/src/natquery/__init__.py +0 -0
- natquery-1.0.0/src/natquery/cli/__init__.py +0 -0
- natquery-1.0.0/src/natquery/cli/commands.py +256 -0
- natquery-1.0.0/src/natquery/cli/formatter.py +126 -0
- natquery-1.0.0/src/natquery/cli/main.py +51 -0
- natquery-1.0.0/src/natquery/cli/shell.py +135 -0
- natquery-1.0.0/src/natquery/config/__init__.py +0 -0
- natquery-1.0.0/src/natquery/config/connection.py +123 -0
- natquery-1.0.0/src/natquery/config/settings.py +100 -0
- natquery-1.0.0/src/natquery/execution/__init__.py +0 -0
- natquery-1.0.0/src/natquery/execution/engine.py +29 -0
- natquery-1.0.0/src/natquery/execution/explain.py +51 -0
- natquery-1.0.0/src/natquery/llm/__init__.py +0 -0
- natquery-1.0.0/src/natquery/llm/client.py +37 -0
- natquery-1.0.0/src/natquery/llm/self_corrector.py +84 -0
- natquery-1.0.0/src/natquery/ml/__init__.py +0 -0
- natquery-1.0.0/src/natquery/observability/__init__.py +0 -0
- natquery-1.0.0/src/natquery/observability/cost_analyzer.py +79 -0
- natquery-1.0.0/src/natquery/observability/index_recommender.py +58 -0
- natquery-1.0.0/src/natquery/observability/logger.py +109 -0
- natquery-1.0.0/src/natquery/observability/performance_api.py +58 -0
- natquery-1.0.0/src/natquery/orchestration/__init__.py +0 -0
- natquery-1.0.0/src/natquery/orchestration/error_classifier.py +29 -0
- natquery-1.0.0/src/natquery/orchestration/error_handler.py +81 -0
- natquery-1.0.0/src/natquery/orchestration/pipeline.py +192 -0
- natquery-1.0.0/src/natquery/orchestration/workspace.py +44 -0
- natquery-1.0.0/src/natquery/prompt/__init__.py +0 -0
- natquery-1.0.0/src/natquery/prompt/builder.py +27 -0
- natquery-1.0.0/src/natquery/schema/__init__.py +0 -0
- natquery-1.0.0/src/natquery/schema/extractor.py +80 -0
- natquery-1.0.0/src/natquery/schema/formatter.py +52 -0
- natquery-1.0.0/src/natquery/security/__init__.py +0 -0
- natquery-1.0.0/src/natquery/security/validator.py +20 -0
- natquery-1.0.0/src/natquery.egg-info/PKG-INFO +147 -0
- natquery-1.0.0/src/natquery.egg-info/SOURCES.txt +42 -0
- natquery-1.0.0/src/natquery.egg-info/dependency_links.txt +1 -0
- natquery-1.0.0/src/natquery.egg-info/entry_points.txt +2 -0
- natquery-1.0.0/src/natquery.egg-info/requires.txt +11 -0
- natquery-1.0.0/src/natquery.egg-info/top_level.txt +1 -0
natquery-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tanmai Raghava
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
natquery-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: natquery
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Interact with PostgreSQL databases using natural language via a secure, performance-aware CLI
|
|
5
|
+
Author-email: Tanmai Raghava <tanmairaghava@kgpian.iitkgp.ac.in>, Krishna Chaitanya <krish89@kgpian.iitkgp.ac.in>, Thania Joshi <thaniavjoshi@kgpian.iitkgp.ac.in>, Lokeswara Reddy <lokeswarareddy2006@gmail.com>, Manasa Vankam <vankammanasa45@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/N-T-Raghava/NatQuery
|
|
8
|
+
Project-URL: Documentation, https://github.com/N-T-Raghava/NatQuery#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/N-T-Raghava/NatQuery
|
|
10
|
+
Project-URL: Issues, https://github.com/N-T-Raghava/NatQuery/issues
|
|
11
|
+
Keywords: nlp,database,postgres,natural-language,cli,llm
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: System Administrators
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Natural Language :: English
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Database
|
|
24
|
+
Classifier: Topic :: Utilities
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: psycopg2-binary>=2.9.0
|
|
29
|
+
Requires-Dist: groq>=0.4.0
|
|
30
|
+
Requires-Dist: rich>=13.0.0
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: black==25.1.0; extra == "dev"
|
|
35
|
+
Requires-Dist: ruff==0.15.6; extra == "dev"
|
|
36
|
+
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# NatQuery
|
|
41
|
+
natquery is a Python package that enables users to interact with relational databases using natural language via a secure, performance-aware command-line interface along with database driven machine learning analytics.
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
natquery/
|
|
45
|
+
│
|
|
46
|
+
├── pyproject.toml
|
|
47
|
+
├── README.md
|
|
48
|
+
├── requirements.txt
|
|
49
|
+
│── src/
|
|
50
|
+
├ ├── natquery/
|
|
51
|
+
│ ├── __init__.py
|
|
52
|
+
│
|
|
53
|
+
│ # =========================
|
|
54
|
+
│ # 1. CLI LAYER
|
|
55
|
+
│ # =========================
|
|
56
|
+
│ ├── cli/
|
|
57
|
+
│ │ ├── __init__.py
|
|
58
|
+
│ │ ├── main.py # Entry point
|
|
59
|
+
│ │ ├── shell.py # Interactive REPL
|
|
60
|
+
│ │ ├── commands.py # CLI command registry
|
|
61
|
+
│
|
|
62
|
+
│ # =========================
|
|
63
|
+
│ # 2. CONFIG LAYER
|
|
64
|
+
│ # =========================
|
|
65
|
+
│ ├── config/
|
|
66
|
+
│ │ ├── __init__.py
|
|
67
|
+
│ │ ├── settings.py # API keys, DB config
|
|
68
|
+
│ │ ├── connection.py # PostgreSQL connection manager
|
|
69
|
+
│
|
|
70
|
+
│ # =========================
|
|
71
|
+
│ # 3. ORCHESTRATION LAYER
|
|
72
|
+
│ # =========================
|
|
73
|
+
│ ├── orchestration/
|
|
74
|
+
│ │ ├── __init__.py
|
|
75
|
+
│ │ ├── pipeline.py # End-to-end coordinator
|
|
76
|
+
│ │ ├── error_handler.py
|
|
77
|
+
│
|
|
78
|
+
│ # =========================
|
|
79
|
+
│ # 4. SCHEMA MODULE
|
|
80
|
+
│ # =========================
|
|
81
|
+
│ ├── schema/
|
|
82
|
+
│ │ ├── __init__.py
|
|
83
|
+
│ │ ├── extractor.py # Dynamic schema extraction
|
|
84
|
+
│ │ ├── formatter.py # Convert schema to prompt-friendly text
|
|
85
|
+
│
|
|
86
|
+
│ # =========================
|
|
87
|
+
│ # 5. PROMPT BUILDER
|
|
88
|
+
│ # =========================
|
|
89
|
+
│ ├── prompt/
|
|
90
|
+
│ │ ├── __init__.py
|
|
91
|
+
│ │ ├── builder.py # Structured prompt construction
|
|
92
|
+
│
|
|
93
|
+
│ # =========================
|
|
94
|
+
│ # 6. LLM CLIENT
|
|
95
|
+
│ # =========================
|
|
96
|
+
│ ├── llm/
|
|
97
|
+
│ │ ├── __init__.py
|
|
98
|
+
│ │ ├── client.py # LLM API calls
|
|
99
|
+
│ │ ├── self_corrector.py # Retry & correction mechanism
|
|
100
|
+
│
|
|
101
|
+
│ # =========================
|
|
102
|
+
│ # 7. SECURITY + VALIDATION
|
|
103
|
+
│ # =========================
|
|
104
|
+
│ ├── security/
|
|
105
|
+
│ │ ├── __init__.py
|
|
106
|
+
│ │ ├── validator.py # SELECT-only enforcement
|
|
107
|
+
│ │ ├── limiter.py # LIMIT enforcement
|
|
108
|
+
│
|
|
109
|
+
│ # =========================
|
|
110
|
+
│ # 8. EXECUTION ENGINE
|
|
111
|
+
│ # =========================
|
|
112
|
+
│ ├── execution/
|
|
113
|
+
│ │ ├── __init__.py
|
|
114
|
+
│ │ ├── engine.py # SQL execution
|
|
115
|
+
│ │ ├── explain.py # EXPLAIN / ANALYZE
|
|
116
|
+
│
|
|
117
|
+
│ # =========================
|
|
118
|
+
│ # 9. MACHINE LEARNING
|
|
119
|
+
│ # =========================
|
|
120
|
+
│ ├── ml/
|
|
121
|
+
│ │ ├── __init__.py
|
|
122
|
+
│ │ ├── preprocessing.py
|
|
123
|
+
│ │ ├── trainer.py
|
|
124
|
+
│ │ ├── inference.py
|
|
125
|
+
│ │
|
|
126
|
+
│ # =========================
|
|
127
|
+
│ # 10. PERFORMANCE + LOGGING
|
|
128
|
+
│ # =========================
|
|
129
|
+
│ ├── observability/
|
|
130
|
+
│ ├── __init__.py
|
|
131
|
+
│ ├── logger.py
|
|
132
|
+
│ ├── cost_analyzer.py
|
|
133
|
+
│ ├── index_recommender.py
|
|
134
|
+
│
|
|
135
|
+
│
|
|
136
|
+
├── database/
|
|
137
|
+
│ ├── schema.sql
|
|
138
|
+
│ ├── seed.py
|
|
139
|
+
│ ├── synthetic_data.py
|
|
140
|
+
│
|
|
141
|
+
├── tests/
|
|
142
|
+
│ ├── test_pipeline.py
|
|
143
|
+
│ ├── test_security.py
|
|
144
|
+
│ ├── test_ml.py
|
|
145
|
+
│
|
|
146
|
+
└── benchmarks/
|
|
147
|
+
├── nl_queries.json
|
natquery-1.0.0/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# NatQuery
|
|
2
|
+
natquery is a Python package that enables users to interact with relational databases using natural language via a secure, performance-aware command-line interface along with database driven machine learning analytics.
|
|
3
|
+
|
|
4
|
+
```
|
|
5
|
+
natquery/
|
|
6
|
+
│
|
|
7
|
+
├── pyproject.toml
|
|
8
|
+
├── README.md
|
|
9
|
+
├── requirements.txt
|
|
10
|
+
│── src/
|
|
11
|
+
├ ├── natquery/
|
|
12
|
+
│ ├── __init__.py
|
|
13
|
+
│
|
|
14
|
+
│ # =========================
|
|
15
|
+
│ # 1. CLI LAYER
|
|
16
|
+
│ # =========================
|
|
17
|
+
│ ├── cli/
|
|
18
|
+
│ │ ├── __init__.py
|
|
19
|
+
│ │ ├── main.py # Entry point
|
|
20
|
+
│ │ ├── shell.py # Interactive REPL
|
|
21
|
+
│ │ ├── commands.py # CLI command registry
|
|
22
|
+
│
|
|
23
|
+
│ # =========================
|
|
24
|
+
│ # 2. CONFIG LAYER
|
|
25
|
+
│ # =========================
|
|
26
|
+
│ ├── config/
|
|
27
|
+
│ │ ├── __init__.py
|
|
28
|
+
│ │ ├── settings.py # API keys, DB config
|
|
29
|
+
│ │ ├── connection.py # PostgreSQL connection manager
|
|
30
|
+
│
|
|
31
|
+
│ # =========================
|
|
32
|
+
│ # 3. ORCHESTRATION LAYER
|
|
33
|
+
│ # =========================
|
|
34
|
+
│ ├── orchestration/
|
|
35
|
+
│ │ ├── __init__.py
|
|
36
|
+
│ │ ├── pipeline.py # End-to-end coordinator
|
|
37
|
+
│ │ ├── error_handler.py
|
|
38
|
+
│
|
|
39
|
+
│ # =========================
|
|
40
|
+
│ # 4. SCHEMA MODULE
|
|
41
|
+
│ # =========================
|
|
42
|
+
│ ├── schema/
|
|
43
|
+
│ │ ├── __init__.py
|
|
44
|
+
│ │ ├── extractor.py # Dynamic schema extraction
|
|
45
|
+
│ │ ├── formatter.py # Convert schema to prompt-friendly text
|
|
46
|
+
│
|
|
47
|
+
│ # =========================
|
|
48
|
+
│ # 5. PROMPT BUILDER
|
|
49
|
+
│ # =========================
|
|
50
|
+
│ ├── prompt/
|
|
51
|
+
│ │ ├── __init__.py
|
|
52
|
+
│ │ ├── builder.py # Structured prompt construction
|
|
53
|
+
│
|
|
54
|
+
│ # =========================
|
|
55
|
+
│ # 6. LLM CLIENT
|
|
56
|
+
│ # =========================
|
|
57
|
+
│ ├── llm/
|
|
58
|
+
│ │ ├── __init__.py
|
|
59
|
+
│ │ ├── client.py # LLM API calls
|
|
60
|
+
│ │ ├── self_corrector.py # Retry & correction mechanism
|
|
61
|
+
│
|
|
62
|
+
│ # =========================
|
|
63
|
+
│ # 7. SECURITY + VALIDATION
|
|
64
|
+
│ # =========================
|
|
65
|
+
│ ├── security/
|
|
66
|
+
│ │ ├── __init__.py
|
|
67
|
+
│ │ ├── validator.py # SELECT-only enforcement
|
|
68
|
+
│ │ ├── limiter.py # LIMIT enforcement
|
|
69
|
+
│
|
|
70
|
+
│ # =========================
|
|
71
|
+
│ # 8. EXECUTION ENGINE
|
|
72
|
+
│ # =========================
|
|
73
|
+
│ ├── execution/
|
|
74
|
+
│ │ ├── __init__.py
|
|
75
|
+
│ │ ├── engine.py # SQL execution
|
|
76
|
+
│ │ ├── explain.py # EXPLAIN / ANALYZE
|
|
77
|
+
│
|
|
78
|
+
│ # =========================
|
|
79
|
+
│ # 9. MACHINE LEARNING
|
|
80
|
+
│ # =========================
|
|
81
|
+
│ ├── ml/
|
|
82
|
+
│ │ ├── __init__.py
|
|
83
|
+
│ │ ├── preprocessing.py
|
|
84
|
+
│ │ ├── trainer.py
|
|
85
|
+
│ │ ├── inference.py
|
|
86
|
+
│ │
|
|
87
|
+
│ # =========================
|
|
88
|
+
│ # 10. PERFORMANCE + LOGGING
|
|
89
|
+
│ # =========================
|
|
90
|
+
│ ├── observability/
|
|
91
|
+
│ ├── __init__.py
|
|
92
|
+
│ ├── logger.py
|
|
93
|
+
│ ├── cost_analyzer.py
|
|
94
|
+
│ ├── index_recommender.py
|
|
95
|
+
│
|
|
96
|
+
│
|
|
97
|
+
├── database/
|
|
98
|
+
│ ├── schema.sql
|
|
99
|
+
│ ├── seed.py
|
|
100
|
+
│ ├── synthetic_data.py
|
|
101
|
+
│
|
|
102
|
+
├── tests/
|
|
103
|
+
│ ├── test_pipeline.py
|
|
104
|
+
│ ├── test_security.py
|
|
105
|
+
│ ├── test_ml.py
|
|
106
|
+
│
|
|
107
|
+
└── benchmarks/
|
|
108
|
+
├── nl_queries.json
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "natquery"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Interact with PostgreSQL databases using natural language via a secure, performance-aware CLI"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Tanmai Raghava", email = "tanmairaghava@kgpian.iitkgp.ac.in"},
|
|
14
|
+
{name = "Krishna Chaitanya", email = "krish89@kgpian.iitkgp.ac.in"},
|
|
15
|
+
{name = "Thania Joshi", email = "thaniavjoshi@kgpian.iitkgp.ac.in"},
|
|
16
|
+
{name = "Lokeswara Reddy", email = "lokeswarareddy2006@gmail.com"},
|
|
17
|
+
{name = "Manasa Vankam", email = "vankammanasa45@gmail.com"}
|
|
18
|
+
]
|
|
19
|
+
keywords = ["nlp", "database", "postgres", "natural-language", "cli", "llm"]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 4 - Beta",
|
|
22
|
+
"Environment :: Console",
|
|
23
|
+
"Intended Audience :: Developers",
|
|
24
|
+
"Intended Audience :: System Administrators",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Natural Language :: English",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3.10",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Topic :: Database",
|
|
33
|
+
"Topic :: Utilities",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
dependencies = [
|
|
37
|
+
"psycopg2-binary>=2.9.0",
|
|
38
|
+
"groq>=0.4.0",
|
|
39
|
+
"rich>=13.0.0"
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://github.com/N-T-Raghava/NatQuery"
|
|
44
|
+
Documentation = "https://github.com/N-T-Raghava/NatQuery#readme"
|
|
45
|
+
Repository = "https://github.com/N-T-Raghava/NatQuery"
|
|
46
|
+
Issues = "https://github.com/N-T-Raghava/NatQuery/issues"
|
|
47
|
+
|
|
48
|
+
[project.optional-dependencies]
|
|
49
|
+
dev = [
|
|
50
|
+
"pytest>=8.0.0",
|
|
51
|
+
"pytest-cov>=4.0.0",
|
|
52
|
+
"black==25.1.0",
|
|
53
|
+
"ruff==0.15.6",
|
|
54
|
+
"build>=1.0.0",
|
|
55
|
+
"twine>=4.0.0"
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[tool.setuptools.packages.find]
|
|
59
|
+
where = ["src"]
|
|
60
|
+
|
|
61
|
+
[project.scripts]
|
|
62
|
+
natquery = "natquery.cli.main:main"
|
|
63
|
+
|
|
64
|
+
[tool.pytest.ini_options]
|
|
65
|
+
markers = [
|
|
66
|
+
"integration: marks integration tests"
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
[tool.black]
|
|
70
|
+
line-length = 88
|
|
71
|
+
target-version = ["py310"]
|
|
72
|
+
|
|
73
|
+
[tool.ruff]
|
|
74
|
+
line-length = 88
|
|
75
|
+
target-version = "py310"
|
natquery-1.0.0/setup.cfg
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from getpass import getpass
|
|
4
|
+
from urllib.parse import urlparse
|
|
5
|
+
from natquery.config.settings import Settings
|
|
6
|
+
from natquery.observability.performance_api import (
|
|
7
|
+
get_last_run,
|
|
8
|
+
compare_last_two_runs,
|
|
9
|
+
get_slowest_queries,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
BASE_DIR = Path.home() / ".natquery"
|
|
14
|
+
CONFIG_FILE = BASE_DIR / "config.json"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def extract_db_name_from_dsn(dsn: str) -> str:
|
|
18
|
+
"""
|
|
19
|
+
Extract database name from a PostgreSQL DSN.
|
|
20
|
+
Supports both URL format (postgresql://) and key-value format (dbname=).
|
|
21
|
+
"""
|
|
22
|
+
parsed = urlparse(dsn)
|
|
23
|
+
|
|
24
|
+
if parsed.scheme: # URL format
|
|
25
|
+
db_name = parsed.path.lstrip("/").split("?")[
|
|
26
|
+
0
|
|
27
|
+
] # Remove leading / and query params
|
|
28
|
+
return db_name if db_name else "unknown_db"
|
|
29
|
+
|
|
30
|
+
# Fallback: key-value DSN (e.g., "dbname=mydb user=postgres host=localhost")
|
|
31
|
+
parts = dsn.split()
|
|
32
|
+
for part in parts:
|
|
33
|
+
if part.startswith("dbname="):
|
|
34
|
+
return part.split("=", 1)[1]
|
|
35
|
+
|
|
36
|
+
return "unknown_db"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def connect_command(dsn_arg=None):
|
|
40
|
+
"""
|
|
41
|
+
Setup for NatQuery.
|
|
42
|
+
If dsn_arg is provided, extracts DB name and uses it as workspace.
|
|
43
|
+
Otherwise, runs interactive setup for standard or DSN connections.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
print("============ NatQuery Setup ============")
|
|
47
|
+
|
|
48
|
+
BASE_DIR.mkdir(exist_ok=True)
|
|
49
|
+
|
|
50
|
+
# If DSN was provided as argument
|
|
51
|
+
if dsn_arg:
|
|
52
|
+
db_name = extract_db_name_from_dsn(dsn_arg)
|
|
53
|
+
workspace_name = db_name
|
|
54
|
+
|
|
55
|
+
print(f"Your Datbase Name: {db_name}")
|
|
56
|
+
print(f"Workspace Name: {workspace_name}")
|
|
57
|
+
|
|
58
|
+
config = {
|
|
59
|
+
"connection_type": "dsn",
|
|
60
|
+
"db_dsn": dsn_arg,
|
|
61
|
+
"db_name": db_name,
|
|
62
|
+
"workspace_name": workspace_name,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
else:
|
|
66
|
+
db_name = input("Database Name: ").strip()
|
|
67
|
+
workspace_name = db_name
|
|
68
|
+
|
|
69
|
+
db_dir = BASE_DIR / workspace_name
|
|
70
|
+
db_dir.mkdir(parents=True, exist_ok=True)
|
|
71
|
+
|
|
72
|
+
ssl_input = input("Use SSL? (yes/no): ").strip().lower()
|
|
73
|
+
use_ssl = ssl_input in {"yes", "y"}
|
|
74
|
+
|
|
75
|
+
config = {
|
|
76
|
+
"connection_type": "standard",
|
|
77
|
+
"db_host": input("DB Host (e.g. 127.0.0.1): ").strip(),
|
|
78
|
+
"db_port": input("DB Port (e.g. 5432): ").strip(),
|
|
79
|
+
"db_name": db_name,
|
|
80
|
+
"db_user": input("DB User: ").strip(),
|
|
81
|
+
"db_password": getpass("DB Password: "),
|
|
82
|
+
"workspace_name": workspace_name,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if use_ssl:
|
|
86
|
+
config["sslmode"] = "require"
|
|
87
|
+
|
|
88
|
+
db_dir = BASE_DIR / workspace_name
|
|
89
|
+
db_dir.mkdir(parents=True, exist_ok=True)
|
|
90
|
+
|
|
91
|
+
# LLM config (common to both)
|
|
92
|
+
config.update(
|
|
93
|
+
{
|
|
94
|
+
"llm_provider": input("LLM Provider (groq): ").strip(),
|
|
95
|
+
"llm_api_key": getpass("LLM API Key: "),
|
|
96
|
+
"llm_model": input("LLM Model (e.g. llama-3.1-70b-versatile): ").strip(),
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
config_file = db_dir / "config.json"
|
|
101
|
+
|
|
102
|
+
with open(config_file, "w") as f:
|
|
103
|
+
json.dump(config, f, indent=2)
|
|
104
|
+
|
|
105
|
+
# Set active database
|
|
106
|
+
(BASE_DIR / "current_db").write_text(workspace_name)
|
|
107
|
+
|
|
108
|
+
print(f"\nConfiguration saved for workspace: {workspace_name}")
|
|
109
|
+
print("You can now run: natquery\n")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def reset_command():
|
|
113
|
+
"""
|
|
114
|
+
Deletes the active database configuration.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
config_path = Settings._get_config_path()
|
|
119
|
+
except RuntimeError:
|
|
120
|
+
print("No active database configured.")
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
if config_path.exists():
|
|
124
|
+
config_path.unlink()
|
|
125
|
+
print("Configuration removed for active database.")
|
|
126
|
+
else:
|
|
127
|
+
print("No configuration found.")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def show_config_command():
|
|
131
|
+
"""
|
|
132
|
+
Display current active DB configuration (without secrets).
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
config = Settings.load_config()
|
|
137
|
+
except RuntimeError:
|
|
138
|
+
print("NatQuery not configured.")
|
|
139
|
+
print("Run: natquery connect")
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
safe_config = config.copy()
|
|
143
|
+
|
|
144
|
+
if "db_password" in safe_config:
|
|
145
|
+
safe_config["db_password"] = "********"
|
|
146
|
+
|
|
147
|
+
if "llm_api_key" in safe_config:
|
|
148
|
+
safe_config["llm_api_key"] = "********"
|
|
149
|
+
|
|
150
|
+
print(json.dumps(safe_config, indent=2))
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def select_database_command():
|
|
154
|
+
"""
|
|
155
|
+
List all configured databases and allow user to select one as the active database.
|
|
156
|
+
Updates the current_db file without requiring reconnection.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
BASE_DIR.mkdir(exist_ok=True)
|
|
160
|
+
|
|
161
|
+
# Find all database directories (those containing config.json)
|
|
162
|
+
databases = []
|
|
163
|
+
|
|
164
|
+
if BASE_DIR.exists():
|
|
165
|
+
for item in BASE_DIR.iterdir():
|
|
166
|
+
if item.is_dir() and (item / "config.json").exists():
|
|
167
|
+
databases.append(item.name)
|
|
168
|
+
|
|
169
|
+
if not databases:
|
|
170
|
+
print("No configured databases found.")
|
|
171
|
+
print("Run: natquery connect")
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
# Get current database
|
|
175
|
+
current_db_file = BASE_DIR / "current_db"
|
|
176
|
+
current_db = (
|
|
177
|
+
current_db_file.read_text().strip() if current_db_file.exists() else None
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Display databases
|
|
181
|
+
print("\n========== Configured Databases ==========")
|
|
182
|
+
for i, db in enumerate(databases, 1):
|
|
183
|
+
marker = " (current)" if db == current_db else ""
|
|
184
|
+
print(f"{i}. {db}{marker}")
|
|
185
|
+
|
|
186
|
+
print("\n")
|
|
187
|
+
|
|
188
|
+
# Get user selection
|
|
189
|
+
while True:
|
|
190
|
+
try:
|
|
191
|
+
selection = input("Select a database (enter number): ").strip()
|
|
192
|
+
index = int(selection) - 1
|
|
193
|
+
|
|
194
|
+
if 0 <= index < len(databases):
|
|
195
|
+
selected_db = databases[index]
|
|
196
|
+
break
|
|
197
|
+
else:
|
|
198
|
+
print(
|
|
199
|
+
f"Invalid selection. Please enter a number between 1 and {len(databases)}."
|
|
200
|
+
)
|
|
201
|
+
except ValueError:
|
|
202
|
+
print("Invalid input. Please enter a number.")
|
|
203
|
+
|
|
204
|
+
# Update current database
|
|
205
|
+
current_db_file.write_text(selected_db)
|
|
206
|
+
print(f"\nActive database switched to: {selected_db}")
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def performance_summary_command():
|
|
210
|
+
db_name = Settings.get_db_config()["dbname"]
|
|
211
|
+
|
|
212
|
+
last = get_last_run(db_name)
|
|
213
|
+
|
|
214
|
+
if not last:
|
|
215
|
+
print("No performance data available.")
|
|
216
|
+
return
|
|
217
|
+
|
|
218
|
+
print("\n====== Performance Summary ======")
|
|
219
|
+
print(f"Execution Time: {last['execution_time_ms']} ms")
|
|
220
|
+
print(f"Total Cost: {last['total_cost']}")
|
|
221
|
+
print(f"Planning Time: {last['planning_time_ms']} ms")
|
|
222
|
+
|
|
223
|
+
print("\n--- Suggestions ---")
|
|
224
|
+
for s in last["suggestions"]:
|
|
225
|
+
print(f"- {s}")
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def performance_compare_command():
|
|
229
|
+
db_name = Settings.get_db_config()["dbname"]
|
|
230
|
+
|
|
231
|
+
comp = compare_last_two_runs(db_name)
|
|
232
|
+
|
|
233
|
+
if not comp:
|
|
234
|
+
print("Oops! Not enough data to compare.")
|
|
235
|
+
return
|
|
236
|
+
|
|
237
|
+
print("\n====== Performance Comparison ======")
|
|
238
|
+
|
|
239
|
+
print(f"Execution Time Change: {comp['execution_time_diff']} ms")
|
|
240
|
+
print(f"Cost Change: {comp['cost_diff']}")
|
|
241
|
+
|
|
242
|
+
if comp["execution_time_diff"] < 0:
|
|
243
|
+
print("Booyah! Query improved!")
|
|
244
|
+
else:
|
|
245
|
+
print("Oops! Query got slower.")
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def performance_slowest_command():
|
|
249
|
+
db_name = Settings.get_db_config()["dbname"]
|
|
250
|
+
|
|
251
|
+
slow = get_slowest_queries(db_name)
|
|
252
|
+
|
|
253
|
+
print("\n====== Slowest Queries ======")
|
|
254
|
+
|
|
255
|
+
for i, q in enumerate(slow, 1):
|
|
256
|
+
print(f"{i}. {q['execution_time_ms']} ms → {q['sql']}")
|