altimate-engine 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.
Files changed (51) hide show
  1. altimate_engine/__init__.py +3 -0
  2. altimate_engine/__main__.py +5 -0
  3. altimate_engine/app/__init__.py +0 -0
  4. altimate_engine/ci/__init__.py +0 -0
  5. altimate_engine/ci/cost_gate.py +162 -0
  6. altimate_engine/connections.py +281 -0
  7. altimate_engine/connectors/__init__.py +21 -0
  8. altimate_engine/connectors/base.py +46 -0
  9. altimate_engine/connectors/bigquery.py +188 -0
  10. altimate_engine/connectors/databricks.py +200 -0
  11. altimate_engine/connectors/duckdb.py +91 -0
  12. altimate_engine/connectors/mysql.py +129 -0
  13. altimate_engine/connectors/postgres.py +109 -0
  14. altimate_engine/connectors/redshift.py +106 -0
  15. altimate_engine/connectors/snowflake.py +150 -0
  16. altimate_engine/connectors/sqlserver.py +201 -0
  17. altimate_engine/credential_store.py +123 -0
  18. altimate_engine/dbt/__init__.py +1 -0
  19. altimate_engine/dbt/lineage.py +168 -0
  20. altimate_engine/dbt/manifest.py +112 -0
  21. altimate_engine/dbt/profiles.py +164 -0
  22. altimate_engine/dbt/runner.py +68 -0
  23. altimate_engine/docker_discovery.py +118 -0
  24. altimate_engine/finops/__init__.py +0 -0
  25. altimate_engine/finops/credit_analyzer.py +346 -0
  26. altimate_engine/finops/query_history.py +218 -0
  27. altimate_engine/finops/role_access.py +255 -0
  28. altimate_engine/finops/unused_resources.py +226 -0
  29. altimate_engine/finops/warehouse_advisor.py +245 -0
  30. altimate_engine/local/__init__.py +1 -0
  31. altimate_engine/local/schema_sync.py +242 -0
  32. altimate_engine/local/test_local.py +74 -0
  33. altimate_engine/models.py +1082 -0
  34. altimate_engine/py.typed +0 -0
  35. altimate_engine/schema/__init__.py +1 -0
  36. altimate_engine/schema/cache.py +394 -0
  37. altimate_engine/schema/inspector.py +122 -0
  38. altimate_engine/schema/pii_detector.py +234 -0
  39. altimate_engine/schema/tags.py +151 -0
  40. altimate_engine/server.py +973 -0
  41. altimate_engine/sql/__init__.py +1 -0
  42. altimate_engine/sql/autocomplete.py +152 -0
  43. altimate_engine/sql/diff.py +63 -0
  44. altimate_engine/sql/executor.py +116 -0
  45. altimate_engine/sql/explainer.py +116 -0
  46. altimate_engine/sql/feedback_store.py +392 -0
  47. altimate_engine/sql/guard.py +657 -0
  48. altimate_engine/ssh_tunnel.py +108 -0
  49. altimate_engine-0.1.0.dist-info/METADATA +76 -0
  50. altimate_engine-0.1.0.dist-info/RECORD +51 -0
  51. altimate_engine-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,3 @@
