README.md

# PgInspect

Elixir library with a C NIF for parsing PostgreSQL queries. It uses
[pganalyze/libpg_query](https://github.com/pganalyze/libpg_query) for parsing,
deparsing, fingerprinting, and normalization.

## Features

- Raw PostgreSQL AST parse/deparse
- High-level query analysis
- Query truncation
- Query normalization
- Query fingerprinting

## Installation

Not published to Hex yet.

## Usage

### Raw AST I/O

```elixir
iex> {:ok, ast} = PgInspect.parse("SELECT * FROM users WHERE id = $1")
iex> match?(%PgQuery.ParseResult{}, ast)
true

iex> PgInspect.deparse(ast)
{:ok, "SELECT * FROM users WHERE id = $1"}
```

### Query Analysis

```elixir
iex> {:ok, analyzed} =
...>   PgInspect.analyze("""
...>   WITH recent_posts AS (SELECT * FROM posts WHERE author_id = $1)
...>   SELECT count(*) FROM recent_posts rp WHERE rp.inserted_at > $2::timestamptz
...>   """)

iex> PgInspect.tables(analyzed)
["posts"]

iex> PgInspect.cte_names(analyzed)
["recent_posts"]

iex> PgInspect.functions(analyzed)
["count"]

iex> PgInspect.filter_columns(analyzed)
[{"posts", "author_id"}, {"recent_posts", "inserted_at"}]

iex> PgInspect.parameter_references(analyzed)
[
  %{location: 56, length: 2},
  %{location: 111, length: 2, typename: ["timestamptz"]}
]
```

### Truncation

```elixir
iex> PgInspect.truncate("SELECT id, name, email FROM users WHERE active = true", 32)
{:ok, "SELECT ... FROM users WHERE ..."}
```

### Normalization

```elixir
iex> PgInspect.Normalize.normalize("SELECT * FROM users WHERE id = 123")
{:ok, "SELECT * FROM users WHERE id = $1"}
```

### Fingerprinting

```elixir
iex> PgInspect.Fingerprint.fingerprint("SELECT * FROM users WHERE id = 123")
{:ok, "a0ead580058af585"}

iex> PgInspect.Fingerprint.fingerprint("SELECT * FROM users WHERE id = 456")
{:ok, "a0ead580058af585"}
```

## Benchmarking

To benchmark the public API surface in the `dev` environment:

```sh
mix benchmark.public_api
```

You can shorten the run for quick checks:

```sh
mix benchmark.public_api --warmup 0.5 --time 2 --memory-time 0
```

The benchmark covers:

- SQL entry points such as `PgInspect.parse/1`, `PgInspect.analyze/1`,
  `PgInspect.truncate/2`, normalization, and fingerprinting
- AST entry points such as `PgInspect.deparse/1` and `PgInspect.Protobuf.to_sql/1`
- analysis result accessors such as `PgInspect.tables/1`

## License

This library is distributed under the terms of the [MIT license](LICENSE).

The libpg_query snapshot is distributed under the BSD 3-Clause license. See
[libpg_query/LICENSE](libpg_query/LICENSE).

## Contributing

Bug reports and pull requests are welcome.