gaard-connectors 0.1.0__py3-none-any.whl
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.
- gaard_connectors/__init__.py +0 -0
- gaard_connectors/mssql/__init__.py +0 -0
- gaard_connectors/mysql/__init__.py +0 -0
- gaard_connectors/oracle/__init__.py +0 -0
- gaard_connectors/postgres/__init__.py +0 -0
- gaard_connectors/sqlalchemy/__init__.py +0 -0
- gaard_connectors/sqlalchemy/executor.py +51 -0
- gaard_connectors/sqlalchemy/introspector.py +62 -0
- gaard_connectors/sqlite/__init__.py +0 -0
- gaard_connectors-0.1.0.dist-info/METADATA +31 -0
- gaard_connectors-0.1.0.dist-info/RECORD +13 -0
- gaard_connectors-0.1.0.dist-info/WHEEL +5 -0
- gaard_connectors-0.1.0.dist-info/top_level.txt +1 -0
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from collections.abc import Mapping
|
|
3
|
+
from typing import Any, cast
|
|
4
|
+
|
|
5
|
+
from sqlalchemy import create_engine, text
|
|
6
|
+
from sqlalchemy.engine import Engine, RowMapping
|
|
7
|
+
from sqlalchemy.exc import SQLAlchemyError
|
|
8
|
+
|
|
9
|
+
from gaard_core.errors import QueryExecutionError
|
|
10
|
+
from gaard_core.json_utils import to_jsonable
|
|
11
|
+
from gaard_core.query_pipeline.models import QueryResult
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SQLAlchemyQueryExecutor:
|
|
15
|
+
def __init__(self, database_url: str, max_rows: int = 100) -> None:
|
|
16
|
+
self.database_url = database_url
|
|
17
|
+
self.max_rows = max_rows
|
|
18
|
+
self.engine: Engine = create_engine(database_url)
|
|
19
|
+
|
|
20
|
+
def execute(self, sql: str) -> QueryResult:
|
|
21
|
+
limited_sql = self._apply_limit(sql)
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
with self.engine.connect() as connection:
|
|
25
|
+
result = connection.execute(text(limited_sql))
|
|
26
|
+
rows = result.mappings().fetchall()
|
|
27
|
+
except SQLAlchemyError as exc:
|
|
28
|
+
raise QueryExecutionError(
|
|
29
|
+
f"Query execution failed. SQL: {limited_sql}. Error: {exc}",
|
|
30
|
+
sql=limited_sql,
|
|
31
|
+
error_detail=str(exc),
|
|
32
|
+
) from exc
|
|
33
|
+
|
|
34
|
+
normalized_rows = [self._normalize_row(row) for row in rows]
|
|
35
|
+
columns = list(normalized_rows[0].keys()) if normalized_rows else []
|
|
36
|
+
|
|
37
|
+
return QueryResult(
|
|
38
|
+
columns=columns,
|
|
39
|
+
rows=normalized_rows,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def _apply_limit(self, sql: str) -> str:
|
|
43
|
+
normalized = sql.strip().rstrip(";")
|
|
44
|
+
|
|
45
|
+
if re.search(r"\blimit\s+\d+\b", normalized, flags=re.IGNORECASE):
|
|
46
|
+
return normalized
|
|
47
|
+
|
|
48
|
+
return f"{normalized} LIMIT {self.max_rows}"
|
|
49
|
+
|
|
50
|
+
def _normalize_row(self, row: Mapping[str, Any] | RowMapping) -> dict[str, Any]:
|
|
51
|
+
return cast(dict[str, Any], to_jsonable(dict(row)))
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import create_engine, inspect
|
|
4
|
+
from sqlalchemy.engine import Engine
|
|
5
|
+
from sqlalchemy.engine.reflection import Inspector
|
|
6
|
+
|
|
7
|
+
from gaard_core.schema.models import ColumnInfo, DatabaseSchema, ForeignKeyInfo, TableInfo
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SQLAlchemySchemaIntrospector:
|
|
11
|
+
def __init__(self, database_url: str) -> None:
|
|
12
|
+
self.database_url = database_url
|
|
13
|
+
self.engine: Engine = create_engine(database_url)
|
|
14
|
+
|
|
15
|
+
def introspect(self) -> DatabaseSchema:
|
|
16
|
+
inspector = inspect(self.engine)
|
|
17
|
+
|
|
18
|
+
tables: list[TableInfo] = []
|
|
19
|
+
|
|
20
|
+
for table_name in inspector.get_table_names():
|
|
21
|
+
tables.append(self._introspect_table(inspector, table_name, "table"))
|
|
22
|
+
|
|
23
|
+
for view_name in inspector.get_view_names():
|
|
24
|
+
tables.append(self._introspect_table(inspector, view_name, "view"))
|
|
25
|
+
|
|
26
|
+
return DatabaseSchema(tables=tables)
|
|
27
|
+
|
|
28
|
+
def _introspect_table(
|
|
29
|
+
self,
|
|
30
|
+
inspector: Inspector,
|
|
31
|
+
name: str,
|
|
32
|
+
object_type: Literal["table", "view"],
|
|
33
|
+
) -> TableInfo:
|
|
34
|
+
columns = [
|
|
35
|
+
ColumnInfo(
|
|
36
|
+
name=column["name"],
|
|
37
|
+
type=str(column["type"]),
|
|
38
|
+
nullable=bool(column.get("nullable", True)),
|
|
39
|
+
primary_key=bool(column.get("primary_key", False)),
|
|
40
|
+
)
|
|
41
|
+
for column in inspector.get_columns(name)
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
foreign_keys = []
|
|
45
|
+
|
|
46
|
+
if object_type == "table":
|
|
47
|
+
foreign_keys = [
|
|
48
|
+
ForeignKeyInfo(
|
|
49
|
+
constrained_columns=list(foreign_key.get("constrained_columns") or []),
|
|
50
|
+
referred_table=str(foreign_key.get("referred_table")),
|
|
51
|
+
referred_columns=list(foreign_key.get("referred_columns") or []),
|
|
52
|
+
)
|
|
53
|
+
for foreign_key in inspector.get_foreign_keys(name)
|
|
54
|
+
if foreign_key.get("referred_table")
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
return TableInfo(
|
|
58
|
+
name=name,
|
|
59
|
+
object_type=object_type,
|
|
60
|
+
columns=columns,
|
|
61
|
+
foreign_keys=foreign_keys,
|
|
62
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gaard-connectors
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Database connectors for GAARD based on SQLAlchemy
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: gaard-core==0.1.0
|
|
8
|
+
Requires-Dist: sqlalchemy>=2.0.0
|
|
9
|
+
Provides-Extra: postgres
|
|
10
|
+
Requires-Dist: psycopg[binary]>=3.2.0; extra == "postgres"
|
|
11
|
+
Provides-Extra: mysql
|
|
12
|
+
Requires-Dist: pymysql>=1.1.0; extra == "mysql"
|
|
13
|
+
Provides-Extra: mssql
|
|
14
|
+
Requires-Dist: pyodbc>=5.1.0; extra == "mssql"
|
|
15
|
+
Provides-Extra: oracle
|
|
16
|
+
Requires-Dist: oracledb>=2.0.0; extra == "oracle"
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
19
|
+
Requires-Dist: ruff>=0.5.0; extra == "dev"
|
|
20
|
+
Requires-Dist: mypy>=1.10.0; extra == "dev"
|
|
21
|
+
|
|
22
|
+
# GAARD - Governed AI Access to Relational Data
|
|
23
|
+
|
|
24
|
+
GAARD is a self-hosted AI SQL Gateway for governed natural-language access to relational data.
|
|
25
|
+
|
|
26
|
+
GAARD allows applications and users to ask questions about relational databases using natural language while keeping SQL generation, validation, execution, prompts, connectors, and auditability under control.
|
|
27
|
+
|
|
28
|
+
For more informacion see https://github.com/pkroliszewski/gaard
|
|
29
|
+
|
|
30
|
+
# This package
|
|
31
|
+
Package gaard-connectors extends gaard functionality by adding support for various database connection schemes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
gaard_connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
gaard_connectors/mssql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
gaard_connectors/mysql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
gaard_connectors/oracle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
gaard_connectors/postgres/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
gaard_connectors/sqlalchemy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
gaard_connectors/sqlalchemy/executor.py,sha256=CwAYDCk5nFk9CjcRZEGlpCyVidXqOH6BErawYQMPNow,1775
|
|
8
|
+
gaard_connectors/sqlalchemy/introspector.py,sha256=RDmxqKdCxXqs3gyQBNRaLJ6uqTk-BSx0hFSZnm2wKGw,2067
|
|
9
|
+
gaard_connectors/sqlite/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
gaard_connectors-0.1.0.dist-info/METADATA,sha256=A0uMaGaf__qt5tJCUrCAsHvzlP5nWLC6rC6npHdq8Vg,1244
|
|
11
|
+
gaard_connectors-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
12
|
+
gaard_connectors-0.1.0.dist-info/top_level.txt,sha256=kih-wrFnx5FwDucrAcPgxDJe5N4lZwYDoxnR9PhBA64,17
|
|
13
|
+
gaard_connectors-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gaard_connectors
|