Metadata-Version: 2.4
Name: bigquery-sql-completion
Version: 0.3.2
Summary: BigQuery SQL completion engine and LSP server
Requires-Python: >=3.10
Requires-Dist: attrs>=23.2
Requires-Dist: cattrs>=23.2
Requires-Dist: pygls>=2.1.0
Requires-Dist: sqlglot>=26
Description-Content-Type: text/markdown

# bigquery-sql-completion

BigQuery SQL completion engine for Python. Ships both a **completion library** and a ready-to-use **LSP server** (stdio, Language Server Protocol) that any editor or extension can spawn.

Requires Python 3.10+. Developed on Python 3.13.

---

## Installation

```bash
pip install bigquery-sql-completion
# or, with uv:
uv add bigquery-sql-completion
```

Runtime dependencies: `attrs`, `cattrs`, `pygls`, `sqlglot`.

---

## LSP server

### As a CLI command

After installation the `bigquery-sql-completion-lsp` script is available on your PATH:

```bash
bigquery-sql-completion-lsp --stdio
```

Pass one or more schema catalog files:

```bash
bigquery-sql-completion-lsp --schema ./schema.json --dialect bigquery
```

The `--stdio` flag is a no-op (stdio is always used) but accepted for compatibility with VS Code's `ServerOptions.args`.

### As a Python call

```python
from bigquery_sql_completion.lsp import serve_stdio

serve_stdio(schema_paths=["./schema.json"], dialect="bigquery")
```

`serve_stdio` blocks until the client disconnects. You can also build and start the server yourself:

```python
from bigquery_sql_completion.lsp import build_server
from pathlib import Path

server = build_server(schema_paths=[Path("schema.json")], dialect="bigquery")
server.start_io()
```

---

## Schema catalog format

The server and library share the same nested JSON format understood by [sqlglot](https://github.com/tobymao/sqlglot):

```json
{
  "my_project": {
    "my_dataset": {
      "orders": {
        "order_id": "INT64",
        "customer_id": "INT64",
        "total": "FLOAT64"
      },
      "customers": {
        "customer_id": "INT64",
        "name": "STRING"
      }
    }
  }
}
```

Nesting depth: **1 level** (table → columns), **2 levels** (dataset → table → columns), or **3 levels** (project → dataset → table → columns). Mixed depth across files is fine; they are deep-merged.

Column types can be plain strings, or objects with a `type` field and an optional `sample_values` list (at most 20 entries; extras are dropped). `sample_values` may be `null` to omit. Supported sample primitives are strings, numbers, and JSON `null` (completed as `NULL` in SQL). This powers optional **value completions** after `=`, `!=`, `<>`, `>`, `>=`, `<`, and `<=` in `WHERE`, `HAVING`, `ON`, and `QUALIFY` predicates when the left-hand side is a simple column and the right-hand value position is at the cursor.

```json
"country": {
  "type": "STRING",
  "sample_values": ["SE", "NO", "DK", null]
}
```

### Loading schema at server start

Three ways (combined, later entries override):

1. **CLI flag** `--schema path/to/catalog.json` (repeatable).
2. **Environment variable** `BIGQUERY_SQL_COMPLETION_SCHEMA_PATHS` — `os.pathsep`-separated list of paths.
3. **`initializationOptions`** from the LSP client (see below).

### Refreshing schema at runtime

Two approaches when schema changes after the server is running:

**File-based (automatic)**: re-write the catalog JSON on disk. The server watches files it was started with via `workspace/didChangeWatchedFiles` and reloads automatically.

**Push notification (in-band, no disk I/O)**: send the `bigquerySqlCompletion/updateSchema` notification from the LSP client:

```json
{
  "catalog": { "project": { "dataset": { "table": { "col": "STRING" } } } },
  "dialect": "bigquery"
}
```

The server replaces the in-memory schema immediately; no restart needed.

You can also trigger a manual reload via the registered command `bigquery-sql-completion.refreshSchema`.

---

## VS Code / LSP client configuration

Pass `initializationOptions` when creating the `LanguageClient`:

```typescript
initializationOptions: {
  bigquerySqlCompletion: {
    schemaPaths: ["/absolute/or/relative/schema.json"],
    dialect: "bigquery",
    // Optional: default project for unqualified `dataset.table` disambiguation and
    // shorter completion inserts (when `completeWithProjectId` is false).
    defaultGcpProjectId: "my-gcp-project",
    completeWithProjectId: true,
  },
}
```

`schemaPaths`, `dialect`, `defaultGcpProjectId`, and `completeWithProjectId` can also be updated via `workspace/didChangeConfiguration` (same shape under the `bigquerySqlCompletion` key). See [docs/lsp-default-project-and-completion-insert.md](docs/lsp-default-project-and-completion-insert.md) for behavior details.

Trigger characters are `.` and `` ` `` by default.

---

## Completion library

Use the completion engine directly without the LSP:

```python
from sqlglot.schema import MappingSchema
from bigquery_sql_completion.complete.complete import complete
from bigquery_sql_completion.types.completion import CompletionRequest
from bigquery_sql_completion.schema import load_catalog_from_paths, to_mapping_schema
from pathlib import Path

catalog = load_catalog_from_paths([Path("schema.json")])
schema = to_mapping_schema(catalog)

req = CompletionRequest(sql="SELECT * FROM my_project.my_dataset.", cursor_position=38)
resp = complete(req, schema)
for item in resp.completions:
    print(item.label)
```

---

## Development

```bash
uv sync
uv run pytest
uv run pyright
uv run ruff check src
```

Run tests on the supported floor version:

```bash
uv run --python 3.10 pytest
```

Ruff and Pyright target Python 3.10 (the supported minimum). The dev venv and editor stay on 3.13.
