Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
47 changes: 46 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ jobs:
--ignore=tests/test_turso.py \
--ignore=tests/test_firebird.py \
--ignore=tests/test_ssh.py \
--ignore=tests/test_clickhouse.py
--ignore=tests/test_clickhouse.py \
--ignore=tests/test_exasol.py

test-databricks:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -543,6 +544,50 @@ jobs:
CLICKHOUSE_DATABASE: test_sqlit
run: uv run pytest tests/test_clickhouse.py -v --timeout=120

test-exasol:
runs-on: ubuntu-latest
needs: build

steps:
- uses: actions/checkout@v4

- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Install dependencies
run: uv sync --group test --no-dev --extra exasol

- name: Start Exasol
run: |
docker run -d --name exasol --privileged \
--memory=4g --memory-swap=4g --cpus=2 --stop-timeout=120 \
-p 127.0.0.1:8563:8563 \
exasol/docker-db:latest-8
for i in {1..60}; do
if nc -z localhost 8563 > /dev/null 2>&1; then
echo "Exasol port is open"
break
fi
echo "Waiting for Exasol... ($i/60)"
sleep 10
done
nc -z localhost 8563

- name: Run Exasol integration tests
env:
EXASOL_HOST: localhost
EXASOL_PORT: 8563
EXASOL_USER: sys
EXASOL_PASSWORD: exasol
EXASOL_SCHEMA: TEST_SQLIT
EXASOL_REQUIRE_LIVE: "1"
run: uv run pytest tests/test_exasol.py -v --timeout=300

test-ssh:
runs-on: ubuntu-latest
needs: build
Expand Down
2 changes: 2 additions & 0 deletions .serena/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/cache
/project.local.yml
141 changes: 141 additions & 0 deletions .serena/project.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# the name by which the project can be referenced within Serena
project_name: "sqlit"


# list of languages for which language servers are started; choose from:
# al angular ansible bash clojure
# cpp cpp_ccls crystal csharp csharp_omnisharp
# dart elixir elm erlang fortran
# fsharp go groovy haskell haxe
# hlsl html java json julia
# kotlin lean4 lua luau markdown
# matlab msl nix ocaml pascal
# perl php php_phpactor powershell python
# python_jedi python_ty r rego ruby
# ruby_solargraph rust scala scss solidity
# svelte swift systemverilog terraform toml
# typescript typescript_vts vue yaml zig
# (This list may be outdated. For the current list, see values of Language enum here:
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
# Note:
# - For C, use cpp
# - For JavaScript, use typescript
# - For Angular projects, use angular (subsumes typescript+html; requires `npm install` in the project root)
# - For Svelte projects, use svelte (subsumes typescript/javascript for .svelte projects; requires npm)
# - For SCSS / Sass / plain CSS, use scss (some-sass-language-server handles all three)
# - For Free Pascal/Lazarus, use pascal
# Special requirements:
# Some languages require additional setup/installations.
# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers
# When using multiple languages, the first language server that supports a given file will be used for that file.
# The first language is the default language and the respective language server will be used as a fallback.
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
languages:
- python

# the encoding used by text files in the project
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
encoding: "utf-8"

# line ending convention to use when writing source files.
# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default)
# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings.
line_ending:

# The language backend to use for this project.
# If not set, the global setting from serena_config.yml is used.
# Valid values: LSP, JetBrains
# Note: the backend is fixed at startup. If a project with a different backend
# is activated post-init, an error will be returned.
language_backend:

# whether to use project's .gitignore files to ignore files
ignore_all_files_in_gitignore: true

# advanced configuration option allowing to configure language server-specific options.
# Maps the language key to the options.
# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available.
# No documentation on options means no options are available.
ls_specific_settings: {}

# list of additional paths to ignore in this project.
# Same syntax as gitignore, so you can use * and **.
# Note: global ignored_paths from serena_config.yml are also applied additively.
ignored_paths: []

# whether the project is in read-only mode
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
# Added on 2025-04-18
read_only: false

# list of tool names to exclude.
# This extends the existing exclusions (e.g. from the global configuration)
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
excluded_tools: []

# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default).
# This extends the existing inclusions (e.g. from the global configuration).
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
included_optional_tools: []

# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
fixed_tools: []

# list of mode names to that are always to be included in the set of active modes
# The full set of modes to be activated is base_modes + default_modes.
# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this setting overrides the global configuration.
# Set this to [] to disable base modes for this project.
# Set this to a list of mode names to always include the respective modes for this project.
base_modes:

