Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d787625
feat(loaders): add Microsoft SQL Server support
gkorland Aug 13, 2026
32ce161
fix(loaders): validate SQL Server identifiers before interpolation
gkorland Aug 13, 2026
889d97c
fix(loaders): qualify SQL Server sample queries with the catalog schema
gkorland Aug 13, 2026
ac1143e
fix(loaders): drop URL-schema fallback in SQL Server sample queries
gkorland Aug 13, 2026
563fd2c
fix(loaders): offload SQL Server introspection off the event loop
Anchel123 Aug 24, 2026
567c795
fix(loaders): bound SQL Server connect and query time
Anchel123 Aug 24, 2026
a0a0cb9
fix(loaders): use one process-wide pymssql timeout
Anchel123 Aug 24, 2026
050db37
test(loaders): cover SQL Server refresh, routing and URL edge cases
Anchel123 Aug 24, 2026
87a1558
fix(sql): stop a broken-out delimiter passing as a quoted identifier
Anchel123 Aug 25, 2026
3607803
Merge remote-tracking branch 'origin/staging' into feat/sqlserver-sup…
Anchel123 Aug 26, 2026
926f91a
fix(sqlserver): refuse a dotted name the sampler cannot disambiguate
Anchel123 Aug 26, 2026
4878a57
Merge remote-tracking branch 'origin/staging' into feat/sqlserver-sup…
Anchel123 Sep 3, 2026
ec7cc04
fix(sqlserver): scope foreign keys to the loaded schema
Anchel123 Sep 7, 2026
b6b8334
fix(sqlserver): make schema introspection valid, resilient and cheaper
Anchel123 Sep 7, 2026
e99cf16
refactor(sqlserver): stop reconfiguring global logging at import
Anchel123 Sep 7, 2026
9d1dc67
fix(sqlserver): keep the prefix intact when refreshing an underscored…
Anchel123 Sep 7, 2026
041b9bf
test(sqlserver): introspect a real server, and drop the stray .coverage
Anchel123 Sep 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,14 @@ SDK
Dependabot
PyPI
pypi
pymssql
sqlserver
SQLServerLoader
dbo
tsql
hostname
TLS
sqlglot
signup
SMTP
outbox
Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,10 @@ pip install queryweaver[server]
pip install queryweaver[dev]
```

> **SQL Server and Snowflake need `queryweaver[server]`.** Their drivers
> (`pymssql` and `snowflake-connector-python`) ship in the `server` extra, so the
> minimal SDK install can only connect to PostgreSQL and MySQL.

### Quick Start

```python
Expand All @@ -345,7 +349,7 @@ async def main():
# Initialize with FalkorDB connection
qw = QueryWeaver(falkordb_url="redis://localhost:6379")

# Connect a PostgreSQL or MySQL database
# Connect a PostgreSQL, MySQL, SQL Server or Snowflake database
Comment thread
coderabbitai[bot] marked this conversation as resolved.
conn = await qw.connect_database("postgresql://user:pass@host:5432/mydb")
print(f"Connected: {conn.database_id}") # "mydb"

Expand Down Expand Up @@ -389,7 +393,7 @@ async with QueryWeaver(falkordb_url="redis://host-a:6379", user_id="tenant_a") a

| Method | Description |
|--------|-------------|
| `connect_database(db_url)` | Connect PostgreSQL/MySQL and load schema |
| `connect_database(db_url)` | Connect PostgreSQL/MySQL/SQL Server/Snowflake and load schema (SQL Server and Snowflake require `queryweaver[server]`) |
| `query(database, question)` | Convert natural language to SQL and execute |
| `get_schema(database)` | Retrieve database schema (tables and relationships) |
| `list_databases()` | List all connected databases |
Expand Down Expand Up @@ -435,7 +439,8 @@ if result.requires_confirmation:
- Python 3.12+
- FalkorDB instance (local or remote)
- OpenAI or Azure OpenAI API key (for LLM)
- Target SQL database (PostgreSQL or MySQL)
- Target SQL database (PostgreSQL, MySQL, SQL Server or Snowflake — the last two
require the `queryweaver[server]` extra)

## Development

Expand Down
17 changes: 15 additions & 2 deletions api/core/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@ def get_database_type_and_loader(
PostgreSQL for backward compatibility on the server path.

When ``sdk_only`` is True, raises ``InvalidArgumentError`` for vendors
that need the ``[server]`` extra (snowflake) or for unknown URL schemes,
so SDK callers get a clean error instead of a deferred ``ImportError``.
that need the ``[server]`` extra (snowflake, sqlserver) or for unknown URL
schemes, so SDK callers get a clean error instead of a deferred
``ImportError``.
"""
if not db_url or db_url == "No URL available for this database.":
return None, None
Expand All @@ -138,6 +139,17 @@ def get_database_type_and_loader(
# pylint: disable=import-outside-toplevel
from api.loaders.snowflake_loader import SnowflakeLoader
return 'snowflake', SnowflakeLoader
if db_url_lower.startswith('sqlserver://'):
if sdk_only:
raise InvalidArgumentError(
"SQL Server requires the [server] extra: "
"pip install queryweaver[server]"
)
# Lazy-import: pymssql is in the [server] extra, not in the core SDK
# install.
# pylint: disable=import-outside-toplevel
from api.loaders.sqlserver_loader import SQLServerLoader
return 'sqlserver', SQLServerLoader

if sdk_only:
raise InvalidArgumentError(
Expand Down Expand Up @@ -205,6 +217,7 @@ def truncate_for_log(query: str, max_length: int = 200) -> str:
"postgres": "postgres",
"mysql": "mysql",
"snowflake": "snowflake",
"sqlserver": "tsql",
}

# sqlglot expression class names that represent a write, DDL, privilege change,
Expand Down
4 changes: 3 additions & 1 deletion api/core/schema_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ def _step_start(steps_counter: int) -> dict[str, str]:
"message": f"Step {steps_counter}: Starting database connection",
}

_KNOWN_DB_SCHEMES = ("postgresql://", "postgres://", "mysql://", "snowflake://")
_KNOWN_DB_SCHEMES = (
"postgresql://", "postgres://", "mysql://", "snowflake://", "sqlserver://",
)


def _step_detect_db_type(steps_counter: int, url: str) -> tuple[type[BaseLoader], dict[str, str]]:
Expand Down
Loading