Repository navigation
Add suporte a MySQL e postgree - #15
Merged
Petroncini merged 6 commits intoSep 29, 2026
Merged
Conversation
…le source of project dependencies
…the single source of project dependencies" This reverts commit a289a2f.
Adds native schema introspection (information_schema, no Java/SchemaCrawler required) and query execution (psycopg2/pymysql) for PostgreSQL and MySQL, routed via a new db_dialeto/db_config state field. The code-gen prompt now tells the LLM which dialect it's targeting instead of always assuming SQLite. SQLite behavior is unchanged (dialect defaults to sqlite when not specified).
Adds an optional db_url field (e.g. postgresql://user:pass@host:port/db) that, when set, drives schema introspection and query execution through SQLAlchemy instead of the manual db_dialeto/db_config path added earlier. The dialect is read from engine.dialect.name, so the caller only needs to provide one connection string instead of six separate flags/params. This is purely additive: when db_url is not set, behavior is unchanged (same db_dialeto/db_config/db_path path as before).
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Critical credential and query-safety issues remain, along with incorrect remote routing, dialect handling, and timeout/error behavior.
Review effort: Lite
Findings: 4
Open (10)
Database password exposed in shell history and process arguments · New MySQL SELECT checks allow file-writing and file-reading statements · New MySQL password exposed in SchemaCrawler process arguments · New Database password is persisted in graph state and checkpoints · New URL-based connections incorrectly default prompt dialect to SQLite · New URL executor ignores the configured query timeout · New DuckDB fallback incorrectly opens files with SQLite · New URL connection failures can crash the graph · New URL path skips virtual foreign-key inference · New Default SQLite dialect breaks DuckDB library calls · New
What changed in this PR
Adds PostgreSQL and MySQL support with schema introspection, query execution, dialect-aware prompts, CLI configuration, and SQLAlchemy URL support.
Changes:
- Adds database connection state and CLI options.
- Implements PostgreSQL/MySQL introspection and executors.
- Adds dialect-specific prompts and dependencies.
| File | Summary |
|---|---|
text_to_insight/state.py |
Adds database dialect and connection fields. |
text_to_insight/runtime.py |
Propagates database configuration. |
text_to_insight/nodes/schema.py |
Adds remote schema introspection. |
text_to_insight/nodes/sandbox.py |
Routes execution by database target. |
text_to_insight/nodes/code_agent/code_sql.py |
Adds PostgreSQL, MySQL, and URL execution. |
text_to_insight/nodes/code_agent/code_agent.py |
Adds dialect-aware prompt guidance. |
text_to_insight/InsightEngine.py |
Exposes database configuration. |
text_to_insight/cli.py |
Adds database connection arguments. |
pyproject.toml |
Adds database dependencies. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+88
to
+90
| "--db-password", | ||
| default=None, | ||
| help="Senha do banco remoto.", |
| with conn.cursor() as cur: | ||
| cur.execute("SET SESSION TRANSACTION READ ONLY") | ||
| cur.execute(f"SET SESSION max_execution_time = {int(timeout_segundos * 1000)}") | ||
| cur.execute(sql) |
Comment on lines
+53
to
+59
| "args": lambda _db, cfg: [ | ||
| "--server=mysql", | ||
| f"--host={cfg['host']}", | ||
| f"--port={cfg.get('port', 3306)}", | ||
| f"--database={cfg['database']}", | ||
| f"--user={cfg['user']}", | ||
| f"--password={cfg['password']}", |
| "db_path": db_path, | ||
| "db_dialeto": db_dialeto, | ||
| "db_url": db_url, | ||
| "db_config": db_config, |
Comment on lines
+49
to
+51
| dialeto = (estado.get("db_dialeto") or "sqlite").strip().lower() | ||
| nome = _NOME_DIALETO.get(dialeto, _NOME_DIALETO["sqlite"]) | ||
| nota = _NOTA_DIALETO.get(dialeto, _NOTA_DIALETO["sqlite"]) |
Comment on lines
+311
to
+315
| def executar_sql_via_url( | ||
| db_url: str, | ||
| sql: str, | ||
| limite_preview: int = 5, | ||
| timeout_segundos: float = 15.0, |
Comment on lines
+589
to
+592
| # sqlite / duckdb / fallback padrão | ||
| conn = sqlite3.connect(f"file:{caminho_db}?mode=ro", uri=True) | ||
| with conn: | ||
| return _formatar_schema_sqlite(conn) |
Comment on lines
+807
to
+815
| db_url = estado.get("db_url", "").strip() | ||
| if db_url: | ||
| from sqlalchemy import create_engine | ||
|
|
||
| engine = create_engine(db_url) | ||
| try: | ||
| contexto = _formatar_schema_sqlalchemy(engine) | ||
| finally: | ||
| engine.dispose() |
Comment on lines
+816
to
+821
| return { | ||
| "contexto_schema": contexto, | ||
| "erro_execucao": "", | ||
| "status": "schema_obtido", | ||
| "tem_descricao": False, | ||
| } |
Comment on lines
+49
to
+51
| db_dialeto: str = "sqlite", | ||
| db_config: dict[str, Any] | None = None, | ||
| db_url: str = "", |
- CLI --db-password now reads from DB_PASSWORD env var by default (avoids leaking secrets into shell history/process args) - validar_sql_segura blocks MySQL INTO OUTFILE/DUMPFILE and LOAD_FILE(), plus Postgres pg_read_file/pg_ls_dir/lo_import/lo_export and SQLite load_extension() -- these start with SELECT and previously slipped past the read-only check - state now redacts db_config/db_url passwords before returning results to callers (_redigir_credenciais), instead of leaking them in plain text - code-gen prompt now also resolves dialect from db_url when db_dialeto is unset, instead of always defaulting to SQLite syntax - executar_sql_via_url now applies the same timeout/read-only session settings the dialect-specific executors already had - schema fallback for an unsupported dialect (e.g. duckdb) now raises a clear error instead of silently trying to open it with the sqlite3 driver - schema introspection failures (native fallback and db_url path) now return a structured exec_erro instead of an uncaught exception - db_url schema path now applies FK inference the same way the db_config path already did - db_dialeto now defaults to "" instead of "sqlite" through runtime/ InsightEngine/cli, so extension-based detection (.duckdb etc) isn't short-circuited for callers who don't set it explicitly
antoniocaarvalh
force-pushed
the
feature/postgres-mysql-support
branch
from
September 28, 2026 20:09
fce2c88 to
ec68b4c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Adicionei suporte a PostgreSQL e MySQL. Criei a introspecção de schema pros dois bancos (via information_schema, sem precisar de Java/SchemaCrawler) e os executores de query (psycopg2 e pymysql), com a mesma validação de segurança de antes (só SELECT/WITH). O prompt do LLM agora sabe qual banco é o alvo, em vez de sempre assumir SQLite (Isso estava me dando problema). Testei tudo local com Postgres 17 e MySQL 8.4, incluindo uma query de exemplo do BIRD que vcfs passaram, e rodei a suíte de testes inteira para ver se quebrava algo do SQLite, e deu tudo certinho. Fiz um docx detalhando mais o processo