1
+ """Altimate Engine — Python sidecar for the Altimate Code CLI."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,5 @@
1
+ """Entry point for `python -m altimate_engine`."""
2
+
3
+ from altimate_engine.server import main
4
+
5
+ main()
File without changes
File without changes
@@ -0,0 +1,162 @@
1
+ """CI cost gate — scan changed SQL files for critical issues.
2
+
3
+ Reads SQL files, runs lint analysis, and returns
4
+ pass/fail based on whether CRITICAL severity issues are found.
5
+
6
+ Skips:
7
+ - Jinja templates ({{ }}, {% %})
8
+ - Parse errors (likely Jinja or non-standard SQL)
9
+ - Non-SQL files
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import os
15
+ import re
16
+ from typing import Any
17
+
18
+ from altimate_engine.sql.guard import guard_lint
19
+
20
+
21
+ # Jinja pattern: {{ ... }} or {% ... %} or {# ... #}
22
+ _JINJA_PATTERN = re.compile(r"\{\{.*?\}\}|\{%.*?%\}|\{#.*?#\}", re.DOTALL)
23
+
24
+
25
+ def _has_jinja(sql: str) -> bool:
26
+ """Check if SQL contains Jinja template syntax."""
27
+ return bool(_JINJA_PATTERN.search(sql))
28
+
29
+
30
+ def _split_statements(sql: str) -> list[str]:
31
+ """Split SQL on semicolons, filtering empty statements."""
32
+ statements = []
33
+ for stmt in sql.split(";"):
34
+ stmt = stmt.strip()
35
+ if stmt:
36
+ statements.append(stmt)
37
+ return statements
38
+
39
+
40
+ def scan_files(
41
+ file_paths: list[str],
42
+ dialect: str = "snowflake",
43
+ ) -> dict[str, Any]:
44
+ """Scan SQL files for critical issues.
45
+
46
+ Args:
47
+ file_paths: List of SQL file paths to scan.
48
+ dialect: SQL dialect for analysis (default: snowflake).
49
+
50
+ Returns:
51
+ Dict with pass/fail status, per-file results, and summary.
52
+ """
53
+ file_results: list[dict[str, Any]] = []
54
+ total_issues = 0
55
+ critical_count = 0
56
+ files_scanned = 0
57
+ files_skipped = 0
58
+
59
+ for path in file_paths:
60
+ # Skip non-SQL files
61
+ if not path.endswith(".sql"):
62
+ files_skipped += 1
63
+ file_results.append({
64
+ "file": path,
65
+ "status": "skipped",
66
+ "reason": "not a SQL file",
67
+ "issues": [],
68
+ })
69
+ continue
70
+
71
+ # Read file
72
+ if not os.path.isfile(path):
73
+ files_skipped += 1
74
+ file_results.append({
75
+ "file": path,
76
+ "status": "skipped",
77
+ "reason": "file not found",
78
+ "issues": [],
79
+ })
80
+ continue
81
+
82
+ try:
83
+ with open(path, "r", encoding="utf-8") as f:
84
+ content = f.read()
85
+ except Exception as e:
86
+ files_skipped += 1
87
+ file_results.append({
88
+ "file": path,
89
+ "status": "skipped",
90
+ "reason": f"read error: {e}",
91
+ "issues": [],
92
+ })
93
+ continue
94
+
95
+ # Skip Jinja templates
96
+ if _has_jinja(content):
97
+ files_skipped += 1
98
+ file_results.append({
99
+ "file": path,
100
+ "status": "skipped",
101
+ "reason": "contains Jinja templates",
102
+ "issues": [],
103
+ })
104
+ continue
105
+
106
+ # Split and analyze each statement
107
+ statements = _split_statements(content)
108
+ if not statements:
109
+ files_skipped += 1
110
+ file_results.append({
111
+ "file": path,
112
+ "status": "skipped",
113
+ "reason": "empty file",
114
+ "issues": [],
115
+ })
116
+ continue
117
+
118
+ files_scanned += 1
119
+ file_issues: list[dict[str, Any]] = []
120
+
121
+ for stmt in statements:
122
+ # Run lint analysis
123
+ lint_result = guard_lint(stmt)
124
+ if lint_result.get("error"):
125
+ # Parse error — skip this statement (likely incomplete SQL)
126
+ continue
127
+
128
+ for finding in lint_result.get("findings", lint_result.get("issues", [])):
129
+ severity = finding.get("severity", "warning")
130
+ file_issues.append({
131
+ "type": finding.get("rule", finding.get("type", "UNKNOWN")),
132
+ "severity": severity,
133
+ "message": finding.get("message", ""),
134
+ "source": "lint",
135
+ })
136
+ total_issues += 1
137
+ if severity in ("error", "critical"):
138
+ critical_count += 1
139
+
140
+ status = "fail" if any(
141
+ i["severity"] in ("error", "critical") for i in file_issues
142
+ ) else "pass"
143
+
144
+ file_results.append({
145
+ "file": path,
146
+ "status": status,
147
+ "issues": file_issues,
148
+ })
149
+
150
+ passed = critical_count == 0
151
+
152
+ return {
153
+ "success": True,
154
+ "passed": passed,
155
+ "exit_code": 0 if passed else 1,
156
+ "files_scanned": files_scanned,
157
+ "files_skipped": files_skipped,
158
+ "total_issues": total_issues,
159
+ "critical_count": critical_count,
160
+ "file_results": file_results,
161
+ "error": None,
162
+ }
@@ -0,0 +1,281 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from altimate_engine.connectors.base import Connector
9
+ from altimate_engine.credential_store import resolve_config
10
+ from altimate_engine.ssh_tunnel import start, stop
11
+
12
+ SSH_FIELDS = {
13
+ "ssh_host",
14
+ "ssh_port",
15
+ "ssh_user",
16
+ "ssh_auth_type",
17
+ "ssh_key_path",
18
+ "ssh_password",
19
+ }
20
+
21
+
22
+ class ConnectionRegistry:
23
+ _connections: dict[str, dict[str, Any]] = {}
24
+ _loaded: bool = False
25
+
26
+ @classmethod
27
+ def load(cls) -> None:
28
+ if cls._loaded:
29
+ return
30
+
31
+ global_config = Path.home() / ".altimate-code" / "connections.json"
32
+ if global_config.exists():
33
+ with open(global_config) as f:
34
+ cls._connections.update(json.load(f))
35
+
36
+ project_config = Path.cwd() / ".altimate-code" / "connections.json"
37
+ if project_config.exists():
38
+ with open(project_config) as f:
39
+ cls._connections.update(json.load(f))
40
+
41
+ for key, value in os.environ.items():
42
+ if key.startswith("ALTIMATE_CODE_CONN_"):
43
+ name = key[len("ALTIMATE_CODE_CONN_") :].lower()
44
+ try:
45
+ cls._connections[name] = json.loads(value)
46
+ except json.JSONDecodeError:
47
+ pass
48
+
49
+ cls._loaded = True
50
+
51
+ @classmethod
52
+ def get(cls, name: str) -> Connector:
53
+ cls.load()
54
+
55
+ if name not in cls._connections:
56
+ raise ValueError(f"Connection '{name}' not found in registry")
57
+
58
+ config = dict(cls._connections[name])
59
+ config = resolve_config(name, config)
60
+
61
+ ssh_host = config.get("ssh_host")
62
+ if ssh_host:
63
+ if config.get("connection_string"):
64
+ raise ValueError(
65
+ "SSH tunneling requires explicit host/port — "
66
+ "cannot be used with connection_string"
67
+ )
68
+ ssh_config = {
69
+ k: config.pop(k) for k in list(config.keys()) if k in SSH_FIELDS
70
+ }
71
+ local_port = start(
72
+ name=name,
73
+ ssh_host=ssh_config.get("ssh_host", ""),
74
+ remote_host=config.get("host", "localhost"),
75
+ remote_port=config.get("port", 5432),
76
+ ssh_port=ssh_config.get("ssh_port", 22),
77
+ ssh_user=ssh_config.get("ssh_user"),
78
+ ssh_auth_type=ssh_config.get("ssh_auth_type", "key"),
79
+ ssh_key_path=ssh_config.get("ssh_key_path"),
80
+ ssh_password=ssh_config.get("ssh_password"),
81
+ )
82
+ config["host"] = "127.0.0.1"
83
+ config["port"] = local_port
84
+
85
+ dialect = config.get("type", "duckdb")
86
+
87
+ if dialect == "duckdb":
88
+ from altimate_engine.connectors.duckdb import DuckDBConnector
89
+
90
+ return DuckDBConnector(
91
+ path=config.get("path", ":memory:"),
92
+ **{k: v for k, v in config.items() if k not in ("type", "path")},
93
+ )
94
+ elif dialect == "postgres":
95
+ from altimate_engine.connectors.postgres import PostgresConnector
96
+
97
+ return PostgresConnector(
98
+ connection_string=config.get("connection_string", ""),
99
+ **{
100
+ k: v
101
+ for k, v in config.items()
102
+ if k not in ("type", "connection_string")
103
+ },
104
+ )
105
+ elif dialect == "snowflake":
106
+ from altimate_engine.connectors.snowflake import SnowflakeConnector
107
+
108
+ _snowflake_keys = {
109
+ "type",
110
+ "account",
111
+ "user",
112
+ "password",
113
+ "private_key_path",
114
+ "private_key_passphrase",
115
+ "warehouse",
116
+ "database",
117
+ "schema",
118
+ "role",
119
+ }
120
+ return SnowflakeConnector(
121
+ account=config.get("account", ""),
122
+ user=config.get("user", ""),
123
+ password=config.get("password"),
124
+ private_key_path=config.get("private_key_path"),
125
+ private_key_passphrase=config.get("private_key_passphrase"),
126
+ warehouse=config.get("warehouse"),
127
+ database=config.get("database"),
128
+ schema=config.get("schema"),
129
+ role=config.get("role"),
130
+ **{k: v for k, v in config.items() if k not in _snowflake_keys},
131
+ )
132
+ elif dialect == "bigquery":
133
+ from altimate_engine.connectors.bigquery import BigQueryConnector
134
+
135
+ _bigquery_keys = {"type", "project", "credentials_path", "location"}
136
+ return BigQueryConnector(
137
+ project=config.get("project", ""),
138
+ credentials_path=config.get("credentials_path"),
139
+ location=config.get("location", "US"),
140
+ **{k: v for k, v in config.items() if k not in _bigquery_keys},
141
+ )
142
+ elif dialect == "databricks":
143
+ from altimate_engine.connectors.databricks import DatabricksConnector
144
+
145
+ _databricks_keys = {
146
+ "type",
147
+ "server_hostname",
148
+ "http_path",
149
+ "access_token",
150
+ "catalog",
151
+ "schema",
152
+ }
153
+ return DatabricksConnector(
154
+ server_hostname=config.get("server_hostname", ""),
155
+ http_path=config.get("http_path", ""),
156
+ access_token=config.get("access_token"),
157
+ catalog=config.get("catalog"),
158
+ schema=config.get("schema"),
159
+ **{k: v for k, v in config.items() if k not in _databricks_keys},
160
+ )
161
+ elif dialect == "redshift":
162
+ from altimate_engine.connectors.redshift import RedshiftConnector
163
+
164
+ _redshift_keys = {
165
+ "type",
166
+ "host",
167
+ "port",
168
+ "database",
169
+ "user",
170
+ "password",
171
+ "connection_string",
172
+ "iam_role",
173
+ "region",
174
+ "cluster_identifier",
175
+ }
176
+ return RedshiftConnector(
177
+ host=config.get("host", ""),
178
+ port=config.get("port", 5439),
179
+ database=config.get("database", "dev"),
180
+ user=config.get("user"),
181
+ password=config.get("password"),
182
+ connection_string=config.get("connection_string"),
183
+ iam_role=config.get("iam_role"),
184
+ region=config.get("region"),
185
+ cluster_identifier=config.get("cluster_identifier"),
186
+ **{k: v for k, v in config.items() if k not in _redshift_keys},
187
+ )
188
+ elif dialect == "mysql":
189
+ from altimate_engine.connectors.mysql import MySQLConnector
190
+
191
+ _mysql_keys = {
192
+ "type",
193
+ "host",
194
+ "port",
195
+ "database",
196
+ "user",
197
+ "password",
198
+ "ssl_ca",
199
+ "ssl_cert",
200
+ "ssl_key",
201
+ }
202
+ return MySQLConnector(
203
+ host=config.get("host", "localhost"),
204
+ port=config.get("port", 3306),
205
+ database=config.get("database"),
206
+ user=config.get("user"),
207
+ password=config.get("password"),
208
+ ssl_ca=config.get("ssl_ca"),
209
+ ssl_cert=config.get("ssl_cert"),
210
+ ssl_key=config.get("ssl_key"),
211
+ **{k: v for k, v in config.items() if k not in _mysql_keys},
212
+ )
213
+ elif dialect == "sqlserver":
214
+ from altimate_engine.connectors.sqlserver import SQLServerConnector
215
+
216
+ _sqlserver_keys = {
217
+ "type",
218
+ "host",
219
+ "port",
220
+ "database",
221
+ "user",
222
+ "password",
223
+ "driver",
224
+ "azure_auth",
225
+ "trust_server_certificate",
226
+ }
227
+ return SQLServerConnector(
228
+ host=config.get("host", "localhost"),
229
+ port=config.get("port", 1433),
230
+ database=config.get("database"),
231
+ user=config.get("user"),
232
+ password=config.get("password"),
233
+ driver=config.get("driver", "ODBC Driver 18 for SQL Server"),
234
+ azure_auth=config.get("azure_auth", False),
235
+ trust_server_certificate=config.get("trust_server_certificate", False),
236
+ **{k: v for k, v in config.items() if k not in _sqlserver_keys},
237
+ )
238
+ else:
239
+ raise ValueError(f"Unsupported connector type: {dialect}")
240
+
241
+ @classmethod
242
+ def list(cls) -> list[dict[str, Any]]:
243
+ cls.load()
244
+ return [
245
+ {"name": name, "type": config.get("type", "unknown")}
246
+ for name, config in cls._connections.items()
247
+ ]
248
+
249
+ @classmethod
250
+ def test(cls, name: str) -> dict[str, Any]:
251
+ try:
252
+ connector = cls.get(name)
253
+ connector.connect()
254
+ connector.execute("SELECT 1")
255
+ connector.close()
256
+ return {"connected": True, "error": None}
257
+ except Exception as e:
258
+ return {"connected": False, "error": str(e)}
259
+ finally:
260
+ stop(name)
261
+
262
+ @classmethod
263
+ def add(cls, name: str, config: dict[str, Any]) -> dict[str, Any]:
264
+ from altimate_engine.credential_store import save_connection
265
+
266
+ result = save_connection(name, config)
267
+ cls._loaded = False
268
+ return result
269
+
270
+ @classmethod
271
+ def remove(cls, name: str) -> bool:
272
+ from altimate_engine.credential_store import remove_connection
273
+
274
+ result = remove_connection(name)
275
+ cls._loaded = False
276
+ return result
277
+
278
+ @classmethod
279
+ def reload(cls) -> None:
280
+ cls._loaded = False
281
+ cls._connections.clear()
@@ -0,0 +1,21 @@
1
+ from altimate_engine.connectors.base import Connector
2
+ from altimate_engine.connectors.duckdb import DuckDBConnector
3
+ from altimate_engine.connectors.postgres import PostgresConnector
4
+ from altimate_engine.connectors.snowflake import SnowflakeConnector
5
+ from altimate_engine.connectors.bigquery import BigQueryConnector
6
+ from altimate_engine.connectors.databricks import DatabricksConnector
7
+ from altimate_engine.connectors.redshift import RedshiftConnector
8
+ from altimate_engine.connectors.mysql import MySQLConnector
9
+ from altimate_engine.connectors.sqlserver import SQLServerConnector
10
+
11
+ __all__ = [
12
+ "Connector",
13
+ "DuckDBConnector",
14
+ "PostgresConnector",
15
+ "SnowflakeConnector",
16
+ "BigQueryConnector",
17
+ "DatabricksConnector",
18
+ "RedshiftConnector",
19
+ "MySQLConnector",
20
+ "SQLServerConnector",
21
+ ]
@@ -0,0 +1,46 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Any
5
+
6
+
7
+ class Connector(ABC):
8
+ @abstractmethod
9
+ def connect(self) -> Any:
10
+ pass
11
+
12
+ @abstractmethod
13
+ def execute(self, sql: str, params: tuple | list | None = None, limit: int = 1000) -> list[dict[str, Any]]:
14
+ pass
15
+
16
+ @abstractmethod
17
+ def list_schemas(self) -> list[str]:
18
+ pass
19
+
20
+ @abstractmethod
21
+ def list_tables(self, schema: str) -> list[dict[str, Any]]:
22
+ pass
23
+
24
+ @abstractmethod
25
+ def describe_table(self, schema: str, table: str) -> list[dict[str, Any]]:
26
+ pass
27
+
28
+ @abstractmethod
29
+ def close(self) -> None:
30
+ pass
31
+
32
+ def set_statement_timeout(self, timeout_ms: int) -> None:
33
+ """Set a per-session statement timeout. Override in subclasses that support it.
34
+
35
+ Args:
36
+ timeout_ms: Maximum query execution time in milliseconds.
37
+ """
38
+ pass
39
+
40
+ def __enter__(self):
41
+ self.connect()
42
+ return self
43
+
44
+ def __exit__(self, exc_type, exc_val, exc_tb):
45
+ self.close()
46
+ return False