# list of mode names that are to be activated by default, overriding the setting in the global configuration.
# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes.
# If the setting is undefined/empty, the default_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
# Therefore, you can set this to [] if you do not want the default modes defined in the global config to apply
# for this project.
# This setting can, in turn, be overridden by CLI parameters (--mode).
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
default_modes:

# initial prompt for the project. It will always be given to the LLM upon activating the project
# (contrary to the memories, which are loaded on demand).
initial_prompt: ""

# time budget (seconds) per tool call for the retrieval of additional symbol information
# such as docstrings or parameter information.
# This overrides the corresponding setting in the global configuration; see the documentation there.
# If null or missing, use the setting from the global configuration.
symbol_info_budget:

# list of regex patterns which, when matched, mark a memory entry as read‑only.
# Extends the list from the global configuration, merging the two lists.
read_only_memory_patterns: []

# list of regex patterns for memories to completely ignore.
# Matching memories will not appear in list_memories or activate_project output
# and cannot be accessed via read_memory or write_memory.
# To access ignored memory files, use the read_file tool on the raw file path.
# Extends the list from the global configuration, merging the two lists.
# Example: ["_archive/.*", "_episodes/.*"]
ignored_memory_patterns: []

# list of mode names to be activated additionally for this project, e.g. ["query-projects"]
# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes.
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
added_modes:

# list of additional workspace folder paths for cross-package reference support (e.g. in monorepos).
# Paths can be absolute or relative to the project root.
# Each folder is registered as an LSP workspace folder, enabling language servers to discover
# symbols and references across package boundaries.
# Currently supported for: TypeScript.
# Example:
# additional_workspace_folders:
# - ../sibling-package
# - ../shared-lib
additional_workspace_folders: []
29 changes: 24 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ To run the complete test suite including SQL Server, PostgreSQL, MySQL, MariaDB,
```bash
docker compose -f infra/docker/docker-compose.test.yml up -d
```
To include the enterprise test containers (Db2, Trino, Presto, Oracle 11g):
To include the enterprise test containers (Db2, Trino, Presto, Oracle 11g, Exasol):
```bash
docker compose -f infra/docker/docker-compose.test.yml --profile enterprise up -d
```
Expand Down Expand Up @@ -176,6 +176,19 @@ The database tests can be configured with these environment variables:
| `ORACLE11G_CLIENT_MODE` | `thick` | Oracle client mode |
| `ORACLE11G_CLIENT_LIB_DIR` | `` | Oracle Instant Client library directory |

**Exasol:**
| Variable | Default | Description |
|----------|---------|-------------|
| `EXASOL_HOST` | `localhost` | Exasol hostname |
| `EXASOL_PORT` | `8563` | Exasol port |
| `EXASOL_USER` | `sys` | Exasol username |
| `EXASOL_PASSWORD` | `exasol` | Exasol password |
| `EXASOL_SCHEMA` | `TEST_SQLIT` | Schema the Exasol fixtures create and drop |
| `EXASOL_READY_TIMEOUT` | `300` | Seconds to wait for Exasol to accept a login |
| `EXASOL_REQUIRE_LIVE` | unset | Set to `1` to fail if the required driver/server is unavailable |

**Note:** Exasol runs in the `enterprise` profile and needs minutes, not seconds, before it accepts connections. `exasol/docker-db` binds port 8563 long before it will authenticate, so an open port is not yet a database that accepts a login. The fixtures retry a real connect until `EXASOL_READY_TIMEOUT` elapses; raise that value on slower hardware or a cold image pull.

**Flight SQL:**
| Variable | Default | Description |
|----------|---------|-------------|
Expand Down Expand Up @@ -271,12 +284,18 @@ removes them. It verifies CLI creation via stdin, separate-process queries, cred
metadata, row limits and rename. Load the test token from your secret manager into the process
environment; do not commit it or pass it as a command-line argument.

Set `SQLIT_LIVE_PROVIDER=databricks`, `SQLIT_LIVE_HOST`, `SQLIT_LIVE_HTTP_PATH`,
`SQLIT_LIVE_TOKEN`, and optionally `SQLIT_LIVE_CATALOG` (default `workspace`), then run:
Set `SQLIT_LIVE_HOST` and `SQLIT_LIVE_TOKEN`, plus the provider-specific settings:

- Databricks: `SQLIT_LIVE_PROVIDER=databricks`, `SQLIT_LIVE_HTTP_PATH`, and optionally
`SQLIT_LIVE_CATALOG` (default `workspace`).
- Exasol: `SQLIT_LIVE_PROVIDER=exasol`, `SQLIT_LIVE_USERNAME`, and optionally
`SQLIT_LIVE_PORT` (default 8563). Use the SaaS PAT as the token.

