# psql-scip

[![CI](https://github.com/gnufood/psql-scip/actions/workflows/ci.yml/badge.svg)](https://github.com/gnufood/psql-scip/actions/workflows/ci.yml)
[![rustc 1.88+](https://img.shields.io/badge/rustc-1.88+-orange?logo=rust)](https://www.rust-lang.org)

SCIP indexer for PostgreSQL. Produces an `index.scip` from a `pg_dump` schema
and a directory of `.sql` files.

## Install

**Pre-built binaries** (Linux x86_64 and aarch64, gnu and musl)

```bash
curl -LsSf https://releases.gnu.foo/psql-scip/latest/install.sh | sh
```

[inspect install.sh](https://releases.gnu.foo/psql-scip/latest/install.sh)

**From crates.io**

```bash
cargo install psql-scip
```

**Build from source**

```bash
git clone https://github.com/gnufood/psql-scip
cd psql-scip
cargo build --release
# binary at target/release/psql-scip
```

Requires Rust 1.88 or later.

## Usage

```bash
psql-scip -s <FILE> -r <DIR> [OPTIONS]
```

| Flag | Short | Default | Description |
|---|---|---|---|
| `--schema <FILE>` | `-s` | required | `pg_dump --schema-only` output |
| `--root <DIR>` | `-r` | required | directory of `.sql` files to index |
| `--out <FILE>` | `-o` | `index.scip` | output path for the SCIP index |
| `--log-level` | `-l` | `warn` | `error` / `warn` / `info` / `debug` / `trace` |
| `--no-gitignore` | `-n` | false | index files excluded by `.gitignore` |

`RUST_LOG` env var overrides `--log-level`. Exit codes: `0` success, `1` fatal error.

## Example

```bash
pg_dump --schema-only mydb > schema.sql

psql-scip \
  --schema schema.sql \
  --root   ./db \
  --out    index.scip
```

Output:

```text
tables=12 functions=34 views=4 types=7 ... schema loaded
count=50 sql files discovered
files=50 occurrences=529 indexing complete
```

The resulting `index.scip` can be used with any SCIP-compatible tool —
[Sourcegraph](https://sourcegraph.com), [`scip` CLI](https://github.com/sourcegraph/scip), etc.

## What gets indexed

### Definitions (`SymbolInformation`)

Extracted from the schema file. Each definition produces a symbol with a kind,
a one-line SQL signature, optional `COMMENT ON` documentation, and an
`enclosing_symbol` for nested constructs (columns inside tables, fields inside
composite types).

| Construct | SCIP kind | Symbol format |
|---|---|---|
| Table | `Struct` | `pgscip . . . public/users.` |
| Column | `Field` | `pgscip . . . public/users#id.` |
| Composite type field | `Field` | `pgscip . . . public/address#street.` |
| Function | `Function` | `pgscip . . . public/find_item().` |
| View | `Class` | `pgscip . . . public/users_view.` |
| Enum type | `Enum` | `pgscip . . . public/status#` |
| Composite type | `Struct` | `pgscip . . . public/point_2d#` |
| Trigger | `Event` | `pgscip . . . public/trg_audit.` |
| Policy | `Attribute` | `pgscip . . . public/rls_policy.` |
| Index | `Property` | `pgscip . . . public/users_idx.` |

Signatures carry inner occurrences where applicable — a function's return type
and a column's type are clickable hyperlinks when that type is a user-defined
type in the schema.

### Relationships

Cross-references encoded directly in `SymbolInformation.relationships`:

| Symbol | Relationship |
|---|---|
| Column with user-defined type | `is_type_definition` → the type |
| Column with FK | `is_reference + is_type_definition` → the referenced column |
| Function | `is_type_definition` → return type and each param type (user-defined only) |
| View | `is_reference` → each source table / view |
| Trigger | `is_reference` → target table and `EXECUTE FUNCTION` target |
| Index | `is_reference` → target table |
| Policy | `is_reference` → target table |

### References (`Occurrence`)

Resolved across all `.sql` files under `--root`. Each occurrence carries the
reference range, the enclosing statement range, and a back-reference to the
defining symbol.

| Construct | Location method |
|---|---|
| Table reference | AST `RangeVar` |
| Column reference | AST `ColumnRef` |
| Function call | AST `FuncCall` |
| Type usage | AST `TypeCast` / `TypeName` |
| Index columns | Lexer — AST carries no position for `ON (col_a, col_b)` |
| FK columns (both sides) | Lexer — AST carries no position for `FOREIGN KEY (col)` / `REFERENCES t(col)` |
| Trigger function | Lexer — AST carries no position after `EXECUTE FUNCTION` |

Parse failures produce diagnostic occurrences with the parser error message
attached, visible as inline errors in supporting editors.

### Not indexed

`DROP` and `ALTER ... RENAME` statements are not yet handled — the schema model
is forward-only. This is not a limitation when using a `pg_dump --schema-only`
output (which never contains destructive DDL). Migration stacking support is
planned.

## Dev

```bash
git clone https://github.com/gnufood/psql-scip
cd psql-scip
just ci   # format-check, lint, test, audit, msrv
```

Requires `cargo-audit` and `cargo-msrv` in addition to Rust 1.88 or later:

```bash
cargo install cargo-audit --locked
cargo install cargo-msrv --locked
```

## Bug reports

[Open an issue](https://github.com/gnufood/psql-scip/issues).

## Acknowledgements

Built on the work of the [Postgres Language Server](https://github.com/supabase-community/postgres-language-server) team, whose crates informed the early design of this project.

## License

[MIT](LICENSE)