```bash
uv run --no-sync pytest tests/integration/test_cloud_provider_credentials.py -v --timeout=240
```

The ordinary CI lane runs without cloud credentials. A configured live run fails on missing
configuration or an unavailable keyring; it does not silently skip those checks.
The ordinary CI lane runs without cloud credentials. Configured live runs fail on missing
settings or an unavailable OS keyring. The dedicated Exasol Docker lane sets
`EXASOL_REQUIRE_LIVE=1`; schema setup errors fail the suite instead of becoming skipped tests.
The Docker TLS regression uses `openssl` to retrieve the test server's public certificate chain.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
---

### Connect
Supports all major databases: SQL Server, PostgreSQL, MySQL, SQLite, MariaDB, FirebirdSQL, Oracle, DuckDB, CockroachDB, ClickHouse, Snowflake, Databricks, Supabase, CloudFlare D1, Turso, Athena, BigQuery, Spanner, RedShift, IBM Db2, SAP HANA, Teradata, Trino, Presto, Apache Flight SQL, Apache Impala, SurrealDB and osquery.
Supports all major databases: SQL Server, PostgreSQL, MySQL, SQLite, MariaDB, FirebirdSQL, Oracle, DuckDB, CockroachDB, ClickHouse, Snowflake, Databricks, Supabase, CloudFlare D1, Turso, Athena, BigQuery, Spanner, RedShift, IBM Db2, SAP HANA, Teradata, Exasol, Trino, Presto, Apache Flight SQL, Apache Impala, SurrealDB and osquery.

![Database Providers](docs/demos/demo-providers.gif)

Expand Down Expand Up @@ -254,6 +254,15 @@ PATs and client secrets are kept in the OS credential store, including connectio
Use stdin to keep the URL out of shell history and process arguments. Browser OAuth does not ask
for a database password. Unity Catalog and the legacy Hive metastore use their respective metadata APIs.

### Exasol authentication and TLS

For Exasol SaaS, select username/password authentication and use the database username from
connection details with your personal access token as the password. OpenID access/refresh token
modes are for those credential types, not SaaS PATs. The selected secret uses the OS credential
store. Default TLS follows pyexasol's certificate verification; use `--tls-mode require` only
when deliberately connecting to a self-signed development server. `verify-ca` validates the CA,
while `verify-full` also checks the hostname.

### How are sensitive credentials stored?

Connection details are stored in `connections.json` inside the config directory, but passwords are stored in your OS keyring when available (macOS Keychain, Windows Credential Locker, Linux Secret Service).
Expand Down Expand Up @@ -301,6 +310,7 @@ Most of the time you can just run `sqlit` and connect. If a Python driver is mis
| Athena | `pyathena` | `pipx inject sqlit-tui pyathena` | `python -m pip install pyathena` |
| BigQuery | `google-cloud-bigquery` | `pipx inject sqlit-tui google-cloud-bigquery` | `python -m pip install google-cloud-bigquery` |
| Spanner | `google-cloud-spanner` | `pipx inject sqlit-tui google-cloud-spanner` | `python -m pip install google-cloud-spanner` |
| Exasol | `pyexasol` | `pipx inject sqlit-tui pyexasol` | `python -m pip install pyexasol` |
| Apache Arrow Flight SQL | `adbc-driver-flightsql` | `pipx inject sqlit-tui adbc-driver-flightsql` | `python -m pip install adbc-driver-flightsql` |
| Apache Impala | `impyla` | `pipx inject sqlit-tui impyla` | `python -m pip install impyla` |
| Trino | `trino` | `pipx inject sqlit-tui trino` | `python -m pip install trino` |
Expand Down
15 changes: 15 additions & 0 deletions infra/docker/docker-compose.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,21 @@ services:
profiles:
- enterprise

# Exasol ships a single-container database image, but it manages its own
# storage volumes and kernel parameters, so it needs privileged mode and a
# long stop grace period - an abrupt kill leaves the data volume dirty.
# No healthcheck: the image ships no lightweight probe, and an open port is
# not readiness. tests/fixtures/exasol.py gates on a real connect instead.
exasol:
image: exasol/docker-db:latest-8
container_name: sqlit-test-exasol
privileged: true
stop_grace_period: 120s
ports:
- "${EXASOL_PORT:-8563}:8563"
profiles:
- enterprise

trino:
image: trinodb/trino:latest
container_name: sqlit-test-trino
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-08-27
Loading
Loading