+
+---
+
+**Documentation**: https://stac-utils.github.io/pgstac/
+
+**Source Code**: https://github.com/stac-utils/pgstac
+
+---
+
+**PgSTAC** is a set of SQL functions and schema to build highly performant databases for Spatio-Temporal Asset Catalogs ([STAC](https://stacspec.org/)). The project also provides **pgstac-migrate** (a focused migration package), the **pgstac** Rust CLI (loading, search, queryables, extensions, maintenance), and **pypgstac-rs** (a Rust-backed Python read/write extension for stac-fastapi-pgstac).
+
+PgSTAC provides functionality for STAC Filters, CQL2 search, and utilities to help manage the indexing and partitioning of STAC Collections and Items.
+
+PgSTAC is used in production to scale to hundreds of millions of STAC items. PgSTAC implements core data models and functions to provide a STAC API from a PostgreSQL database. PgSTAC is entirely within the database and does not provide an HTTP-facing API. The [STAC FastAPI](https://github.com/stac-utils/stac-fastapi) PgSTAC backend and [Franklin](https://github.com/azavea/franklin) can be used to expose a PgSTAC catalog. Integrating PgSTAC with any other language with PostgreSQL drivers is also possible.
+
+PgSTAC Documentation: https://stac-utils.github.io/pgstac/pgstac
+
+Rust client & CLI Documentation: https://stac-utils.github.io/pgstac/pgstac-rs
+
+Migrating off pyPgSTAC: https://stac-utils.github.io/pgstac/pypgstac
+
+pgstac-migrate package: `src/pgstac-migrate`
+
+## Project structure
+
+```
+/
+ ├── src/pgstac/sql/ - PgSTAC SQL code
+ ├── src/pgstac/migrations/ - Migrations for incremental upgrades
+ ├── src/pgstac/tests/ - PgSTAC SQL test suite
+ ├── src/pgstac-migrate/ - pgstac-migrate migration package
+ ├── src/pgstac-rs/ - Rust crate: pgstac CLI + pgstac Python extension
+ └── scripts/ - scripts to set up the environment, create migrations, and run tests
+```
+
+## Contribution & Development
+
+See [CONTRIBUTING.md](https://github.com//stac-utils/pgstac/blob/master/CONTRIBUTING.md)
+
+## License
+
+See [LICENSE](https://github.com//stac-utils/pgstac/blob/master/LICENSE)
+
+## Authors
+
+See [contributors](https://github.com/stac-utils/pgstac/graphs/contributors) for a listing of individual contributors.
+
+## Changes
+
+See [CHANGELOG.md](https://github.com/stac-utils/pgstac/blob/master/CHANGELOG.md).
diff --git a/docs/src/pgstac-rs.md b/docs/src/pgstac-rs.md
new file mode 100644
index 00000000..a6637d02
--- /dev/null
+++ b/docs/src/pgstac-rs.md
@@ -0,0 +1,160 @@
+# Rust client & CLI (`pgstac-rs`)
+
+`src/pgstac-rs` is a Rust crate (`pgstac`) and a command-line tool (`pgstac`) that talk to a PgSTAC
+database. It provides:
+
+- a **read engine** that drives `search_plan` / `collection_search_plan` and does the band stepping,
+ hydration (EWKB→GeoJSON, fragment merge), keyset token minting, and the STAC `fields` include/exclude
+ projection **in Rust** — page-equivalent to SQL `search()` but with flat memory under streaming and
+ the CPU moved off the database. When a search's `fields` need no shared fragment, `search_plan` nulls
+ `fragment_id` in the projection it returns, so the engine skips the per-row `item_fragments` lookup;
+- a **connection pool** (`PgstacPool`, the `pool` feature) with the async read API;
+- a **dump/export** library + the `pgstac` **CLI** (`export` / `cli` features);
+- a **Python wheel** (`pgstac`, the `python` feature, built with [maturin](https://github.com/PyO3/maturin)).
+
+## Cargo features
+
+| Feature | Adds |
+| ----------- | ---- |
+| *(default)* | The `Pgstac` trait over any `tokio_postgres::GenericClient`. |
+| `pool` | `PgstacPool`: pooled async read API (rustls TLS, PgBouncer-safe). |
+| `export` | The dump library (scan partitions → stac-geoparquet + manifest). |
+| `cli` | The `pgstac` binary (implies `export`). |
+| `python` | The `pgstac` pyo3 extension module. |
+
+## Environment variables
+
+The library and CLI resolve a connection from a single DSN if one is given, otherwise from the standard
+**libpq** environment variables. The connection `search_path` is **always** set to `pgstac, public`.
+
+### Connection
+
+| Variable | Purpose |
+| -------- | ------- |
+| `PGSTAC_DSN` | Full Postgres connection string (URL or key/value). The CLI `--dsn` flag defaults to this. |
+| `DATABASE_URL` | Fallback DSN if `PGSTAC_DSN` is unset. |
+| `PGHOST` | Server host. |
+| `PGPORT` | Server port. |
+| `PGDATABASE` | Database name. |
+| `PGUSER` | Username. |
+| `PGPASSWORD` | Password. |
+| `PGOPTIONS` | Extra startup options. |
+| `PGAPPNAME` | `application_name` for the connection. |
+| `PGCONNECT_TIMEOUT` | Connection timeout, in seconds. |
+| `PGSSLMODE` | TLS mode (`disable`, `prefer`, `require`, `verify-ca`, `verify-full`). |
+| `PGSSLROOTCERT` | Path to the CA certificate. |
+| `PGSSLCERT` | Path to the client certificate. |
+| `PGSSLKEY` | Path to the client key. |
+
+A DSN (`PGSTAC_DSN` / `--dsn`) takes precedence; the individual `PG*` variables fill in any field the
+DSN does not set.
+
+### Test fixtures
+
+The integration tests skip (rather than fail) when their database is unreachable. Point them at your
+fixtures with:
+
+| Variable | Default | Used by |
+| -------- | ------- | ------- |
+| `PGSTAC_RS_TEST_DB` | `postgresql://username:password@localhost:5439/postgis` | clean-template tests |
+| `PGSTAC_RS_TEST_RICH_DB` | `…/pgstac_rs_test_rich` | search/stream/hydration/collection parity tests |
+| `PGSTAC_RS_TEST_TEMPLATE` | `pgstac_rs_test_template` | pool clone-from-template tests |
+| `PGSTAC_PARITY_DB_010` | `…/a_parity010` | 0.10 dump end-to-end tests |
+| `PGSTAC_PARITY_DB_0911` | `…/a_parity0911` | 0.9.11 dump end-to-end tests |
+
+## Library
+
+```rust,no_run
+use pgstac::{ConnectConfig, PgstacPool};
+use futures::StreamExt;
+use serde_json::json;
+
+# tokio_test::block_on(async {
+// Pool from the environment (PGSTAC_DSN or the PG* vars). Create once at startup.
+let pool = PgstacPool::connect(ConnectConfig::from_env()).await.unwrap();
+
+// A bounded page, with keyset next/prev tokens.
+let page = pool.search_page(&json!({"collections": ["landsat-c2-l2"]}), None, 10).await.unwrap();
+
+// Stream every match with flat memory.
+let mut items = Box::pin(pool.search_items(json!({"collections": ["landsat-c2-l2"]}), None, None));
+while let Some(item) = items.next().await { let _ = item.unwrap(); }
+# })
+```
+
+Pool methods: `search_page`, `search_items` (stream), `search_collect_items`, `stream_ndjson`,
+`search_matched`, `collection_search`, `get_item`, `get_collection`, `get_queryables`.
+
+## CLI
+
+```text
+pgstac
+ dump Dump a pgstac instance (or a subset) to a directory, tar, S3, or stdout
+ search Stream a search/CQL2 result as NDJSON / ItemCollection / geoparquet (0.10 only)
+```
+
+Both subcommands read `--dsn` from `$PGSTAC_DSN` when the flag is omitted.
+
+### `pgstac dump`
+
+Writes fully-hydrated items as stac-geoparquet (one file per partition, ordered by `datetime, id`) plus
+`collection.json` / `queryables.json` / `settings.json` and a sha256'd `manifest.json` (written last —
+its presence marks a complete dump).
+
+| Option | Default | Description |
+| ------ | ------- | ----------- |
+| `--dsn ` | `$PGSTAC_DSN` / local dev | Postgres connection string. |
+| `-o, --out ` | *(required)* | Destination: a directory, `s3://bucket/prefix` (or other object-store URL), a `*.tar` / `*.tar.zst` file, or `-` for stdout. |
+| `-c, --collection ` | *(all)* | Restrict to these collection ids (repeatable). |
+| `--datetime-start ` | — | Inclusive datetime prefilter start (requires `--datetime-end`). |
+| `--datetime-end ` | — | Exclusive datetime prefilter end (requires `--datetime-start`). |
+| `--bbox ` | — | Bbox prefilter (EPSG:4326). |
+| `--compression ` | `zstd` | Parquet codec: `zstd`, `snappy`, `uncompressed`. |
+| `--skip-errors` | off | Continue past per-item errors, recording skips in the report. |
+| `--memory-budget ` | ~25% RAM | Memory budget for the buffered geoparquet path. |
+| `--concurrency ` | `1` | Max partitions dumped concurrently (>1 opens one extra connection per worker). |
+| `--consistent` | off | Dump the whole instance under one repeatable-read snapshot (implies a parallel run). |
+| `--tar-zstd` | off | Write a compressed `.tar.zst` when `--out` is a `.tar` path. |
+| `--dry-run` | off | Report the plan without writing data. |
+
+### `pgstac search`
+
+Streams a 0.10 search off the keyset engine.
+
+| Option | Default | Description |
+| ------ | ------- | ----------- |
+| `--dsn ` | `$PGSTAC_DSN` / local dev | Postgres connection string. |
+| `-o, --out ` | `-` (stdout) | A file path or `-` for stdout. |
+| `--format ` | `ndjson` | `ndjson` (streams all pages), `itemcollection` (one SQL-faithful page with next/prev + context), or `geoparquet`. |
+| `-c, --collection ` | *(all)* | Restrict to these collection ids (repeatable). |
+| `--id ` | — | Restrict to these item ids (repeatable). |
+| `--bbox ` | — | Bbox (EPSG:4326). |
+| `--datetime
` | — | Datetime / interval (STAC datetime syntax). |
+| `--filter ` | — | CQL2-JSON filter (a JSON object). |
+| `--limit ` | server default | Page size (the keyset limit). |
+| `--token ` | — | Continuation token (`next:…` / `prev:…`). With `itemcollection`, returns exactly that one page. |
+| `--max-items ` | *(unbounded)* | Cap total items streamed (`ndjson` / `geoparquet`). |
+| `--compression ` | `zstd` | Parquet codec (`geoparquet` only). |
+
+## Python wheel
+
+```sh
+maturin build -m src/pgstac-rs/Cargo.toml # produces the pgstac wheel
+```
+
+```python
+import asyncio, orjson, pgstac
+
+async def main():
+ pool = await pgstac.Pgstac.connect() # libpq env, or pass a dsn string
+ body = orjson.dumps({"collections": ["landsat-c2-l2"], "limit": 10}).decode()
+ fc = orjson.loads(await pool.search(body))
+ print(fc["numberReturned"])
+
+asyncio.run(main())
+```
+
+`Pgstac` methods (each `async`, returning a JSON string): `connect(dsn=None)`, `search(search,
+token=None, limit=10)`, `search_collect(search, max_items=None)`, `search_matched(search)`,
+`collection_search(search, token=None)`, `get_item(collection_id, item_id)`,
+`get_collection(collection_id)`, `get_queryables(collection_id=None)`.
diff --git a/docs/src/pgstac.md b/docs/src/pgstac.md
index bf9e78a3..402a4471 100644
--- a/docs/src/pgstac.md
+++ b/docs/src/pgstac.md
@@ -125,7 +125,7 @@ If two or more collections in your catalog share a property name, but have diffe
There is a utility SQL function that can be used to help populate the `queryables` table by looking at a sample of data for each collection. This utility can also look to the json schema for STAC extensions defined in the `stac_extensions` table.
-The `stac_extensions` table contains a `url` field and a `content` field for each extension that should be introspected to compare for fields. This can either be filled in manually or by using the `pypgstac loadextensions` command included with pypgstac. This command will look at the `stac_extensions` attribute in all collections to populate the `stac_extensions` table, fetching the json content of each extension. If any urls were added manually to the stac_extensions table, it will also populate any records where the content is NULL.
+The `stac_extensions` table contains a `url` field and a `content` field for each extension that should be introspected to compare for fields. This can either be filled in manually or by using the `pgstac load-extensions` command (the Rust CLI built from `src/pgstac-rs`). This command will look at the `stac_extensions` attribute in all collections to populate the `stac_extensions` table, fetching the json content of each extension (http(s) URLs and local paths). If any urls were added manually to the stac_extensions table, it will also populate any records where the content is NULL.
Once the `stac_extensions` table has been filled in, you can run the `missing_queryables` function either for a single collection:
diff --git a/docs/src/pypgstac.md b/docs/src/pypgstac.md
index 3256aabe..1769aae7 100644
--- a/docs/src/pypgstac.md
+++ b/docs/src/pypgstac.md
@@ -1,70 +1,69 @@
-
-
-PgSTAC includes a Python utility for bulk data loading and managing migrations.
-
-pyPgSTAC is available on PyPI
-```
-python -m pip install pypgstac
-```
-
-pyPgSTAC installs the PostgreSQL driver dependencies (`psycopg[binary]` and `psycopg-pool`) by default.
-
-Or can be built locally
-```
-git clone https://github.com/stac-utils/pgstac
-cd pgstac/pypgstac
-python -m pip install .
-```
-
-```
-pypgstac --help
-Usage: pypgstac [OPTIONS] COMMAND [ARGS]...
-
-Options:
- --install-completion Install completion for the current shell.
- --show-completion Show completion for the current shell, to copy it or
- customize the installation.
-
- --help Show this message and exit.
-
-Commands:
- initversion Get initial version.
- load Load STAC data into a pgstac database.
- migrate Migrate a pgstac database.
- pgready Wait for a pgstac database to accept connections.
- version Get version from a pgstac database.
-```
-
-pyPgSTAC will get the database connection settings from the **standard PG environment variables**:
-
-- PGHOST=0.0.0.0
-- PGPORT=5432
-- PGUSER=username
-- PGDATABASE=postgis
-- PGPASSWORD=asupersecretpassword
-
-It can also take a DSN database url "postgresql://..." via the **--dsn** flag.
-
-### Migrations
-
-`pypgstac migrate` is a compatibility wrapper over the standalone `pgstac-migrate` package.
-
-- Runtime planning and apply logic lives in `pgstac-migrate`.
-- `pypgstac migrate` remains supported for backward compatibility.
-
-Migration filenames use canonical PostgreSQL extension naming:
-
-- **Base migrations:** `pgstac--.sql`
-- **Incremental migrations:** `pgstac----.sql`
-
-These files are bundled in the `pgstac-migrate` artifact and used by both CLIs.
-
-### `pgstac-migrate` CLI and API
-
-For direct migration operations (recommended for new integrations):
+# pyPgSTAC has been removed
+
+The legacy `pypgstac` Python package (migrations, bulk loading, queryables, and
+the pure-Python hydration helpers) has been removed from this repository. Every
+capability it provided now lives in one of its replacements:
+
+- **`pgstac-migrate`** — the migration runtime (planning + apply), published to
+ PyPI as [`pgstac-migrate`](https://pypi.org/project/pgstac-migrate/).
+- **`pgstac`** — the Rust CLI (built from `src/pgstac-rs` with the `cli` feature)
+ for loading, searching, queryables, extensions, and maintenance.
+- **`pypgstac-rs`** — the Rust-backed Python extension (module name
+ `pgstac`) exposing the read/write pool API for `stac-fastapi-pgstac`,
+ published to PyPI as
+ [`pypgstac-rs`](https://pypi.org/project/pypgstac-rs/).
+
+Previously published `pypgstac` releases remain available on PyPI; no new
+versions are published.
+
+## Command and API mapping
+
+| Old `pypgstac` capability | Replacement |
+|---|---|
+| `pypgstac migrate --toversion X` | `pgstac-migrate migrate --to X` |
+| `pypgstac version` (DB-installed version) | `pgstac-migrate current` |
+| `pypgstac load items file --method upsert` | `pgstac load file --policy upsert` |
+| `pypgstac load items --method insert_ignore` | `pgstac load file --policy ignore` |
+| `pypgstac search ''` | `pgstac search ''` |
+| `pypgstac load_queryables q.json` | `pgstac load-queryables q.json` |
+| `pypgstac loadextensions` | `pgstac load-extensions` |
+| `pypgstac runqueue` | `pgstac runqueue` |
+| `pypgstac pgready` | `pg_isready` (or a connect-retry loop) |
+| `pypgstac.hydration.hydrate` | `hydraters.hydrate`, or the `pgstac` read API (server-side hydration) |
+| `pypgstac.hydration.dehydrate` | the Rust loader in `pgstac load` / `pgstac` (dehydration runs in Rust at ingest) |
+| `pypgstac.migrate.MigrationPath` | `pgstac_migrate.compat.MigrationPath` |
+| `pypgstac.__version__` | `pgstac_migrate.__version__` (or the `pypgstac-rs` wheel version) |
+
+## Behavior changes to note
+
+- **Conflict-mode default changed.** `pypgstac load` defaulted to `insert`
+ (fail on duplicate). `pgstac load` defaults to `upsert`. The Rust
+ `--policy` values are `upsert` (default), `ignore`, and `error`; `delsert`
+ semantics are handled by the loader.
+- **Flag renames.** `--method` → `--policy`; `--chunksize` → `--batch-size`.
+ The table is auto-detected from the input, so there is no `items`/`collections`
+ positional argument.
+- **Search engine.** `pgstac search` drives the Rust keyset engine
+ (`search_plan`, client-side band-stepping/hydration/token minting) rather than
+ calling the SQL `search()` function. It gains NDJSON / ItemCollection /
+ geoparquet output formats.
+- **Client-side version gate removed.** The old `Loader.check_version`
+ major/minor pre-check is gone; ingesting against a mismatched schema now fails
+ at the SQL layer instead.
+- **Legacy dehydrated input format dropped.** The tab-delimited dehydrated
+ ndjson (`.pgcopy`/`.txt`) `pypgstac load` accepted is not a `pgstac load`
+ input. The Rust loader dehydrates hydrated input in-process; dump/restore uses
+ stac-geoparquet.
+
+## Migrations
+
+`pgstac-migrate` owns runtime migration planning and apply logic.
```bash
pgstac-migrate migrate --help
+pgstac-migrate migrate # migrate to the latest bundled version
+pgstac-migrate migrate --to 0.9.11
+pgstac-migrate current # print the version installed in the DB
pgstac-migrate plan
pgstac-migrate versions
pgstac-migrate info
@@ -81,199 +80,73 @@ print(result.final_version)
Use `target=None` for latest, or set `target=""`.
-### Running Migrations
-pyPgSTAC has a utility for checking the version of an existing PgSTAC database and applying the appropriate migrations in the correct order. It can also be used to setup a database from scratch.
-
-To create an initial PgSTAC database or bring an existing one up to date, check you have the pypgstac version installed you want to migrate to and run:
-```
-pypgstac migrate
-```
-
-### Bootstrapping an Empty Database
-
-When starting with an empty database, you have two options for initializing PgSTAC:
+`pgstac-migrate` reads the standard PG environment variables (`PGHOST`,
+`PGPORT`, `PGUSER`, `PGDATABASE`, `PGPASSWORD`) and also accepts a
+`--dsn "postgresql://..."`.
-#### Option 1: Execute as Power User
+Migration filenames use canonical PostgreSQL extension naming:
-This approach uses a database user with administrative privileges (such as 'postgres') to run the migration, which will automatically create all necessary extensions and roles:
+- **Base migrations:** `pgstac--.sql`
+- **Incremental migrations:** `pgstac----.sql`
-```bash
-# Set environment variables for database connection
-export PGHOST=localhost
-export PGPORT=5432
-export PGDATABASE=yourdatabase
-export PGUSER=postgres # A user with admin privileges
-export PGPASSWORD=yourpassword
-
-# Run the migration
-pypgstac migrate
-```
+### Bootstrapping an Empty Database
-The migration process will automatically:
-- Create required extensions (postgis, btree_gist, unaccent)
-- Create necessary roles (pgstac_admin, pgstac_read, pgstac_ingest)
-- Set up the pgstac schema and tables
+Running `pgstac-migrate migrate` against an empty database as a privileged user
+(such as `postgres`) creates the required extensions (postgis, btree_gist,
+unaccent), the roles (`pgstac_admin`, `pgstac_read`, `pgstac_ingest`), and the
+`pgstac` schema.
-In production environments, you should assign these roles to your application database user rather than continuing to use the postgres user:
+In production, assign those roles to your application user rather than using the
+`postgres` superuser:
```sql
--- Grant appropriate roles to your application user
GRANT pgstac_read TO your_app_user;
GRANT pgstac_ingest TO your_app_user;
GRANT pgstac_admin TO your_app_user;
-
--- Set the search path for your application user
ALTER USER your_app_user SET search_path TO pgstac, public;
```
-#### Option 2: Create User with Initial Grants
+## Bulk Data Loading
-If you don't have administrative privileges or prefer more control over the setup process, you can manually prepare the database before running migrations.
-
-Connect to your database as an administrator and execute:
-
-```sql
-\c [database]
-
--- Create required extensions
-CREATE EXTENSION IF NOT EXISTS postgis;
-CREATE EXTENSION IF NOT EXISTS btree_gist;
-CREATE EXTENSION IF NOT EXISTS unaccent;
-
--- Create required roles
-CREATE ROLE pgstac_admin;
-CREATE ROLE pgstac_read;
-CREATE ROLE pgstac_ingest;
-
--- Grant appropriate permissions
-ALTER DATABASE [database] OWNER TO [user];
-ALTER USER [user] SET search_path TO pgstac, public;
-ALTER DATABASE [database] set search_path to pgstac, public;
-GRANT CONNECT ON DATABASE [database] TO [user];
-GRANT ALL PRIVILEGES ON TABLES TO [user];
-GRANT ALL PRIVILEGES ON SEQUENCES TO [user];
-GRANT pgstac_read TO [user] WITH ADMIN OPTION;
-GRANT pgstac_ingest TO [user] WITH ADMIN OPTION;
-GRANT pgstac_admin TO [user] WITH ADMIN OPTION;
-```
-
-Then run the migration as your non-admin user:
+Use the `pgstac` Rust CLI (built from `src/pgstac-rs` with the `cli` feature).
+It loads stac-geoparquet, NDJSON, or JSON through the Rust loader (dehydration,
+fragment splitting, and the binary COPY all run in Rust). Collections are loaded
+before items.
```bash
-# Set environment variables for database connection
-export PGHOST=localhost
-export PGPORT=5432
-export PGDATABASE=yourdatabase
-export PGUSER=[user] # Your non-admin user
-export PGPASSWORD=yourpassword
-
-# Run the migration
-pypgstac migrate
+pgstac load items.ndjson # default policy: upsert
+pgstac load items.ndjson --policy ignore # skip conflicting ids
+pgstac load items.ndjson --policy error # fail on duplicate ids
```
-### Verifying Migration
+## Loading Queryables
-To verify that PgSTAC was installed correctly:
+Queryables let clients discover which terms are available for CQL2 filter
+expressions. Load them from a JSON schema file:
```bash
-# Check the PgSTAC version
-pypgstac version
-```
-
-### Bulk Data Loading
-A python utility is included which allows to load data from any source openable by smart-open using python in a memory efficient streaming manner using PostgreSQL copy. There are options for collections and items and can be used either as a command line or a library.
-
-To load an ndjson of items directly using copy (will fail on any duplicate ids but is the fastest option to load new data you know will not conflict)
-```
-pypgstac load items
-```
-
-To load skipping any records that conflict with existing data
-```
-pypgstac load items --method insert_ignore
-```
-
-To upsert any records, adding anything new and replacing anything with the same id
-```
-pypgstac load items --method upsert
-```
-
-### Loading Queryables
-
-Queryables are a mechanism that allows clients to discover what terms are available for use when writing filter expressions in a STAC API. The Filter Extension enables clients to filter collections and items based on their properties using the Common Query Language (CQL2).
-
-To load queryables from a JSON file:
-
-```
-pypgstac load_queryables queryables.json
-```
-
-To load queryables for specific collections:
-
-```
-pypgstac load_queryables queryables.json --collection_ids [collection1,collection2]
-```
-
-To load queryables and delete properties not present in the file:
-
-```
-pypgstac load_queryables queryables.json --delete_missing
-```
-
-To load queryables and create indexes only for specific fields:
-
-```
-pypgstac load_queryables queryables.json --index_fields [field1,field2]
+pgstac load-queryables queryables.json
+pgstac load-queryables queryables.json --collection-ids collection1,collection2
+pgstac load-queryables queryables.json --delete-missing
+pgstac load-queryables queryables.json --index-fields field1,field2
```
-By default, no indexes are created when loading queryables. Using the `--index_fields` parameter allows you to selectively create indexes only for fields that require them. Creating too many indexes can degrade database performance, especially for write operations, so it's recommended to only index fields that are frequently used in queries.
+By default no indexes are created. Use `--index-fields` to selectively index the
+fields you filter on frequently; over-indexing degrades write performance.
-When using `--delete_missing` with specific collections, only properties for those collections will be deleted:
+The JSON file should follow the queryables schema described in the
+[STAC API - Filter Extension](https://github.com/stac-api-extensions/filter#queryables).
-```
-pypgstac load_queryables queryables.json --collection_ids [collection1,collection2] --delete_missing
-```
+## Automated Collection Extent Updates
-You can combine all parameters as needed:
-
-```
-pypgstac load_queryables queryables.json --collection_ids [collection1,collection2] --delete_missing --index_fields [field1,field2]
-```
+Setting `pgstac.update_collection_extent` to `true` enables a trigger that
+adjusts collection spatial/temporal extents as items are ingested. To reduce
+load-transaction overhead, combine it with `pgstac.use_queue`, which defers the
+work to a queue drained by:
-The JSON file should follow the queryables schema as described in the [STAC API - Filter Extension](https://github.com/stac-api-extensions/filter#queryables). Here's an example:
-
-```json
-{
- "$schema": "https://json-schema.org/draft/2019-09/schema",
- "$id": "https://example.com/stac/queryables",
- "type": "object",
- "title": "Queryables for Example STAC API",
- "description": "Queryable names for the Example STAC API",
- "properties": {
- "id": {
- "description": "Item identifier",
- "type": "string"
- },
- "datetime": {
- "description": "Datetime",
- "type": "string",
- "format": "date-time"
- },
- "eo:cloud_cover": {
- "description": "Cloud cover percentage",
- "type": "number",
- "minimum": 0,
- "maximum": 100
- }
- },
- "additionalProperties": true
-}
+```bash
+pgstac runqueue # runs CALL run_queued_queries()
```
-The command will extract the properties from the JSON file and create queryables in the database. It will also determine the appropriate property wrapper based on the type of each property and create the necessary indexes.
-
-### Automated Collection Extent Updates
-
-By setting `pgstac.update_collection_extent` to `true`, a trigger is enabled to automatically adjust the spatial and temporal extents in collections when new items are ingested. This feature, while helpful, may increase overhead within data load transactions. To alleviate performance impact, combining this setting with `pgstac.use_queue` is beneficial. This approach necessitates a separate process, such as a scheduled task via the `pg_cron` extension, to periodically invoke `CALL run_queued_queries();`. Such asynchronous processing ensures efficient transactional performance and updated collection extents.
-
-*Note: The `pg_cron` extension must be properly installed and configured to manage the scheduling of the `run_queued_queries()` function.*
+Schedule `pgstac runqueue` (or `CALL run_queued_queries();` via `pg_cron`) to
+process the queue asynchronously.
diff --git a/docs/src/release-notes.md b/docs/src/release-notes.md
deleted file mode 120000
index 699cc9e7..00000000
--- a/docs/src/release-notes.md
+++ /dev/null
@@ -1 +0,0 @@
-../../CHANGELOG.md
\ No newline at end of file
diff --git a/docs/src/release-notes.md b/docs/src/release-notes.md
new file mode 100644
index 00000000..690757b5
--- /dev/null
+++ b/docs/src/release-notes.md
@@ -0,0 +1,830 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/)
+and this project adheres to [Semantic Versioning](http://semver.org/).
+
+
+## [Unreleased]
+
+### Added
+
+- New [Promoted Fields](https://stac-utils.github.io/pgstac/promoted-fields/) reference document listing every STAC property that pgstac promotes to a native `items` column, with spec source, extension version, SQL type, and a machine-readable YAML registry for AI-assisted updates.
+- Align promoted fields with current STAC extension specs: add `proj:geometry`, `view:moon_azimuth`, `view:moon_elevation`, `sat:platform_international_designator`, `sat:anx_datetime`; replace `proj:epsg` (int) with `proj:code` (text); move `eo:bands` to core `bands` (STAC 1.1); remove `file:values_regex`.
+- Add deterministic SHA-256 `content_hash` to STAC items to track data changes across migrations.
+- Add `pgstac_updated_at` column to items table as part of separating STAC property updates from database metadata updates.
+- Deterministic Planetary Computer benchmark fixture manifest + fetch tooling for `naip`, `sentinel-2-l2a`, and `landsat-c2-l2` (1000 items per collection), plus CI/manual benchmark workflows that emit JSON/CSV/Markdown artifacts and branch comparison reports.
+- `src/pgstac-rs` read/streaming engine: a Rust-side keyset search that drives `search_plan`/`collection_search_plan` and does the band stepping, hydration, token minting, and the STAC `fields` include/exclude projection client-side (page-equivalent to SQL `search()`); a flat-memory streaming iterator (`query_raw` portal on one pooled connection + per-new-fragment fetch on a parallel connection, so memory stays flat regardless of result size); byte-identical Rust hydration (EWKB→GeoJSON, serialize-time fragment-asset merge that never deep-parses large asset arrays, `bbox` emitted from raw bytes to preserve numeric precision); collection search, item/collection getters, and queryables; a pooled hydration-invariant cache; and a parallel-context primitive.
+- `pgstac` CLI (`src/pgstac-rs`, `cli` feature): `dump` a pgstac instance to a directory / `*.tar(.zst)` / S3 (object-store) / stdout as fully-hydrated stac-geoparquet (one file per partition) + collection/queryables/settings JSON + a sha256'd manifest, with collection/datetime/bbox prefilters, parallel and consistent-snapshot modes, and dry-run; `search` to stream results off the keyset engine, selecting the format with rustac's `stac_io::Format` spelling — `--format ndjson` (default), `json` (one ItemCollection page), or `geoparquet[]` (compression carried in the format string, e.g. `geoparquet[snappy]`).
+- `pgstac` Python wheel (`src/pgstac-rs`, `python` feature, built with maturin): a pyo3 extension exposing the async read API (search, collection search, getters, queryables, parallel match count) over a startup-created connection pool, for use in `stac-fastapi-pgstac`.
+- New [Rust client & CLI](https://stac-utils.github.io/pgstac/pgstac-rs/) reference documenting the crate features, the full read/pool API, every `pgstac dump` / `pgstac search` option, and all connection + test environment variables.
+- `src/pgstac-rs`: a `store` cargo feature that gates the `object_store`-backed remote dump sink (local fs + S3/GCS/Azure), so the remote sink can be built without the full `cli` feature.
+
+### Removed
+
+- Removed the legacy `pypgstac` Python package (`src/pypgstac`). Every capability moved to a replacement: `pgstac-migrate` (migrations), the `pgstac` Rust CLI (`src/pgstac-rs`, `cli` feature — loading, search, queryables, extensions, run-queue, maintenance), and the `pgstac` Python wheel (`pgstac` module — the read/write pool API). Previously published `pypgstac` releases remain on PyPI; no new versions are published. Migration reference:
+ - `pypgstac migrate --toversion X` → `pgstac-migrate migrate --to X`
+ - `pypgstac version` → `pgstac-migrate current`
+ - `pypgstac load items f --method upsert` → `pgstac load f --policy upsert` (default policy changed from `insert` to `upsert`; `--method`→`--policy`, `--chunksize`→`--batch-size`; table is auto-detected)
+ - `pypgstac search` → `pgstac search` (Rust keyset engine; adds NDJSON / ItemCollection / geoparquet output)
+ - `pypgstac load_queryables f.json` → `pgstac load-queryables f.json`
+ - `pypgstac loadextensions` → `pgstac load-extensions`
+ - `pypgstac runqueue` → `pgstac runqueue`
+ - `pypgstac pgready` → `pg_isready`
+ - `pypgstac.hydration.{hydrate,dehydrate}` → `hydraters` / the `pgstac` read API (hydration and dehydration run in Rust)
+ - `pypgstac.migrate.MigrationPath` → `pgstac_migrate.compat.MigrationPath`
+ - `pypgstac.__version__` → `pgstac_migrate.__version__`
+- Removed the `pypgstac` PyPI publish job and the `-pypgstac` / `-pypgstac-runtime` Docker image build jobs from the release workflow; added a `pypgstac-rs` (maturin) PyPI publish job. The `pgstac-migrate` and crates.io publish jobs are unchanged.
+
+### Changed
+
+- `search_plan`: bake the collection clamp as a literal so the datetime-band query is parameterized only by `$1`/`$2`/`$3` (band low/high, limit) and can be prepared once by a streaming client; fix the non-datetime branch's datetime clamp to be exclusive of the next month (`< months[last] + 1 month`) so items dated after the start of the final month are not dropped.
+- `fields_to_itemcols`: emit excluded heavy columns as `NULL::type AS
` (named) instead of an unaliased `NULL::type`, so a client preparing the `search_plan` query keeps a stable result schema across any `fields` value while still never transferring the heavy value. It also nulls `fragment_id` in the projection when the requested fields need no shared fragment (`needs_fragment` is false), so a client driving the `search_plan` query skips the per-row `item_fragments` lookup entirely — the fragment-skip rides in the returned projection rather than a separate flag.
+- Replaced expensive row-based trigger for item inserts with optimized SQL/PLPGSQL hydration strategies to improve ingestion throughput.
+- Update pypgstac loaders to dynamically generate hashes during ingestion where required, avoiding trigger recalculation.
+- Add tombstone table `items_deleted_log` and `pgstac_updated_at` metadata column to items table.
+- Add batched tombstone GC routines: `gc_deleted_items_log_batch(interval, integer)`, overloaded `gc_deleted_items_log(interval, integer)`, and `gc_deleted_items_log_committed(interval, integer)` for commit-per-batch cleanup of large tombstone backlogs.
+- Add PGTap coverage for batched tombstone GC signatures/behavior and read-only rejection paths.
+- New `pgstac-migrate` package under `src/pgstac-migrate/` with a standalone
+ CLI, Python API, and tests for migration planning and execution.
+- New Rust crate under `src/pgstac-rs/` with updated CI/release wiring,
+ README guidance, and test coverage.
+- `src/pgstac/pyproject.toml` `tool.pgpkg` project metadata for canonical SQL +
+ migration staging.
+- `scripts/makemigration` host wrapper for the in-container `makemigration` helper.
+- `.env.example` documenting all supported environment variables for local development.
+- All host-facing scripts (`test`, `format`, `migrate`, `server`, `stageversion`,
+ `runinpypgstac`, `console`) now accept `--help` / `-h` and honor environment-variable
+ counterparts for common flags (`PGSTAC_BUILD_POLICY`, `PGSTAC_FAST`, `PGSTAC_WATCH`,
+ `PGSTAC_STRICT`).
+- `scripts/pgstacenv` gains `ensure_env_file` (auto-creates `.env` from `.env.example`
+ on first run) and `first_available_pgport` (avoids port collisions on shared machines).
+- `scripts/test` expanded from a 3-line wrapper to a full-featured test runner supporting
+ `--fast`, `--watch`, `--build-policy`, `--no-strict`, and stale-image detection.
+- PostgreSQL 16, 17, and 18-beta added to the CI and Docker build matrix. (Closes #334)
+- Weekly scheduled CI run (`cron: '23 4 * * 0'`) to catch upstream base-image CVEs without
+ requiring a code change. (Closes #202)
+- `workflow_dispatch` trigger for manual CI runs.
+- `pg_tle` v1.5.2 built and pre-loaded in the `pgstacbase` image; database init runs
+ `CREATE EXTENSION IF NOT EXISTS pg_tle`.
+- `pg_stat_statements` and `pg_cron` are now installed in the pgstac Docker image,
+ added to `shared_preload_libraries`, and initialized during container bootstrap
+ (`pg_stat_statements` in the app database, `pg_cron` in `postgres`).
+- `scripts/container-scripts/test` now includes extension smoke tests that verify
+ preload configuration plus basic runtime behavior for both
+ `pg_stat_statements` and `pg_cron`.
+- `pypgstac-runtime` Docker target: slim Python 3.13-trixie image without the Rust/build
+ toolchain, for production deployments where the Rust build environment is not needed.
+- Dependabot coverage expanded to Docker base images and pip packages (two new
+ ecosystems with grouped update policies).
+
+### Changed
+- `pypgstac migrate` now delegates runtime migration planning and apply logic to
+ `pgstac-migrate`; `src/pypgstac/src/pypgstac/migrate.py` remains as a
+ compatibility wrapper.
+- Migration filenames are now canonicalized to
+ `pgstac--.sql` / `pgstac----.sql` in
+ `src/pgstac/migrations/` and `src/pypgstac/src/pypgstac/migrations/`.
+- `scripts/container-scripts/stageversion` and
+ `scripts/container-scripts/makemigration` now shell through `pgpkg`
+ (`uv run --no-project --with "pgpkg>=0.1.1,<0.2"` and
+ `uv run --no-project --with "pgpkg[diff]>=0.1.1,<0.2"`) with optional
+ `PGPKG_REPO_DIR` override support.
+- `scripts/runinpypgstac` now supports a `PGPKG_LOCAL_REPO_DIR` mount override
+ for local pgpkg development while keeping the default flow PyPI-first.
+- Search cache hashing, storage, and concurrency control were reworked: SHA-256
+ cache keys, canonical where-clause inputs, `searches`-backed lifecycle,
+ retention-driven GC, and less blocking row touch / update behavior.
+- Search context stats updates now use optimistic compare-and-update guards on
+ `statslastupdated`, reducing stale overwrites when concurrent workers refresh
+ counts.
+- GitHub Actions and release automation were refreshed for the current layout:
+ Rust crate path updates, workflow/action version bumps, and Dependabot group
+ adjustments.
+- Tagged releases now publish the new `pgstac-migrate` package to PyPI alongside `pypgstac` via trusted publishing in `.github/workflows/release.yml`.
+- In-container helper scripts moved from `docker/pypgstac/bin/` to
+ `scripts/container-scripts/`; container `PATH` updated accordingly.
+- `docker/pgstac/Dockerfile` and `docker/pypgstac/Dockerfile` base images updated from
+ `bullseye` to `trixie`. (Closes #231)
+- All Docker `RUN` layers now use BuildKit cache mounts for apt, uv, and git caches,
+ significantly reducing incremental rebuild times.
+- `docker-compose.yml`: adds `env_file: .env`, explicit `PGHOST`/`PGPORT` defaults,
+ a pgstac healthcheck, and a `service_healthy` dependency on pypgstac.
+- `runinpypgstac` gains `--build-policy {always,missing,never}` replacing the bare
+ `--build` flag; `PGSTAC_BUILD_POLICY` env var provides a persistent default.
+- Dev tooling: `flake8`, `black`, and `mypy` removed in favour of `ruff==0.15.11` and
+ `ty==0.0.31`. `pre-commit` pinned to `3.5.0`. `pre-commit-hooks` updated to v5.0.0.
+- `cachetools` upper bound removed (`cachetools>=5.3.0`) since `pypgstac` only uses
+ `cachetools.func.lru_cache`; no known incompatible API changes affect this usage.
+- `pypgstac` developer tooling config now consistently targets Ruff + ty:
+ removes stale mypy config, pins Ruff to `0.15.11` to match pre-commit,
+ and adds minimal `[tool.ty]` project settings.
+- `pypgstac` now requires Python 3.11+ and advertises support through 3.14;
+ settings now use `pydantic-settings` and require `pydantic>=2,<3`.
+- Formatting/type-check pipeline now uses `scripts/test --formatting` as the
+ single pre-commit entry point (removing duplicate direct Ruff pre-commit hooks)
+ and aligns Ruff line-length handling with the formatter (`E501` ignored;
+ explicit `line-length = 88`).
+- GitHub Actions and release automation were refreshed for the current layout:
+ Rust crate path updates, `dorny/paths-filter` v2→v3,
+ `docker/build-push-action` v4→v6, `astral-sh/setup-uv` v8.0.0→v8.1.0,
+ refreshed SHA pins, and Dependabot group updates (`actions-all` replaces
+ `minor-and-patch`, with new `docker-base-images`, `python-dev-tooling`, and
+ `python-runtime` groups).
+- `docker-compose.yml` removes explicit `container_name` entries to avoid conflicts
+ between concurrent local instances.
+
+### Removed
+- PL/Rust support: `pgstacbase-plrust` and `pgstac-plrust` Docker targets removed; the
+ pgstac image no longer builds or ships PL/Rust or the Rust toolchain. (Closes #339)
+- `flake8`, `black`, and `mypy` removed from dev dependencies.
+
+### Fixed
+- Explicit search stats refresh now propagates through cached and uncached search paths when `updatestats` is requested, keeping `numberMatched`/context counts current.
+- `scripts/container-scripts/test` now refreshes collation metadata for the
+ `postgres` database during setup to avoid noisy warning output.
+- Read-only search with context now returns `numberMatched` without requiring
+ cache writes, reducing failure risk for replica/read-only deployments.
+- `load.py`: Use timezone-aware `MIN_DATETIME_UTC` / `MAX_DATETIME_UTC` sentinel
+ constants (instead of naive `datetime.min` / `datetime.max`) to avoid
+ `TypeError: can't compare offset-naive and offset-aware datetimes`.
+- CI `lowest-direct` dependency install: avoid uv cache permission failures by using
+ a writable temp cache path in the test job install step.
+- `pypgstac` dependency floor for `orjson` raised to `>=3.11.0` to avoid selecting
+ the broken `3.9.0` sdist under `--resolution lowest-direct`.
+- `pydantic` minimum raised to `>=2.10` so `--resolution lowest-direct` on Python 3.13
+ does not resolve to `pydantic-core==2.0.1`, which fails to build.
+- `scripts/container-scripts/test` now derives the active database from
+ `PGDATABASE`/`POSTGRES_DB` when checking server extensions and refreshing
+ collation versions, instead of assuming `postgis`.
+
+
+## [v0.9.11]
+
+
+### Fixed
+- Fix timestamp regex in partition constraint parsing to handle fractional seconds (microseconds), preventing incorrect `(-infinity, infinity)` constraint bounds.
+- Add explicit ANALYZE before `st_estimatedextent()` in `update_partition_stats` for deterministic spatial extent calculation.
+- Consolidate materialized view refreshes in `update_partition_stats` to a single unconditional refresh, reducing redundant operations.
+- Use `partition_sys_meta` (live VIEW) instead of `partitions` (stale MATERIALIZED VIEW) in loader `_partition_update()` for real-time partition bounds.
+- Expand loader retry to 10 attempts and add `SerializationFailure`, `LockNotAvailable`, `ObjectInUse` to retryable exceptions.
+- Add `before_sleep` retry handler to force partition constraint refresh on `CheckViolation`.
+- Materialize `itertools.groupby` generators with `list()` before `load_partition()` to prevent silent data loss on retry.
+- Use safe `item.pop('partition', None)` to avoid `KeyError` on retry.
+- Inline `search_tohash` into `search_hash` to eliminate cross-function dependency that broke pg_dump/pg_restore (pg_dump orders functions alphabetically).
+
+### Added
+- `pgstac_restore` script for restoring pg_dump backups — installs a temporary event trigger to fix search_path during restore.
+- Race condition tests for sequential and concurrent loader operations with new-Loader-per-item pattern.
+- Search path independence tests verifying `partition_sys_meta`, `partition_stats`, `partitions`, `partitions_view`, and `partition_steps` work identically with and without `pgstac` in `search_path`.
+- pg_dump/pg_restore test (`--pgdump`) validating backup and restore of a pgstac database with sample data.
+- Documentation for pg_dump/pg_restore best practices with PgSTAC.
+
+## [v0.9.10]
+
+### Fixed
+- Improved performance and correctness of partition constraint parsing.
+
+##[v0.9.9]
+
+### Changed
+* changed container images to use non-root `user`
+* fix bug where closed and broken db connections are reused.
+
+### Fixed
+* replace space-separated terms with adjacency operator in free-text search (#387)
+
+## [v0.9.8]
+
+### Fixed
+- Allow array as q parameter for full text search
+
+
+## [v0.9.7]
+
+### Fixed
+- Fix bad handling of leading +/- terms in free-text search
+- Use consistent tsquery config in free-text search
+
+## [v0.9.6]
+
+### Added
+
+- Add `load_queryables` function to pypgstac for loading queryables from a JSON file
+- Add support for specifying collection IDs when loading queryables
+
+### Fixed
+- Added missing 0.8.6-0.9.0 migration script
+
+## [v0.9.5]
+
+### Changed
+
+ - Pin to `plpygic>=0.5.0` and use `geom.ewkb` instead of `geom.wkt` when formatting items in `Loader.format_item`. Fixes (#357)
+
+## [v0.9.4]
+
+### Changed
+ - Relax pypgstac dependencies
+
+## [v0.9.3]
+
+### Fixed
+
+- Fix CI issue with tests not running
+- Fix for issue with nulls in title or keywords for free text search
+
+### Changed
+
+- Replace hardcoded org name in CI
+
+## [v0.9.2]
+
+### Added
+
+- Add limited support for free-text search in the search functions. (Fixes #293)
+ - the `q` parameter is converted from the
+ [OGC API - Features syntax](https://docs.ogc.org/DRAFTS/24-031.html) into a `tsquery`
+ statement which is used to compare to the description, title, and keywords fields in items or collection_search
+ - the text search is un-indexed and will be very slow for item-level searches!
+ - Add support for Postgres 17
+ - Support for adding data to the private field using the pypgstac loader
+
+### Fixed
+
+- Add `open=True` in `psycopg.ConnectionPool` to avoid future behavior change
+- Switch from postgres `server_version` to `server_version_num` to get PG version (Fixes #300)
+- Allow read-only replicas work even when the context extension is enabled (Fixes #300)
+- Consistently ensure use of instantiated postgres fields when addressing with 'properties.' prefix
+
+### Changed
+- Move rust hydration to a separate repo
+
+## [v0.9.1]
+
+### Fixed
+
+- Fixed double nested extent when using trigger based update collection extent. (Fixes #274)
+- Fix time formatting (Fixes #275)
+- Relaxes smart-open dependency check (Fixes #273)
+- Switch to uv for docker image
+
+## [v0.9.0]
+
+### Breaking Changes
+
+- Context Extension has been deprecated. Context is now reported using OGC Features compliant numberMatched and numberReturned
+- Paging return from search using prev/next properties has been deprecated. Paging is now available in the spec compliant Links
+
+### Added
+
+- Add support for Casei and Accenti (Fixes #237). (Also, requires the addition of the unaccent extension)
+- Add numberReturned and numberMatched fields for ItemCollection. BREAKING CHANGE: As the context extension is deprecated, this also removes the "context" item from results.
+- Updated docs on automated updates of collection extents. (CLOSES #247)
+- stac search now returns paging information using standards compliant links rather than prev/next properties (Fixes #265)
+
+### Fixed
+
+- Fixes issue when there is a None rather than an empty dictionary in hydration.
+- Use "debug" log level rather than "log" to prevent growth in log messages due to differences in how client_min_messages and log_min_messages treat log levels. (Fixes #242)
+- Refactor search_query and search_where functions to eliminate race condition when running identical queries. (Fixes #233)
+- Fixes CQL2 Parser for Between operator (Fixes #251)
+- Update PyO3 for rust hydration performance improvements.
+
+## [v0.8.6]
+
+### Fixed
+
+ - Relax version requirement for smart-open (Fixes #273)
+ - Use uv pip in docker build
+
+## [v0.8.5]
+
+### Fixed
+
+- Fix issue when installing or migrating pgstac using a non superuser (particularly when using the default role found on RDS). (FIXES #239). Backports fix into migrations for 0.8.2, 0.8.3, and 0.8.4.
+- Adds fixes/updates to documentation
+- Fixes issue when using geometry with the strict queryables setting set.
+
+## [v0.8.4]
+
+### Fixed
+
+- Make release deployment use postgres images without plrust
+- Update versions of plrust in dockerfile (used for development, there is no plrust code yet)
+- Update incremental migration tests to start at v0.3.0 rather than v0.1.9 due to a breaking change in pg_partman at version 5 that has no ability to pin a version. Migrating from prior to v0.3.0 should still work fine as long as pg_partman has not been updated on the database.
+
+## [v0.8.3]
+
+### Added
+
+- Add support for arm64 to Docker images
+
+### Fixed
+
+- Fixes a critical bug when using the ingest_staging_upsert table or the upsert_item/upsert_items functions to update records with existing data where the existing row would get deleted, but the new row would not get added.
+
+## [v0.8.2]
+
+### Added
+
+- Add support functions and tests for Collection Search
+- Add configuration parameter for base_url to be able to generate absolute links
+- With this release, this is only used to create links for paging in collection_search
+- Adds read only mode to allow use of pgstac on read replicas
+- Note: Turning on romode disables any caching (particularly when context is turned on) and does not allow to store q query hash that can be used with geometry_search.
+- Add option to pypgstac loader "--usequeue" that forces use of the query queue for the loading process
+- Add "pypgstac runqueue" command to run any commands that are set in the query queue
+
+### Fixed
+
+- Fix bug with end_datetime constraint management leading to inability to add data outside of constraints
+- Fix bugs dealing with table ownership to ensure that all pgstac tables are owned by the pgstac_admin role
+- Fixes issues with errors/warnings caused when doing index maintenance
+- Fixes issues with errors/warnings caused with partition management
+- Make sure that pgstac_ingest role always has read/write permissions on all tables
+- Remove call to create_table_constraints from check_partition function. create_table_constraints was being called twice as it also gets called from update_partition_stats
+- Add NOT NULL constraint to collections table (FIXES #224)
+- Fix issue with indexes not getting created as the pg_admin role using SECURITY DEFINER
+
+### Changed
+
+- Revert pydantic requirement back to '>=1.7' and use basesettings conditionally from pydantic or pydantic.v1 to allow compatibility with pydantic 2 as well as with stac-fastapi that requires pydantic <2
+
+## [v0.8.1]
+
+### Fixed
+
+- Fix issue with CI building/pushing docker images
+
+## [v0.8.0]
+
+### Fixed
+
+- Revert an optimisation which limited the number of results from a search query to the number of item IDs specified in the query.
+This fixes an issue where items with the same ID that are in multiple collections could be left out of search results.
+
+### Changed
+
+- update `pydantic` requirement to `~=2.0`
+- update docker and ci workflows to build binary wheels for rust additions to pypgstac
+- split docker into database service and python/rust container
+- Modify scripts to auto-generate unreleased migration
+- Add pre commit tasks to generate migration and to rebuild and compile pypgstac with maturin for rust
+- Add private jsonb column to items and collections table to hold private metadata that should not be returned as part of a stac item
+- Add generated columns to collections with the bounding box as a geometry and the datetime and end_datetime from the extents (this is to help with forthcoming work on collections search)
+- Add PLRust to the Docker postgres image for forthcoming work to add optional PLRust functions for expensive json manipulation (including hydration)
+- Remove default queryable for eo:cloud_cover
+
+## [v0.7.10]
+
+### Fixed
+
+- Return an empty jsonb array from all_collections() when the collections table is empty, instead of NULL. Fixes #186.
+- Add delete trigger to collections to clean up partition_stats records and remove any partitions. Fixes #185
+- Fixes boolean casting in get_setting_bool function
+
+## [v0.7.9]
+
+### Fixed
+
+- Update docker image to use postgis 3.3.3
+
+## [v0.7.8]
+
+### Fixed
+
+- Fix issue with search_query not returning all fields on first use of a query. Fixes #182
+
+## [v0.7.7]
+
+### Fixed
+
+- Fix migrations for 0.7.4->0.7.5 and 0.7.5->0.7.6 to use the partition_view rather than the materialized view to avoid issue with refreshing the materialized view when run in the same statement that is accessing the view. Fixes #180.
+
+### Added
+
+- Add a short cirucit for id searches that sets the limit to be no more than the number of ids in the filter.
+- Add 'timing' configuration variable that adds a "timing" element to the return object with the amount of time that it took to return a search.
+- Reduce locking when updating statistics in the search table. Use skip locked to skip updating last_used and count when there is a lock being held.
+
+## [v0.7.6]
+
+### Fixed
+
+- Fix issue with checking for existing collections in queryable trigger function that prevented adding scoped queryable entries.
+
+## [v0.7.5]
+
+### Fixed
+
+- Default sort not getting set when sortby not included in query with token (Fixes [#177](https://github.com/stac-utils/pgstac/issues/177))
+- Fixes regression in performance between with changes for partition structure at v0.7.0. Changes the normal view for partitions and partition_steps into indexed materialized views. Adds refreshing of the views to existing triggers to make sure they stay up to date.
+
+## [v0.7.4]
+
+### Added
+
+- Add --v and --vv options to scripts/test to change logging to notice / log when running tests.
+- Add framework for option to cache expensive item formatting/hydrating calls. Note: this only provides functionality to add and read from the cached calls, but does not have any wiring to remove any entries from the cache.
+- Update the costs for json formatting functions to 5000 to help the query planner choose to prefer using indexes on json fields.
+
+### Fixed
+
+- Fix bug in foreign key and unique collection detection in queryables trigger function, update tests to catch.
+- Add collection id to tokens to ensure uniqueness and improve speed when looking up token values. Update tests to use the new keys. Old item id only tokens are still valid, but new results will all contain the new keys.
+- Improve performance when looking for whether next/prev links should be added.
+- Update Search function to remove the use of cursors and temp tables.
+- Update get_token_filter to remove the use of temp tables.
+
+## [v0.7.3]
+
+### Fixed
+
+- Use IF EXISTS when dropping constraints to avoid race conditions
+- Rework function that finds indexes that need to be added to be added and to find functionally identical indexes better.
+
+## [v0.7.2]
+
+### Fixed
+
+- Use version_parser for parsing versions in pypgstac
+- Fix issue with dropping functions/procedures in 0.6.13->0.7.0 migrations
+- Fix issue with CREATE OR REPLACE TRIGGER on PG 13
+- Fix issue identifying duplicate indexes in maintain_partition_queries function
+- Ensure that pgstac_read role has read permissions to all partitions
+- Fix issue (and add tests) caused by bug in psycopg datetime types not being able to translate 'infinity', '-infinity'
+
+## [v0.7.1]
+
+### Fixed
+
+- Fix permission issue when running incremental migrations.
+- Make sure that pypgstac migrate runs in a single transaction
+- Don't try to use concurrently when building indexes by default (this was tripping things up when using with pg_cron)
+- Don't short circuit search for requests with ids (Fixes #159)
+- Fix for issue with pagination when sorting by columns with nulls (Fixes #161 Fixes #152)
+- Fixes issue where duplicate datetime,end_datetime index was being built.
+- Fix bug in pypgstac loader when using delsert option
+
+### Added
+
+- Add trigger to detect duplicate configurations for name/collection combination in queryables
+- Add trigger to ensure collections added to queryables exist
+- Add tests for queryables triggers
+- Add more tests for different pagination scenarios
+
+## [v0.7.0]
+
+### Added
+
+- Reorganize code base to create clearer separation between pgstac sql code and pypgstac.
+- Move Python tooling to use hatch with all python project configuration in pyproject.toml
+- Rework testing framework to not rely on pypgstac or migrations. This allows to run tests on any code updates without creating a version first. If a new version has been staged, the tests will still run through all incremental migrations to make sure they pass as well.
+- Add pre-commit to run formatting as well as the tests appropriate for which files have changed.
+- Add a query queue to allow for deferred processing of steps that do not change the ability to get results, but enhance performance. The query queue allows to use pg_cron or similar to run tasks that are placed in the queue.
+- Modify triggers to allow the use of the query queue for building indexes, adding constraints that are used solely for constraint exclusion, and updating partition and collection spatial and temporal extents. The use of the queue is controlled by the new configuration parameter "use_queue" which can be set as the pgstac.use_queue GUC or by setting in the pgstac_settings table.
+- Reorganize how partitions are created and updated to maintain more metadata about partition extents and better tie the constraints to the actual temporal extent of a partition.
+- Add "partitions" view that shows stats about number of records, the partition range, constraint ranges, actual date range and spatial extent of each partition.
+- Add ability to automatically update the extent object on a collection using the partition metadata via triggers. This is controlled by the new configuration parameter "update_collection_extent" which can be set as the pgstac.update_collection_extent GUC or by setting in the pgstac_settings table. This can be combined with "use_queue" to defer the processing.
+- Add many new tests.
+- Migrations now make sure that all objects in the pgstac schema are owned by the pgstac_admin role. Functions marked as "SECURITY DEFINER" have been moved to the lower level functions responsible for creating/altering partitions and adding records to the search/search_wheres tables. This should open the door for approaches to using Row Level Security.
+- Allow pypgstac loader to load data on pgstac databases that have the same major version even if minor version differs. [162] () Cherry picked from .
+
+### Fixed
+
+- Allow empty strings in datetime intervals
+- Set search_path and application_name upon connection rather than as kwargs for compatibility with RDS [156] ()
+
+## [v0.6.13]
+
+### Fixed
+
+- Fix issue with sorting and paging where in some circumstances the aggregation of data changed the expected order
+
+## [v0.6.12]
+
+### Added
+
+- Add ability to merge enum, min, and max from queryables where collections have different values.
+- Add tooling in pypgstac and pgstac to add stac_extension definitions to the database.
+- Modify missing_queryables function to try to use stac_extension definitions to populate queryable definitions from the stac_extension schemas.
+- Add validate_constraints procedure
+- Add analyze_items procedure
+- Add check_pgstac_settings function to check system and pgstac settings.
+
+### Fixed
+
+- Fix issue with upserts in the trigger for using the items_staging tables
+- Fix for generating token query for sorting. [152] ()
+
+## [v0.6.11]
+
+### Fixed
+
+- update pypgstac requirements to support python 3.11 [142](https://github.com/stac-utils/pgstac/pull/142)
+- rename pgstac setting `default-filter-lang` to `default_filter_lang` to allow pgstac on postgresql>=14
+
+## [v0.6.10]
+
+### Fixed
+
+- Makes sure that passing in a non-existing collection does not return a queryable object.
+
+## [v0.6.9]
+
+### Fixed
+
+- Set cursor_tuple_fraction to 1 in search function to let query planner know to expect the entire table result within the search function to be returned. The default cursor_tuple_fraction of .1 within that function was at times creating bad query plans leading to slow queries.
+
+## [v0.6.8]
+
+### Added
+
+- Add get_queryables function to return a composite queryables json for either a single collection (text), a list of collections(text[]), or for the full repository (null::text).
+- Add missing_queryables(collection text, tablesample int) function to help identify if there are any properties in a collection without entries in the queryables table. The tablesample parameter is an int <=100 that is the approximate percentage of the collection to scan to look for missing queryables rather than reading every item.
+- Add missing_queryables(tablesample int) function that scans all collections using a sample of records to identify missing queryables.
+
+## [v0.6.7]
+
+### Added
+
+- Add get_queryables function to return a composite queryables json for either a single collection (text), a list of collections(text[]), or for the full repository (null::text).
+- Add missing_queryables(collection text, tablesample int) function to help identify if there are any properties in a collection without entries in the queryables table. The tablesample parameter is an int <=100 that is the approximate percentage of the collection to scan to look for missing queryables rather than reading every item.
+- Add missing_queryables(tablesample int) function that scans all collections using a sample of records to identify missing queryables.
+
+## [v0.6.6]
+
+### Added
+
+- Add support for array operators in CQL2 (a_equals, a_contains, a_contained_by, a_overlaps).
+- Add check in loader to make sure that pypgstac and pgstac versions match before loading data [#123](https://github.com/stac-utils/pgstac/issues/123)
+
+## [v0.6.5]
+
+### Fixed
+
+- Fix for type casting when using the "in" operator [#122](https://github.com/stac-utils/pgstac/issues/122)
+- Fix failure of pypgstac load for large items [#121](https://github.com/stac-utils/pgstac/pull/121)
+
+## [v0.6.4]
+
+### Fixed
+
+- Fixed casts for numeric data when a property is not in the queryables table to use the type from the incoming json filter
+- Fixed issue loader grouping an unordered iterable by partition, speeding up loads of items with mixed partitions [#116](https://github.com/stac-utils/pgstac/pull/116)
+
+## [v0.6.3]
+
+### Fixed
+
+- Fixed content_hydrate argument ordering which caused incorrect behavior in database hydration [#115](https://github.com/stac-utils/pgstac/pull/115)
+
+### Added
+
+- Skip partition updates when unnecessary, which can drastically improve large ingest performance into existing partitions. [#114](https://github.com/stac-utils/pgstac/pull/114)
+
+## [v0.6.2]
+
+### Fixed
+
+- Ensure special keys are not in content when loaded [#112](https://github.com/stac-utils/pgstac/pull/112/files)
+
+## [v0.6.1]
+
+### Fixed
+
+- Fix issue where using equality operator against an array was only comparing the first element of the array
+
+## [v0.6.0]
+
+### Fixed
+
+- Fix function signatures for transactional functions (delete_item etc) to make sure that they are marked as volatile
+- Fix function for getting start/end dates from a stac item
+
+### Changed
+
+- Update hydration/dehydration logic to make sure that it matches hydration/dehydration in pypgstac
+- Update fields logic in pgstac to only use full paths and to match logic in stac-fastapi
+- Always include id and collection on features regardless of fields setting
+
+### Added
+
+- Add tests to ensure that pgstac and pypgstac hydration logic is equivalent
+- Add conf item to search to allow returning results without hydrating. This allows an application using pgstac to shift the CPU load of rehydrating items from the database onto the application server.
+- Add "--dehydrated" option to loader to be able to load a dehydrated file (or iterable) of items such as would be output using pg_dump or postgresql copy.
+- Add "--chunksize" option to loader that can split the processing of an iterable or file into chunks of n records at a time
+
+## [v0.5.1]
+
+### Fixed
+
+### Changed
+
+### Added
+
+- Add conf item to search to allow returning results without hydrating. This allows an application using pgstac to shift the CPU load of rehydrating items from the database onto the application server.
+
+## [v0.5.0]
+
+Version 0.5.0 is a major refactor of how data is stored. It is recommended to start a new database from scratch and to move data over rather than to use the inbuilt migration which will be very slow for larger amounts of data.
+
+### Fixed
+
+### Changed
+
+- The partition layout has been changed from being hardcoded to a partition to week to using nested partitions. The first level is by collection, for each collection, there is an attribute partition_trunc which can be set to NULL (no temporal partitions), month, or year.
+
+- CQL1 and Query Code have been refactored to translate to CQL2 to reduce duplicated code in query parsing.
+
+- Unused functions have been stripped from the project.
+
+- Pypgstac has been changed to use Fire rather than Typo.
+
+- Pypgstac has been changed to use Psycopg3 rather than Asyncpg to enable easier use as both sync and async.
+
+- Indexing has been reworked to eliminate indexes that from logs were not being used. The global json index on properties has been removed. Indexes on individual properties can be added either globally or per collection using the new queryables table.
+
+- Triggers for maintaining partitions have been updated to reduce lock contention and to reflect the new data layout.
+
+- The data pager which optimizes "order by datetime" searches has been updated to get time periods from the new partition layout and partition metadata.
+
+- Tests have been updated to reflect the many changes.
+
+### Added
+
+- On ingest, the content in an item is compared to the metadata available at the collection level and duplicate information is stripped out (this is primarily data in the item_assets property). Logic is added in to merge this data back in on data usage.
+
+## [v0.4.5]
+
+### Fixed
+
+- Fixes support for using the intersects parameter at the base of a search (regression from changes in 0.4.4)
+- Fixes issue where results for a search on id returned [None] rather than [] (regression from changes in 0.4.4)
+
+### Changed
+
+- Changes requirement for PostgreSQL to 13+, the triggers used to main partitions are not available to be used on partitions prior to 13 ([#90](https://github.com/stac-utils/pgstac/pull/90))
+- Bump requirement for asyncpg to 0.25.0 ([#82](https://github.com/stac-utils/pgstac/pull/82))
+
+### Added
+
+- Added more tests.
+
+## [v0.4.4]
+
+### Added
+
+- Adds support for using ids, collections, datetime, bbox, and intersects parameters separated from the filter-lang (Fixes #85)
+ - Previously use of these parameters was translated into cql-json and then to SQL, so was not available when using cql2-json
+ - The deprecated query parameter is still only available when filter-lang is set to cql-json
+
+### Changed
+
+- Add PLPGSQL for item lookups by id so that the query plan for the simple query can be cached
+ - Use item_by_id function when looking up records used for paging filters
+ - Add a short circuit to search to use item_by_id lookup when using the ids parameter
+ - This short circuit avoids using the query cache for this simple case
+ - Ordering when using the ids parameter is hard coded to return results in the same order as the array passed in (this avoids the overhead of full parsing and additional overhead to sort)
+
+### Fixed
+
+- Fix to make sure that filtering on the search_wheres table leverages the functional index on the hash of the query rather than on the query itself.
+
+## [v0.4.3]
+
+### Fixed
+
+- Fix for optimization when using equals with json properties. Allow optimization for both "eq" and "=" (was only previously enabled for "eq")
+
+## [v0.4.2]
+
+### Changed
+
+- Add support for updated CQL2 spec to use timestamp or interval key
+
+### Fixed
+
+- Fix for 0.3.4 -> 0.3.5 migration making sure that partitions get renamed correctly
+
+## [v0.4.1]
+
+### Changed
+
+- Update `typer` to 0.4.0 to avoid clashes with `click` ([#76](https://github.com/stac-utils/pgstac/pull/76))
+
+### Fixed
+
+- Fix logic in getting settings to make sure that filter-lang set on query is respected. ([#77](https://github.com/stac-utils/pgstac/pull/77))
+- Fix for large queries in the query cache. ([#71](https://github.com/stac-utils/pgstac/pull/71))
+
+## [v0.4.0]
+
+### Fixed
+
+- Fixes syntax for IN, BETWEEN, ISNULL, and NOT in CQL 1 ([#69](https://github.com/stac-utils/pgstac/pull/69))
+
+### Added
+
+- Adds support for modifying settings through pgstac_settings table and by passing in 'conf' object in search json to support AWS RDS where custom user configuration settings are not allowed and changing settings on the fly for a given query.
+- Adds support for CQL2-JSON ([#67](https://github.com/stac-utils/pgstac/pull/67))
+ - Adds tests for all examples in
+ - filter-lang parameter controls which dialect of CQL to use
+ - Adds 'default-filter-lang' setting to control what dialect to use when 'filter-lang' is not present
+ - old style stac 'query' object and top level ids, collections, datetime, bbox, and intersects parameters are only available with cql-json
+
+## [v0.3.4]
+
+### Added
+
+- add `geometrysearch`, `geojsonsearch` and `xyzsearch` for optimized searches for tiled requets ([#39](https://github.com/stac-utils/pgstac/pull/39))
+- add `create_items` and `upsert_items` methods for bulk insert ([#39](https://github.com/stac-utils/pgstac/pull/39))
+
+## [v0.3.3]
+
+### Fixed
+
+- Fixed CQL term to be "id", not "ids" ([#46](https://github.com/stac-utils/pgstac/pull/46))
+- Make sure featureCollection response has empty features `[]` not `null` ([#46](https://github.com/stac-utils/pgstac/pull/46))
+- Fixed bugs for `sortby` and `pagination` ([#46](https://github.com/stac-utils/pgstac/pull/46))
+- Make sure pgtap errors get caught in CI ([#46](https://github.com/stac-utils/pgstac/pull/46))
+
+## [v0.3.2]
+
+## Fixed
+
+- Fixed CQL term to be "collections", not "collection" ([#43](https://github.com/stac-utils/pgstac/pull/43))
+
+## [v0.3.1]
+
+_TODO_
+
+## [v0.2.8]
+
+### Added
+
+- Type hints to pypgstac that pass mypy checks ([#18](https://github.com/stac-utils/pgstac/pull/18))
+
+### Fixed
+
+- Fixed issue with pypgstac loads which caused some writes to fail ([#18](https://github.com/stac-utils/pgstac/pull/18))
+
+[Unreleased]: https://github.com/stac-utils/pgstac/compare/v0.9.11...HEAD
+[v0.9.11]: https://github.com/stac-utils/pgstac/compare/v0.9.10...v0.9.11
+[v0.9.10]: https://github.com/stac-utils/pgstac/compare/v0.9.9...v0.9.10
+[v0.9.9]: https://github.com/stac-utils/pgstac/compare/v0.9.8...v0.9.9
+[v0.9.8]: https://github.com/stac-utils/pgstac/compare/v0.9.7...v0.9.8
+[v0.9.7]: https://github.com/stac-utils/pgstac/compare/v0.9.6...v0.9.7
+[v0.9.6]: https://github.com/stac-utils/pgstac/compare/v0.9.5...v0.9.6
+[v0.9.5]: https://github.com/stac-utils/pgstac/compare/v0.9.4...v0.9.5
+[v0.9.4]: https://github.com/stac-utils/pgstac/compare/v0.9.3...v0.9.4
+[v0.9.3]: https://github.com/stac-utils/pgstac/compare/v0.9.2...v0.9.3
+[v0.9.2]: https://github.com/stac-utils/pgstac/compare/v0.9.1...v0.9.2
+[v0.9.1]: https://github.com/stac-utils/pgstac/compare/v0.9.0...v0.9.1
+[v0.9.0]: https://github.com/stac-utils/pgstac/compare/v0.8.5...v0.9.0
+[v0.8.5]: https://github.com/stac-utils/pgstac/compare/v0.8.4...v0.8.5
+[v0.8.4]: https://github.com/stac-utils/pgstac/compare/v0.8.3...v0.8.4
+[v0.8.3]: https://github.com/stac-utils/pgstac/compare/v0.8.2...v0.8.3
+[v0.8.2]: https://github.com/stac-utils/pgstac/compare/v0.8.1...v0.8.2
+[v0.8.1]: https://github.com/stac-utils/pgstac/compare/v0.8.0...v0.8.1
+[v0.8.0]: https://github.com/stac-utils/pgstac/compare/v0.7.10...v0.8.0
+[v0.7.10]: https://github.com/stac-utils/pgstac/compare/v0.7.9...v0.7.10
+[v0.7.9]: https://github.com/stac-utils/pgstac/compare/v0.7.8...v0.7.9
+[v0.7.8]: https://github.com/stac-utils/pgstac/compare/v0.7.7...v0.7.8
+[v0.7.7]: https://github.com/stac-utils/pgstac/compare/v0.7.6...v0.7.7
+[v0.7.6]: https://github.com/stac-utils/pgstac/compare/v0.7.5...v0.7.6
+[v0.7.5]: https://github.com/stac-utils/pgstac/compare/v0.7.4...v0.7.5
+[v0.7.4]: https://github.com/stac-utils/pgstac/compare/v0.7.3...v0.7.4
+[v0.7.3]: https://github.com/stac-utils/pgstac/compare/v0.7.2...v0.7.3
+[v0.7.2]: https://github.com/stac-utils/pgstac/compare/v0.7.1...v0.7.2
+[v0.7.1]: https://github.com/stac-utils/pgstac/compare/v0.7.0...v0.7.1
+[v0.7.0]: https://github.com/stac-utils/pgstac/compare/v0.6.13...v0.7.0
+[v0.6.13]: https://github.com/stac-utils/pgstac/compare/v0.6.12...v0.6.13
+[v0.6.12]: https://github.com/stac-utils/pgstac/compare/v0.6.11...v0.6.12
+[v0.6.11]: https://github.com/stac-utils/pgstac/compare/v0.6.10...v0.6.11
+[v0.6.10]: https://github.com/stac-utils/pgstac/compare/v0.6.9...v0.6.10
+[v0.6.9]: https://github.com/stac-utils/pgstac/compare/v0.6.8...v0.6.9
+[v0.6.8]: https://github.com/stac-utils/pgstac/compare/v0.6.7...v0.6.8
+[v0.6.7]: https://github.com/stac-utils/pgstac/compare/v0.6.6...v0.6.7
+[v0.6.6]: https://github.com/stac-utils/pgstac/compare/v0.6.5...v0.6.6
+[v0.6.5]: https://github.com/stac-utils/pgstac/compare/v0.6.4...v0.6.5
+[v0.6.4]: https://github.com/stac-utils/pgstac/compare/v0.6.3...v0.6.4
+[v0.6.3]: https://github.com/stac-utils/pgstac/compare/v0.6.2...v0.6.3
+[v0.6.2]: https://github.com/stac-utils/pgstac/compare/v0.6.1...v0.6.2
+[v0.6.1]: https://github.com/stac-utils/pgstac/compare/v0.6.0...v0.6.1
+[v0.6.0]: https://github.com/stac-utils/pgstac/compare/v0.5.1...v0.6.0
+[v0.5.1]: https://github.com/stac-utils/pgstac/compare/v0.5.0...v0.5.1
+[v0.5.0]: https://github.com/stac-utils/pgstac/compare/v0.4.5...v0.5.0
+[v0.4.5]: https://github.com/stac-utils/pgstac/compare/v0.4.4...v0.4.5
+[v0.4.4]: https://github.com/stac-utils/pgstac/compare/v0.4.3...v0.4.4
+[v0.4.3]: https://github.com/stac-utils/pgstac/compare/v0.4.2...v0.4.3
+[v0.4.2]: https://github.com/stac-utils/pgstac/compare/v0.4.1...v0.4.2
+[v0.4.1]: https://github.com/stac-utils/pgstac/compare/v0.4.0...v0.4.1
+[v0.4.0]: https://github.com/stac-utils/pgstac/compare/v0.3.4...v0.4.0
+[v0.3.4]: https://github.com/stac-utils/pgstac/compare/v0.3.3...v0.3.4
+[v0.3.3]: https://github.com/stac-utils/pgstac/compare/v0.3.2...v0.3.3
+[v0.3.2]: https://github.com/stac-utils/pgstac/compare/v0.3.1...v0.3.2
+[v0.3.1]: https://github.com/stac-utils/pgstac/compare/v0.3.0...v0.3.1
+[v0.2.8]: https://github.com/stac-utils/pgstac/compare/ff02c9cee7bbb0a2de21530b0aeb34e823f2e95c...v0.2.8
diff --git a/scripts/cipublish b/scripts/cipublish
deleted file mode 100755
index 9556826e..00000000
--- a/scripts/cipublish
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/bin/bash
-SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
-cd $SCRIPT_DIR/..
-set -e
-
-if [[ -n "${CI}" ]]; then
- set -x
-fi
-
-function usage() {
- echo -n \
- "Usage: $(basename "$0")
-Publish pypgstac
-
-Options:
---test Publish to test pypi
-"
-}
-
-POSITIONAL=()
-while [[ $# -gt 0 ]]
-do
- key="$1"
- case $key in
-
- --help)
- usage
- exit 0
- shift
- ;;
-
- --test)
- TEST_PYPI="--repository testpypi"
- shift
- ;;
-
- *) # unknown option
- POSITIONAL+=("$1") # save it in an array for later
- shift # past argument
- ;;
- esac
-done
-set -- "${POSITIONAL[@]}" # restore positional parameters
-
-# Fail if this isn't CI and we aren't publishing to test pypi
-if [ -z "${TEST_PYPI}" ] && [ -z "${CI}" ]; then
- echo "Only CI can publish to pypi"
- exit 1
-fi
-
-if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
- pushd pypgstac
- rm -rf dist
- pip install build twine
- python -m build --sdist --wheel
- twine upload ${TEST_PYPI} dist/*
- popd
-fi
diff --git a/scripts/container-scripts/format b/scripts/container-scripts/format
index 6c22233e..4565936f 100755
--- a/scripts/container-scripts/format
+++ b/scripts/container-scripts/format
@@ -18,7 +18,7 @@ This script is meant to be run inside the dev container.
}
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
- echo "Formatting pypgstac..."
- ruff check --fix pypgstac/src/pypgstac pypgstac/tests
- ruff format pypgstac/src/pypgstac pypgstac/tests
+ echo "Formatting pgstac-migrate..."
+ uv run --directory pgstac-migrate --group dev ruff check --fix src/pgstac_migrate tests
+ uv run --directory pgstac-migrate --group dev ruff format src/pgstac_migrate tests
fi
diff --git a/scripts/container-scripts/makemigration b/scripts/container-scripts/makemigration
index 0aacb351..e84fb7fb 100755
--- a/scripts/container-scripts/makemigration
+++ b/scripts/container-scripts/makemigration
@@ -75,7 +75,6 @@ fi
BASEDIR=$SRCDIR
PGSTACDIR=$BASEDIR/pgstac
PGPKGDIR=${PGPKG_REPO_DIR:-}
-PYPGSTACDIR=$BASEDIR/pypgstac
MIGRATIONSDIR=$BASEDIR/pgstac/migrations
SQLDIR=$BASEDIR/pgstac/sql
diff --git a/scripts/container-scripts/stageversion b/scripts/container-scripts/stageversion
index edcaf264..ab42d8ec 100755
--- a/scripts/container-scripts/stageversion
+++ b/scripts/container-scripts/stageversion
@@ -8,7 +8,8 @@ BASEDIR=$SRCDIR
PGSTACDIR=$BASEDIR/pgstac
PGPKGDIR=${PGPKG_REPO_DIR:-}
SQLDIR=$BASEDIR/pgstac/sql
-PYPGSTACDIR=$BASEDIR/pypgstac
+MIGRATEDIR=$BASEDIR/pgstac-migrate
+RSDIR=$BASEDIR/pgstac-rs
MIGRATIONSDIR=$BASEDIR/pgstac/migrations
function run_pgpkg() {
@@ -28,8 +29,8 @@ function usage() {
cat < $PYPGSTACDIR/src/pypgstac/version.py
-"""Version."""
+echo "Setting pgstac-migrate version to $PYVERSION"
+cat < $MIGRATEDIR/src/pgstac_migrate/__init__.py
+"""PgSTAC migration wrapper."""
__version__ = "${PYVERSION}"
EOD
-sed -i "s/^version[ ]*=[ ]*.*$/version = \"${PYVERSION}\"/" $PYPGSTACDIR/pyproject.toml
+sed -i "s/^version[ ]*=[ ]*.*$/version = \"${PYVERSION}\"/" $MIGRATEDIR/pyproject.toml
+
+echo "Setting pgstac-rs crate version to $PYVERSION"
+sed -i "s/^version[ ]*=[ ]*.*$/version = \"${PYVERSION}\"/" $RSDIR/Cargo.toml
makemigration -f $OLDVERSION -t $VERSION
diff --git a/scripts/container-scripts/test b/scripts/container-scripts/test
index 6d2d0256..6e7efb65 100755
--- a/scripts/container-scripts/test
+++ b/scripts/container-scripts/test
@@ -3,15 +3,15 @@ set -e
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
REPO_DIR=${PGSTAC_REPO_DIR:-/opt/src}
export PATH="$SCRIPT_DIR:$PATH"
-if [[ -d "$REPO_DIR/pgstac" && -d "$REPO_DIR/pypgstac" ]]; then
+if [[ -d "$REPO_DIR/pgstac" && -d "$REPO_DIR/pgstac-migrate" ]]; then
export SRCDIR="$REPO_DIR"
-elif [[ -d "$REPO_DIR/src/pgstac" && -d "$REPO_DIR/src/pypgstac" ]]; then
+elif [[ -d "$REPO_DIR/src/pgstac" && -d "$REPO_DIR/src/pgstac-migrate" ]]; then
export SRCDIR="$REPO_DIR/src"
-elif [[ -d "$SCRIPT_DIR/../../src/pgstac" && -d "$SCRIPT_DIR/../../src/pypgstac" ]]; then
+elif [[ -d "$SCRIPT_DIR/../../src/pgstac" && -d "$SCRIPT_DIR/../../src/pgstac-migrate" ]]; then
export SRCDIR="$(cd "$SCRIPT_DIR/../../src" && pwd)"
else
- echo "Unable to find pgstac/pypgstac sources under '$REPO_DIR'." >&2
- echo "Set PGSTAC_REPO_DIR to either a directory containing pgstac/ and pypgstac/ or a repository root containing src/pgstac and src/pypgstac." >&2
+ echo "Unable to find pgstac/pgstac-migrate sources under '$REPO_DIR'." >&2
+ echo "Set PGSTAC_REPO_DIR to either a directory containing pgstac/ and pgstac-migrate/ or a repository root containing src/pgstac and src/pgstac-migrate." >&2
exit 1
fi
export PGSTACDIR=$SRCDIR/pgstac
@@ -32,9 +32,10 @@ Options:
--pgtap Run PGTap SQL tests.
--basicsql Run basic SQL output tests.
--basicsql-createout Recreate missing .sql.out fixtures.
- --pypgstac Run Python tests.
+ --pymigrate Run pgstac-migrate Python tests.
--migrations Run migration-path validation.
--pgdump Run pg_dump / pg_restore validation.
+ --rust Run the pgstac-rs Rust tests (cargo test --features cli).
--nomigrations Run everything except migration and pg_dump tests.
--v Raise client_min_messages to notice.
--vv Raise client_min_messages to debug1.
@@ -135,11 +136,11 @@ EOSQL
}
function test_formatting(){
- cd $SRCDIR/pypgstac
+ cd $SRCDIR/pgstac-migrate
echo "Running ruff"
- uv run --group dev ruff check src/pypgstac tests
- uv run --group dev ruff format --check src/pypgstac tests
+ uv run --group dev ruff check src/pgstac_migrate tests
+ uv run --group dev ruff format --check src/pgstac_migrate tests
echo "Running ty"
uv run --group dev --group test ty check
@@ -149,16 +150,15 @@ function test_formatting(){
echo "Checking for legacy dotted migration filenames."
find $SRCDIR/pgstac/migrations -maxdepth 1 -type f -name 'pgstac.*.sql' | grep . && { echo "Legacy dotted migration filenames remain in src/pgstac/migrations."; exit 1; }
- find $SRCDIR/pypgstac/src/pypgstac/migrations -maxdepth 1 -type f -name 'pgstac.*.sql' | grep . && { echo "Legacy dotted migration filenames remain in src/pypgstac/src/pypgstac/migrations."; exit 1; }
- VERSION=$(cd $SRCDIR/pypgstac && uv run python -c "from pypgstac.version import __version__; print(__version__)")
+ VERSION=$(cd $SRCDIR/pgstac-migrate && uv run python -c "from pgstac_migrate import __version__; print(__version__)")
echo $VERSION
if echo $VERSION | grep "dev"; then
VERSION="unreleased"
fi
- echo "Checking whether base sql migration exists for pypgstac version."
+ echo "Checking whether base sql migration exists for pgstac-migrate version."
[ -f $SRCDIR/pgstac/migrations/pgstac--"${VERSION}".sql ] || { echo "****FAIL No migration exists at pgstac/migrations/pgstac--${VERSION}.sql"; exit 1; }
echo "Congratulations! All formatting tests pass."
@@ -228,18 +228,12 @@ done
psql -X -q -c "DROP DATABASE IF EXISTS pgstac_test_basicsql WITH (force);";
}
-function test_pypgstac(){
+function test_pymigrate(){
[[ $MESSAGELOG == 1 ]] && VERBOSE="-vvv"
-TEMPLATEDB=${1:-pgstac_test_db_template}
- cd $SRCDIR/pypgstac
- psql -X -q -v ON_ERROR_STOP=1 <&2
+ exit 1
+ fi
+ if [[ ! -f "$crate_dir/Cargo.toml" ]]; then
+ echo "test_rust: pgstac-rs crate not found at '$crate_dir' (is src/pgstac-rs mounted into the container?)." >&2
+ exit 1
+ fi
+ scripts_dir="$(cd "$scripts_dir" && pwd)"
+ base="postgresql://${user}:${pass}@${host}:${port}"
+
+ echo "=== Building pgstac-rs test templates on ${host}:${port} ==="
+ PGHOST="$host" PGPORT="$port" PGUSER="$user" PGPASSWORD="$pass" "$scripts_dir/pgstac-rs-test-db"
+ PGHOST="$host" PGPORT="$port" PGUSER="$user" PGPASSWORD="$pass" "$scripts_dir/pgstac-rs-test-db" pgstac_rs_ingest_template
+ PGHOST="$host" PGPORT="$port" PGUSER="$user" PGPASSWORD="$pass" "$scripts_dir/pgstac-rs-test-rich-db"
+
+ echo "=== Running pgstac-rs cargo tests ==="
+ # Point every test-connection env var the suite reads at the container database, so no test falls back
+ # to the localhost:5439 dev default (which only exists outside the container). CARGO_HOME and
+ # CARGO_TARGET_DIR come from the image (persistent named volumes) so the dep registry + build
+ # artifacts survive across runs and never touch the mounted host target/.
+ (
+ cd "$crate_dir"
+ PGSTAC_RS_TEST_BASE="$base" \
+ PGSTAC_RS_TEST_DB="${base}/pgstac_rs_test_rich" \
+ PGSTAC_RS_TEST_RICH_DB="${base}/pgstac_rs_test_rich" \
+ PGSTAC_RS_INGEST_TEMPLATE="pgstac_rs_ingest_template" \
+ cargo test --features cli
+ )
+ echo "pgstac-rs Rust tests passed!"
+}
+
FORMATTING=0
SETUPDB=0
PGTAP=0
BASICSQL=0
-PYPGSTAC=0
+PYMIGRATE=0
MIGRATIONS=0
PGDUMP=0
MESSAGENOTICE=0
MESSAGELOG=0
CREATEBASICSQLOUT=0
+RUST=0
while [[ $# -gt 0 ]]
do
@@ -424,9 +461,8 @@ while [[ $# -gt 0 ]]
shift
;;
- --pypgstac)
- SETUPDB=1
- PYPGSTAC=1
+ --pymigrate)
+ PYMIGRATE=1
shift
;;
@@ -446,7 +482,12 @@ while [[ $# -gt 0 ]]
SETUPDB=1
PGTAP=1
BASICSQL=1
- PYPGSTAC=1
+ PYMIGRATE=1
+ shift
+ ;;
+
+ --rust)
+ RUST=1
shift
;;
@@ -463,16 +504,16 @@ CLIENTMESSAGES='warning'
[[ $MESSAGEDEBUG -eq 1 ]] && CLIENTMESSAGES='debug1'
echo $CLIENTMESSAGES
-if [[ ($FORMATTING -eq 0) && ($SETUPDB -eq 0) && ($PGTAP -eq 0) && ($BASICSQL -eq 0) && ($PYPGSTAC -eq 0) && ($MIGRATIONS -eq 0) && ($PGDUMP -eq 0) ]]
+if [[ ($FORMATTING -eq 0) && ($SETUPDB -eq 0) && ($PGTAP -eq 0) && ($BASICSQL -eq 0) && ($PYMIGRATE -eq 0) && ($MIGRATIONS -eq 0) && ($PGDUMP -eq 0) && ($RUST -eq 0) ]]
then
FORMATTING=1
SETUPDB=1
PGTAP=1
BASICSQL=1
- # PYPGSTAC and MIGRATIONS are intentionally excluded from the default
- # run-all block while Python loader updates are pending on this branch.
- # Use --pypgstac or --migrations flags to run them explicitly.
+ # PYMIGRATE and MIGRATIONS are intentionally excluded from the default
+ # run-all block. Use --pymigrate or --migrations flags to run them explicitly.
PGDUMP=1
+ RUST=1
fi
[ $FORMATTING -eq 1 ] && test_formatting
@@ -483,8 +524,9 @@ if [ $SETUPDB -eq 1 ]; then
fi
[ $PGTAP -eq 1 ] && test_pgtap
[ $BASICSQL -eq 1 ] && test_basicsql
-[ $PYPGSTAC -eq 1 ] && test_pypgstac
+[ $PYMIGRATE -eq 1 ] && test_pymigrate
[ $MIGRATIONS -eq 1 ] && test_migrations
[ $PGDUMP -eq 1 ] && test_pgdump
+[ $RUST -eq 1 ] && test_rust
exit 0
diff --git a/scripts/migrate b/scripts/migrate
index dd97f82a..db8da346 100755
--- a/scripts/migrate
+++ b/scripts/migrate
@@ -4,9 +4,9 @@ cd $SCRIPT_DIR/..
function usage() {
cat < /dev/null && pwd )
+source "$SCRIPT_DIR/pgstacenv" # cd to repo root, ensure .env
+
+# Connection defaults (overridable via env / .env). An explicitly-set PGHOST (e.g. from the container
+# test runner) wins over .env, so .env is only sourced when no host was provided.
+[[ -z "${PGHOST:-}" && -f .env ]] && set -a && source .env && set +a
+PGHOST=${PGHOST:-localhost}
+PGPORT=${PGPORT:-5439}
+PGUSER=${PGUSER:-username}
+export PGPASSWORD=${PGPASSWORD:-password}
+
+TEMPLATE=${1:-${PGSTAC_RS_TEST_TEMPLATE:-pgstac_rs_test_template}}
+PGSTAC_SQL="src/pgstac/pgstac.sql"
+
+if [[ ! -f "$PGSTAC_SQL" ]]; then
+ echo "ERROR: $PGSTAC_SQL not found (run from a repo checkout)." >&2
+ exit 1
+fi
+
+echo "Building clean pgstac template '$TEMPLATE' on $PGHOST:$PGPORT ..."
+
+# (Re)create the template database from a maintenance connection (postgres/postgis), then load the
+# assembled pgstac.sql into it. Terminating sessions first lets a re-run drop an in-use template.
+psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d postgres -v ON_ERROR_STOP=1 -q < /dev/null && pwd )
+source "$SCRIPT_DIR/pgstacenv" # cd to repo root, ensure .env
+
+# An explicitly-set PGHOST (e.g. from the container test runner) wins over .env.
+[[ -z "${PGHOST:-}" && -f .env ]] && set -a && source .env && set +a
+PGHOST=${PGHOST:-localhost}
+PGPORT=${PGPORT:-5439}
+PGUSER=${PGUSER:-username}
+export PGPASSWORD=${PGPASSWORD:-password}
+
+TEMPLATE=${1:-${PGSTAC_RS_TEST_RICH_TEMPLATE:-pgstac_rs_test_rich}}
+PGSTAC_SQL="src/pgstac/pgstac.sql"
+FIXROOT="src/pgstac/tests/testdata/planetary-computer"
+FIXTURES=(landsat-c2-l2 sentinel-2-l2a naip)
+
+for required in "$PGSTAC_SQL" "$FIXROOT/collections.ndjson"; do
+ [[ -f "$required" ]] || { echo "ERROR: $required not found (run from a repo checkout)." >&2; exit 1; }
+done
+
+psql() { command psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" "$@"; }
+
+echo "Building rich pgstac template '$TEMPLATE' on $PGHOST:$PGPORT ..."
+
+psql -d postgres -v ON_ERROR_STOP=1 -q <&2; exit 1; }
+ psql -d "$TEMPLATE" -v ON_ERROR_STOP=1 -q \
+ -c "\\copy items_staging (content) FROM '$items' $JSON_COPY"
+done
+
+# The bulk items_staging path does not populate partition_stats; analyze_items() fills the per-month
+# histogram so the search() band engine and partition_bounds work. (analyze_items is a procedure that
+# commits internally, so it must run on its own outside a transaction block.)
+psql -d "$TEMPLATE" -v ON_ERROR_STOP=1 -q -c "CALL analyze_items();"
+
+psql -d "$TEMPLATE" -tAc "SET search_path=pgstac,public;
+ SELECT format(
+ 'rich template ready: %s collections (%s with fragment_config), %s items, %s fragments',
+ (SELECT count(*) FROM collections),
+ (SELECT count(*) FROM collections WHERE fragment_config IS NOT NULL),
+ (SELECT count(*) FROM items),
+ (SELECT count(*) FROM item_fragments));"
diff --git a/scripts/test b/scripts/test
index 2ddcef0a..8467054a 100755
--- a/scripts/test
+++ b/scripts/test
@@ -14,10 +14,22 @@ Options:
--no-strict Allow stale-image shortcuts and watch fallback.
-h, --help Show this help text.
+Test suite flags (passed through to the in-container runner; no suite flag = all except
+migrations/pymigrate):
+ --formatting ruff + ty checks.
+ --pgtap PGTap SQL tests.
+ --basicsql Basic SQL output comparison tests.
+ --rust pgstac-rs Rust tests (cargo test --features cli).
+ --pgdump pg_dump / pg_restore round-trip.
+ --pymigrate pgstac-migrate Python tests.
+ --migrations Full migration-chain validation.
+ --nomigrations Everything except migrations + pg_dump.
+
Examples:
$(basename "$0")
$(basename "$0") --fast
- $(basename "$0") --pypgstac --build-policy always
+ $(basename "$0") --rust
+ $(basename "$0") --pymigrate --build-policy always
EOF
}
diff --git a/src/pgstac-migrate/pyproject.toml b/src/pgstac-migrate/pyproject.toml
index d20f34c2..32526097 100644
--- a/src/pgstac-migrate/pyproject.toml
+++ b/src/pgstac-migrate/pyproject.toml
@@ -13,9 +13,34 @@ dependencies = [
[project.scripts]
pgstac-migrate = "pgstac_migrate.cli:main"
+[dependency-groups]
+dev = [
+ "ruff==0.15.14",
+ "ty==0.0.39",
+]
+test = [
+ "pytest>=8.3,<9.1",
+]
+
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/pgstac_migrate"]
+
+[tool.ruff]
+target-version = "py311"
+line-length = 88
+
+[tool.ruff.lint.isort]
+known-first-party = ["pgstac_migrate"]
+
+[tool.ty.environment]
+python-version = "3.11"
+
+[tool.ty.src]
+include = ["src/pgstac_migrate", "tests"]
+
+[tool.pytest.ini_options]
+addopts = "-vv"
diff --git a/src/pgstac-migrate/src/pgstac_migrate/api.py b/src/pgstac-migrate/src/pgstac_migrate/api.py
index 83e27d3c..4fb475cb 100644
--- a/src/pgstac-migrate/src/pgstac_migrate/api.py
+++ b/src/pgstac-migrate/src/pgstac_migrate/api.py
@@ -49,3 +49,33 @@ def migrate(
password=password,
version_source=PgstacVersionSource(),
)
+
+
+def current_version(
+ *,
+ conninfo: str | None = None,
+ host: str | None = None,
+ port: int | str | None = None,
+ dbname: str | None = None,
+ user: str | None = None,
+ password: str | None = None,
+) -> str | None:
+ """Return the PgSTAC version installed in a live database, or ``None``.
+
+ Reads the authoritative version from ``pgstac.migrations`` the same way the
+ migration planner does. Connection parameters left unset fall back to the
+ standard libpq environment variables (``PGHOST``, ``PGDATABASE``, ...).
+ """
+ import psycopg
+ from psycopg.conninfo import make_conninfo
+
+ resolved = make_conninfo(
+ conninfo or "",
+ host=host,
+ port=port,
+ dbname=dbname,
+ user=user,
+ password=password,
+ )
+ with psycopg.connect(resolved) as conn:
+ return PgstacVersionSource().read_live_version(conn, None)
diff --git a/src/pgstac-migrate/src/pgstac_migrate/cli.py b/src/pgstac-migrate/src/pgstac_migrate/cli.py
index 0a57fb4d..c1ea4977 100644
--- a/src/pgstac-migrate/src/pgstac_migrate/cli.py
+++ b/src/pgstac-migrate/src/pgstac_migrate/cli.py
@@ -18,6 +18,7 @@
from pgpkg.versioning import default_target
from pgstac_migrate.api import artifact_path as resolved_artifact_path
+from pgstac_migrate.api import current_version
from pgstac_migrate.api import migrate as migrate_database
from pgstac_migrate.build import build_local_artifact
@@ -90,6 +91,14 @@ def main(argv: list[str] | None = None) -> int:
)
p_migrate.add_argument("--dry-run", action="store_true")
+ p_current = sub.add_parser(
+ "current",
+ help="Print the PgSTAC version installed in a live DB",
+ add_help=False,
+ )
+ p_current.add_argument("--help", action="help", help="Show help and exit")
+ _add_db_args(p_current)
+
sub.add_parser("info", help="Print baked artifact info")
sub.add_parser("versions", help="List baked migration versions")
sub.add_parser("build-artifact", help="Bake the local PgSTAC migration artifact")
@@ -127,6 +136,19 @@ def main(argv: list[str] | None = None) -> int:
print("(dry-run: rolled back)")
return 0
+ if args.cmd == "current":
+ password = _resolve_password(args)
+ version = current_version(
+ conninfo=args.dsn,
+ host=args.host,
+ port=args.port,
+ dbname=args.dbname,
+ user=args.user,
+ password=password,
+ )
+ print(version if version is not None else "(not installed)")
+ return 0
+
artifact, catalog = _load_artifact_and_catalog()
if args.cmd == "info":
print(f"project: {artifact.manifest.project_name}")
diff --git a/src/pgstac-migrate/src/pgstac_migrate/version_source.py b/src/pgstac-migrate/src/pgstac_migrate/version_source.py
index 067f3e32..68ea223f 100644
--- a/src/pgstac-migrate/src/pgstac_migrate/version_source.py
+++ b/src/pgstac-migrate/src/pgstac_migrate/version_source.py
@@ -23,7 +23,7 @@ def _has_set_version(self, conn: psycopg.Connection) -> bool:
def read_live_version(
self,
conn: psycopg.Connection,
- config: ProjectConfig,
+ config: ProjectConfig | None,
) -> str | None:
del config
with conn.cursor() as cur:
diff --git a/src/pgstac-migrate/tests/test_parity.py b/src/pgstac-migrate/tests/test_parity.py
index adac6e63..2426eb99 100644
--- a/src/pgstac-migrate/tests/test_parity.py
+++ b/src/pgstac-migrate/tests/test_parity.py
@@ -1,10 +1,10 @@
"""Cross-surface migration plan parity tests.
-Asserts that the pgstac-migrate artifact catalog and the pypgstac MigrationPath
-compatibility helper produce *identical* ordered file sequences for every
-(source, target) pair in the parity matrix.
+Asserts that the pgstac-migrate artifact catalog and the
+``pgstac_migrate.compat.MigrationPath`` compatibility helper produce *identical*
+ordered file sequences for every (source, target) pair in the parity matrix.
-This is the canonical regression test for "both tools would apply exactly the
+This is the canonical regression test for "both surfaces would apply exactly the
same SQL in exactly the same order".
"""
@@ -65,7 +65,7 @@ def test_plan_parity_across_surfaces(
source: str | None,
target: str,
) -> None:
- """pgstac-migrate catalog plan == pypgstac MigrationPath for every test case."""
+ """pgstac-migrate catalog plan == compat MigrationPath for every test case."""
pgpkg_planner = import_module("pgpkg.planner")
compat = import_module("pgstac_migrate.compat")
@@ -79,7 +79,7 @@ def test_plan_parity_across_surfaces(
pgpkg_files.append(migration_plan.bootstrap_base.name)
pgpkg_files.extend(step.file.name for step in migration_plan.steps)
- # ---- pypgstac MigrationPath compat path ----------------------------------
+ # ---- pgstac_migrate.compat MigrationPath path ----------------------------
compat_source = "init" if source is None else source
compat_files = compat.MigrationPath(
migrations_dir, compat_source, target
@@ -87,6 +87,6 @@ def test_plan_parity_across_surfaces(
assert pgpkg_files == compat_files, (
f"Plan mismatch for {source!r} → {target!r}:\n"
- f" pgstac-migrate catalog: {pgpkg_files}\n"
- f" pypgstac MigrationPath: {compat_files}"
+ f" pgstac-migrate catalog: {pgpkg_files}\n"
+ f" compat MigrationPath helper: {compat_files}"
)
diff --git a/src/pgstac-migrate/uv.lock b/src/pgstac-migrate/uv.lock
index 751c5223..c9ccfc94 100644
--- a/src/pgstac-migrate/uv.lock
+++ b/src/pgstac-migrate/uv.lock
@@ -2,6 +2,24 @@ version = 1
revision = 3
requires-python = ">=3.11"
+[[package]]
+name = "colorama"
+version = "0.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
+]
+
+[[package]]
+name = "iniconfig"
+version = "2.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
+]
+
[[package]]
name = "packaging"
version = "26.2"
@@ -34,12 +52,37 @@ dependencies = [
{ name = "psycopg", extra = ["binary"] },
]
+[package.dev-dependencies]
+dev = [
+ { name = "ruff" },
+ { name = "ty" },
+]
+test = [
+ { name = "pytest" },
+]
+
[package.metadata]
requires-dist = [
{ name = "pgpkg", specifier = ">=0.1.1,<0.2" },
{ name = "psycopg", extras = ["binary"], specifier = ">=3.1.0" },
]
+[package.metadata.requires-dev]
+dev = [
+ { name = "ruff", specifier = "==0.15.14" },
+ { name = "ty", specifier = "==0.0.39" },
+]
+test = [{ name = "pytest", specifier = ">=8.3,<9.1" }]
+
+[[package]]
+name = "pluggy"
+version = "1.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
+]
+
[[package]]
name = "psycopg"
version = "3.3.4"
@@ -109,6 +152,81 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/eb/e6/5fff07a70d1f945ed90ae131c3bd76cab32beff7c58c6db15ad5820b6d1f/psycopg_binary-3.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:c37e024c07308cd06cf3ec51bfd0e7f6157585a4d84d1bce4a7f5f7913719bf8", size = 3666849, upload-time = "2026-05-01T23:31:51.165Z" },
]
+[[package]]
+name = "pygments"
+version = "2.20.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
+]
+
+[[package]]
+name = "pytest"
+version = "9.0.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "iniconfig" },
+ { name = "packaging" },
+ { name = "pluggy" },
+ { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
+]
+
+[[package]]
+name = "ruff"
+version = "0.15.14"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/dc/8a/8bce2894573e9dae6ff4d77fe34ad727d79b9e6238ad288c5638990d90f6/ruff-0.15.14.tar.gz", hash = "sha256:48e866b165be4a9bdbf310f7d3c9a07edef2fe8cd63ffeb4e00bb590506ebf9f", size = 4700910, upload-time = "2026-05-21T14:34:55.177Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b9/c8/74a92c6ff9fcfb4f1f947126d3ebee8389276e161ecc85de5bda7cda51bd/ruff-0.15.14-py3-none-linux_armv6l.whl", hash = "sha256:8dd2db9416e487c8d4b01fa7056bb02c4d05969d4f8d17a08c229c2f4ff3c108", size = 10739177, upload-time = "2026-05-21T14:34:37.332Z" },
+ { url = "https://files.pythonhosted.org/packages/45/91/254a35c20acc38a7223c9d2d594af12e794432464f2cdeb52af1dc4a892d/ruff-0.15.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:be4ff55af755bd71a00ab3dc6bd7ffc467bd76e0df6881e286c2e3d23e8fb43b", size = 11144969, upload-time = "2026-05-21T14:34:43.978Z" },
+ { url = "https://files.pythonhosted.org/packages/56/9e/d13e40f83b8d0a94430e6778ce1d94a43b38cf2efe63278bdd2b4c65abbf/ruff-0.15.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:48d5909d7d06276ce7dde6d32bfa4b0d4cb2651145cd8ee4b440722cbc77832f", size = 10478207, upload-time = "2026-05-21T14:34:48.378Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/f1/b15a7839fa4f332f8acec78e20564f26bb2d866e3d21710b877fd0263000/ruff-0.15.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca8cbfa94c4f90984a67561978602746d4cd27103568f745fa90eee3f0d4107d", size = 10818459, upload-time = "2026-05-21T14:34:22.318Z" },
+ { url = "https://files.pythonhosted.org/packages/45/33/53d651177f84f94b400a0e27f8824eeada3dddc9d5ee8aeb048f4352a520/ruff-0.15.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a6bbc0333f1ab053423bcbf6226477d266ca7cec7738c4c8e3f55647803f3c4", size = 10541800, upload-time = "2026-05-21T14:34:20.209Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/a6/868f87e0bf9786ed24b5d0d0ad8676b8a94fd1912f42cddf9cfc7857818a/ruff-0.15.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a24a4f7605d7003a6674d4387651effd939dead3fddd0f36561eb77a9a2e542", size = 11342149, upload-time = "2026-05-21T14:34:46.365Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/8b/38cd5c19faffdcc05a408d2b78edccc69492ab9720eadb49ea15ef80d768/ruff-0.15.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:049b5326e53ed80978f2fc041a280603f69dd6b0c95464342a2bb4572d9d9e2f", size = 12212563, upload-time = "2026-05-21T14:34:28.579Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/4d/a3c5b874a556d5731e3e657aaf04311bb76f0a5c3ec220ed43051be6b64b/ruff-0.15.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4ed42e6696c8dfa5f06728e6441993901f548eb92d73bc472cb5a38d1395fbf", size = 11493299, upload-time = "2026-05-21T14:34:41.836Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/c0/56472c251d09858a53e51efbd485b09e1995d8731668b76d52e5dd6ee0f1/ruff-0.15.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:715c543cf450c4888251f91c52f1942a800541d9bddd7ac060aa4e6b77ae7cba", size = 11455931, upload-time = "2026-05-21T14:34:57.276Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/4a/e2e7b4d8dbf233d4eace59c75bc3435fa6d8bd3bae82d351d4e4300c0fd1/ruff-0.15.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:72ebab6013ec887d439d8b7593737a0a4ffb06d45d209d4e4bf2e92813082d3f", size = 11400794, upload-time = "2026-05-21T14:34:39.773Z" },
+ { url = "https://files.pythonhosted.org/packages/97/c7/83c0539fe34c3e09136204d1e75d6052492364e0b3cb05e9465423f567d7/ruff-0.15.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:49072d36abdbe97a8dd7f480afe9c675699c0c495d4c84076e2c1203c4550581", size = 10804759, upload-time = "2026-05-21T14:34:31.045Z" },
+ { url = "https://files.pythonhosted.org/packages/86/a6/18f2bfc095a2ab4a78745644e428205532ce6653a5d0fa8501572891534d/ruff-0.15.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:958522aee105068640c2c2ceae08f413ae44d922f52a1374ac13d6a96032fc93", size = 10539517, upload-time = "2026-05-21T14:34:53.064Z" },
+ { url = "https://files.pythonhosted.org/packages/54/3a/5a8b3b69c654d4e4bf1d246ac5b49cbcdac6eaab6905925f8915f31e3b80/ruff-0.15.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f3707da619a143a2e8830e2abab8224478d69ace2d28cb6c20543ae97c36bf61", size = 11065169, upload-time = "2026-05-21T14:34:24.484Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/c5/8864e4e7925b836ea354b31d57641ec03830564e281a8b6f061f8c3e0ec1/ruff-0.15.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:bb01d645694e3ec0102105d07ef2d53703970407d59c04e59d3ba0b7a1d53553", size = 11560214, upload-time = "2026-05-21T14:34:50.975Z" },
+ { url = "https://files.pythonhosted.org/packages/36/38/012bf76752e1f89ed50b77b99532d90f3a3e287bc7918e1fc0948ac866ac/ruff-0.15.14-py3-none-win32.whl", hash = "sha256:6d0c1ad2a0ab718d39b6d8fd2217981ce4d625cd96a720095f798fb47d8b13e6", size = 10805548, upload-time = "2026-05-21T14:34:33.453Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/b7/4ea2c170f10ad760fff2a5250beb18897719dc8b52b53a24cddbb9dd3f19/ruff-0.15.14-py3-none-win_amd64.whl", hash = "sha256:802342981e056db3851a7836e5b070f8f15f67d4a685ae2a6160939d364b2902", size = 11939523, upload-time = "2026-05-21T14:34:18.077Z" },
+ { url = "https://files.pythonhosted.org/packages/62/d5/bc97ff895ec35cf3925d4bd60f3b39d822f377a446906ec9bcc87405e59b/ruff-0.15.14-py3-none-win_arm64.whl", hash = "sha256:ff47b90a9ef6a40c9e2f3b479c1fb78531adf055b94c1eba0a7ba04b31951826", size = 11208607, upload-time = "2026-05-21T14:34:26.525Z" },
+]
+
+[[package]]
+name = "ty"
+version = "0.0.39"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d0/8d/7b5c74dc287fbcb37bae9853cec13bf44717c1735298500e4aeba31579a9/ty-0.0.39.tar.gz", hash = "sha256:f750277e76a01ecd86185960eca73823c26a53c51103568d56d4d904575159fd", size = 5702365, upload-time = "2026-05-22T21:09:56.403Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/08/17/9b89802c26d12d0f7a27bc25d4066d941d42891e8898f9f26499f0067e32/ty-0.0.39-py3-none-linux_armv6l.whl", hash = "sha256:c1bb7ac70f1f7d70cc6655fd96558039e4562b10f489fa49c7ebfd5fcee73ad1", size = 11360431, upload-time = "2026-05-22T21:09:18.689Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/c6/663ded50e823dbf9fb9d002eca46b7cb1fb2c72b744b84f22ce732a0ee0b/ty-0.0.39-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3435b64c1e59c14c9aa39c20cc018823937cd38d55db853e74d95b8f420569b0", size = 11096281, upload-time = "2026-05-22T21:09:15.383Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/ae/5d38ba9a6456ff4c78d212cf464fd8b9a25d8118465197b0b2dc891c0b19/ty-0.0.39-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5f136377ce46c73677701a9e1ad730bf72f699bcec046e422eb79d0886cac3ab", size = 10529674, upload-time = "2026-05-22T21:09:46.471Z" },
+ { url = "https://files.pythonhosted.org/packages/be/6f/43638cb8106445d3c8817256a0731cde9dd7b6a53ae2e881294bc1930ca3/ty-0.0.39-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36b65fb0cc17f03e851d40e210d420be94ab8bc52d041328ad1e45f616036a61", size = 11055561, upload-time = "2026-05-22T21:09:36.981Z" },
+ { url = "https://files.pythonhosted.org/packages/91/17/95e62cf4458527ce78dc386eba18f8b10c3fb64cd8c9e7e59b262ff6029d/ty-0.0.39-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4967967bfadf3860ff84c3fccdbaec8edf8aa20d0d727521084733d853de6657", size = 11127185, upload-time = "2026-05-22T21:09:31.395Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/c0/93666c213db5c71ab1b1f1a0db5f66bf8c7c0e0b0bf59859f5da8f0b3c36/ty-0.0.39-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e10ecb1297099ddf9a1f054f8bd921d1863ce85fb819a3c96ed27865a1ba6ed", size = 11608459, upload-time = "2026-05-22T21:09:12.862Z" },
+ { url = "https://files.pythonhosted.org/packages/79/85/3b26585afc8b50230d6464bb0642feef4fab3f847e38b1f0ffa971a81446/ty-0.0.39-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9b19cca70e465d71b0510656343883d62372bbe74b7845cae7c0e701d6d5264b", size = 12177101, upload-time = "2026-05-22T21:09:40.519Z" },
+ { url = "https://files.pythonhosted.org/packages/49/4a/1039e4f6afc576dc1c3a4d22a6478904a1ad3766597cd0b93c077ab9dfce/ty-0.0.39-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:56c6704b01b9b3d80ff26b2918423b742516d1e469bef830e9254dcedc9185bf", size = 11827815, upload-time = "2026-05-22T21:09:49.89Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/c5/4688652870e350a76a8157f7ffb59ad54f37d5d10725aa7076f66ac94ec8/ty-0.0.39-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b7840ff46764b6a6757f4ade1cd0530fc3e8a0b435ca93e7602360e4cb90b6", size = 11694429, upload-time = "2026-05-22T21:09:21.568Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/72/8a1c4e823bb5bdc935a1c8140e100304e36a68a4139592f170aa9736fdb7/ty-0.0.39-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1c62a3a87ce26b50819f0dbf03bd95f23f19eeb87bbc7aa732ec64277c77f1aa", size = 11869846, upload-time = "2026-05-22T21:09:28.053Z" },
+ { url = "https://files.pythonhosted.org/packages/17/9f/cf982457b861ae22d657c5dcdbc631199f7f90264279db1d17230dfbc3ff/ty-0.0.39-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f8c34bc81a9c3516e49904e9d8330aac385377cca98390193ea02b903a40fcf0", size = 11029763, upload-time = "2026-05-22T21:09:06.791Z" },
+ { url = "https://files.pythonhosted.org/packages/46/c9/95b64f6d43ae6e8f0b7e13dacf9c196d35819af22b1924171fba31383156/ty-0.0.39-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:66f5ab11586a64e79cb692ad685ee5469325c31b5f30bd3554f52f36dbe28cc4", size = 11146761, upload-time = "2026-05-22T21:09:10.178Z" },
+ { url = "https://files.pythonhosted.org/packages/52/69/0a89cfb06f7632a05bf56c78e0affb4a40f81759e275376cea75c9c5abe9/ty-0.0.39-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e8d89732bcbbcb091f439e556dfc4932f198b118b47d5b85212c60662099670e", size = 11281843, upload-time = "2026-05-22T21:09:34.234Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/53/64c4a27067a46643fea2b3fcf21a8a2f838d91a65ffdd14f2e82945b9538/ty-0.0.39-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:eceb6c91dcd05a231119f82abdd9aa337513de23ca6ac990bc44f88791dc1799", size = 11792477, upload-time = "2026-05-22T21:09:24.923Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/e8/02f4dd4a12bcdbda0006f9c7ff3b99a4be06bd0d257d3bd4a5b66de074e6/ty-0.0.39-py3-none-win32.whl", hash = "sha256:891c3262314dbc80bf3e872634d23dd216306945daa9a9fcc206ce5ed21ac4c9", size = 10615377, upload-time = "2026-05-22T21:09:43.167Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/5a/aaeb22faa8d4dae90a287d4c3636c671edcff3b99be5f4fc8b79ad71eef6/ty-0.0.39-py3-none-win_amd64.whl", hash = "sha256:ba7f2d54452535419e90f6f03ff39282999e87b43c21c00559f6d7ad711a36d5", size = 11710711, upload-time = "2026-05-22T21:09:53.179Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/17/ae7339651bfcaa5f54698c8c70eaf5031baa400ecb67baec31d03a56cbd4/ty-0.0.39-py3-none-win_arm64.whl", hash = "sha256:eb4cf0fefbbfedf9a352597bb2431ebdcb7eb3a595c0f825f228e897a0ec285d", size = 11081409, upload-time = "2026-05-22T21:09:03.741Z" },
+]
+
[[package]]
name = "typing-extensions"
version = "4.15.0"
diff --git a/src/pgstac-rs/Cargo.lock b/src/pgstac-rs/Cargo.lock
index 53ad26fb..a4c57dde 100644
--- a/src/pgstac-rs/Cargo.lock
+++ b/src/pgstac-rs/Cargo.lock
@@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 4
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
[[package]]
name = "ahash"
version = "0.8.12"
@@ -9,6 +15,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
+ "const-random",
"getrandom 0.3.4",
"once_cell",
"serde",
@@ -25,6 +32,21 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "alloc-no-stdlib"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
+
+[[package]]
+name = "alloc-stdlib"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195"
+dependencies = [
+ "alloc-no-stdlib",
+]
+
[[package]]
name = "allocator-api2"
version = "0.2.21"
@@ -40,6 +62,56 @@ dependencies = [
"libc",
]
+[[package]]
+name = "anstream"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
+
+[[package]]
+name = "anstyle-parse"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
+dependencies = [
+ "anstyle",
+ "once_cell_polyfill",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "anyhow"
version = "1.0.102"
@@ -64,6 +136,157 @@ dependencies = [
"object",
]
+[[package]]
+name = "arrow-arith"
+version = "58.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0ab212d2c1886e802f51c5212d78ebbcbb0bec980fff9dadc1eb8d45cd0b738"
+dependencies = [
+ "arrow-array",
+ "arrow-buffer",
+ "arrow-data",
+ "arrow-schema",
+ "chrono",
+ "num-traits",
+]
+
+[[package]]
+name = "arrow-array"
+version = "58.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfd33d3e92f207444098c75b42de99d329562be0cf686b307b097cc52b4e999e"
+dependencies = [
+ "ahash",
+ "arrow-buffer",
+ "arrow-data",
+ "arrow-schema",
+ "chrono",
+ "chrono-tz",
+ "half",
+ "hashbrown 0.17.1",
+ "num-complex",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "arrow-buffer"
+version = "58.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c6cd424c2693bcdbc150d843dc9d4d137dd2de4782ce6df491ad11a3a0416c0"
+dependencies = [
+ "bytes",
+ "half",
+ "num-bigint",
+ "num-traits",
+]
+
+[[package]]
+name = "arrow-cast"
+version = "58.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c5aefb56a2c02e9e2b30746241058b85f8983f0fcff2ba0c6d09006e1cded7f"
+dependencies = [
+ "arrow-array",
+ "arrow-buffer",
+ "arrow-data",
+ "arrow-ord",
+ "arrow-schema",
+ "arrow-select",
+ "atoi",
+ "base64",
+ "chrono",
+ "half",
+ "lexical-core",
+ "num-traits",
+ "ryu",
+]
+
+[[package]]
+name = "arrow-data"
+version = "58.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c88210023a2bfee1896af366309a3028fc3bcbd6515fa29a7990ee1baa08ee0"
+dependencies = [
+ "arrow-buffer",
+ "arrow-schema",
+ "half",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "arrow-ipc"
+version = "58.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "238438f0834483703d88896db6fe5a7138b2230debc31b34c0336c2996e3c64f"
+dependencies = [
+ "arrow-array",
+ "arrow-buffer",
+ "arrow-data",
+ "arrow-schema",
+ "arrow-select",
+ "flatbuffers",
+]
+
+[[package]]
+name = "arrow-json"
+version = "58.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "205ca2119e6d679d5c133c6f30e68f027738d95ed948cf77677ea69c7800036b"
+dependencies = [
+ "arrow-array",
+ "arrow-buffer",
+ "arrow-cast",
+ "arrow-ord",
+ "arrow-schema",
+ "arrow-select",
+ "chrono",
+ "half",
+ "indexmap 2.14.0",
+ "itoa",
+ "lexical-core",
+ "memchr",
+ "num-traits",
+ "ryu",
+ "serde_core",
+ "serde_json",
+ "simdutf8",
+]
+
+[[package]]
+name = "arrow-ord"
+version = "58.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bffd8fd2579286a5d63bac898159873e5094a79009940bcb42bbfce4f19f1d0"
+dependencies = [
+ "arrow-array",
+ "arrow-buffer",
+ "arrow-data",
+ "arrow-schema",
+ "arrow-select",
+]
+
+[[package]]
+name = "arrow-schema"
+version = "58.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f633dbfdf39c039ada1bf9e34c694816eb71fbb7dc78f613993b7245e078a1ed"
+
+[[package]]
+name = "arrow-select"
+version = "58.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cd065c54172ac787cf3f2f8d4107e0d3fdc26edba76fdf4f4cc170258942222"
+dependencies = [
+ "ahash",
+ "arrow-array",
+ "arrow-buffer",
+ "arrow-data",
+ "arrow-schema",
+ "num-traits",
+]
+
[[package]]
name = "as-slice"
version = "0.1.5"
@@ -76,6 +299,28 @@ dependencies = [
"stable_deref_trait",
]
+[[package]]
+name = "async-stream"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "async-trait"
version = "0.1.89"
@@ -87,6 +332,15 @@ dependencies = [
"syn",
]
+[[package]]
+name = "atoi"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "atomic-polyfill"
version = "1.0.3"
@@ -96,18 +350,52 @@ dependencies = [
"critical-section",
]
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+[[package]]
+name = "aws-lc-rs"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00"
+dependencies = [
+ "aws-lc-sys",
+ "zeroize",
+]
+
+[[package]]
+name = "aws-lc-sys"
+version = "0.41.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4"
+dependencies = [
+ "cc",
+ "cmake",
+ "dunce",
+ "fs_extra",
+]
+
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+[[package]]
+name = "base64ct"
+version = "1.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
+
[[package]]
name = "bit-set"
version = "0.8.0"
@@ -153,6 +441,36 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c"
+[[package]]
+name = "brotli"
+version = "8.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+ "brotli-decompressor",
+]
+
+[[package]]
+name = "brotli-decompressor"
+version = "5.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+]
+
+[[package]]
+name = "bs58"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
+dependencies = [
+ "tinyvec",
+]
+
[[package]]
name = "bumpalo"
version = "3.20.2"
@@ -184,6 +502,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
dependencies = [
"find-msvc-tools",
+ "jobserver",
+ "libc",
"shlex",
]
@@ -193,6 +513,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
[[package]]
name = "chacha20"
version = "0.10.0"
@@ -218,18 +544,139 @@ dependencies = [
"windows-link",
]
+[[package]]
+name = "chrono-tz"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3"
+dependencies = [
+ "chrono",
+ "phf 0.12.1",
+]
+
+[[package]]
+name = "clap"
+version = "4.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
+
+[[package]]
+name = "cmake"
+version = "0.1.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
+dependencies = [
+ "cc",
+]
+
[[package]]
name = "cmov"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746"
+[[package]]
+name = "colorchoice"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
+
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
[[package]]
name = "const-oid"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
+[[package]]
+name = "const-random"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
+dependencies = [
+ "const-random-macro",
+]
+
+[[package]]
+name = "const-random-macro"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
+dependencies = [
+ "getrandom 0.2.17",
+ "once_cell",
+ "tiny-keccak",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
@@ -311,6 +758,12 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+[[package]]
+name = "crunchy"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
+
[[package]]
name = "crypto-common"
version = "0.1.6"
@@ -340,8 +793,109 @@ dependencies = [
]
[[package]]
-name = "digest"
-version = "0.10.7"
+name = "darling"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0"
+dependencies = [
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "deadpool"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b"
+dependencies = [
+ "deadpool-runtime",
+ "lazy_static",
+ "num_cpus",
+ "tokio",
+]
+
+[[package]]
+name = "deadpool-postgres"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d697d376cbfa018c23eb4caab1fd1883dd9c906a8c034e8d9a3cb06a7e0bef9"
+dependencies = [
+ "async-trait",
+ "deadpool",
+ "getrandom 0.2.17",
+ "tokio",
+ "tokio-postgres",
+ "tracing",
+]
+
+[[package]]
+name = "deadpool-runtime"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b"
+dependencies = [
+ "tokio",
+]
+
+[[package]]
+name = "der"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
+dependencies = [
+ "const-oid 0.9.6",
+ "der_derive",
+ "flagset",
+ "zeroize",
+]
+
+[[package]]
+name = "der_derive"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "deranged"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
@@ -356,7 +910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer 0.12.0",
- "const-oid",
+ "const-oid 0.10.2",
"crypto-common 0.2.1",
"ctutils",
]
@@ -372,6 +926,18 @@ dependencies = [
"syn",
]
+[[package]]
+name = "dunce"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+
+[[package]]
+name = "dyn-clone"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
+
[[package]]
name = "earcut"
version = "0.4.5"
@@ -396,12 +962,31 @@ dependencies = [
"serde",
]
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+[[package]]
+name = "errno"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "fallible-iterator"
version = "0.2.0"
@@ -419,12 +1004,54 @@ dependencies = [
"regex-syntax",
]
+[[package]]
+name = "fastrand"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
+
+[[package]]
+name = "filetime"
+version = "0.2.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
+dependencies = [
+ "cfg-if",
+ "libc",
+]
+
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+[[package]]
+name = "flagset"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe"
+
+[[package]]
+name = "flatbuffers"
+version = "25.12.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3"
+dependencies = [
+ "bitflags",
+ "rustc_version",
+]
+
+[[package]]
+name = "flate2"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
+dependencies = [
+ "miniz_oxide",
+ "zlib-rs",
+]
+
[[package]]
name = "float_next_after"
version = "2.0.0"
@@ -442,6 +1069,12 @@ dependencies = [
"serde",
]
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
[[package]]
name = "foldhash"
version = "0.1.5"
@@ -473,6 +1106,27 @@ dependencies = [
"num",
]
+[[package]]
+name = "fs_extra"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
+
+[[package]]
+name = "futures"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
[[package]]
name = "futures-channel"
version = "0.3.32"
@@ -489,6 +1143,23 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
+[[package]]
+name = "futures-executor"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
+
[[package]]
name = "futures-macro"
version = "0.3.32"
@@ -524,10 +1195,13 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
+ "futures-channel",
"futures-core",
+ "futures-io",
"futures-macro",
"futures-sink",
"futures-task",
+ "memchr",
"pin-project-lite",
"slab",
]
@@ -608,6 +1282,35 @@ dependencies = [
"spade",
]
+[[package]]
+name = "geoarrow-array"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dafe7b7de3fab1a8b7099fd6a6434ca955fa65065f9c19f0f8a133693f3c2b0e"
+dependencies = [
+ "arrow-array",
+ "arrow-buffer",
+ "arrow-schema",
+ "geo-traits",
+ "geoarrow-schema",
+ "num-traits",
+ "wkb",
+ "wkt 0.14.0",
+]
+
+[[package]]
+name = "geoarrow-schema"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d4a7edb2a1d87024a93805332a9c8184a0354836271d42c0d18cf628a5e3cd0"
+dependencies = [
+ "arrow-schema",
+ "geo-traits",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+]
+
[[package]]
name = "geographiclib-rs"
version = "0.2.7"
@@ -644,6 +1347,29 @@ dependencies = [
"tinyvec",
]
+[[package]]
+name = "geoparquet"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a64b758b2b1fc749c5eb212215afa6bc14e1fca93884dac339ab7097ffc20ce1"
+dependencies = [
+ "arrow-arith",
+ "arrow-array",
+ "arrow-buffer",
+ "arrow-ord",
+ "arrow-schema",
+ "geo-traits",
+ "geo-types",
+ "geoarrow-array",
+ "geoarrow-schema",
+ "indexmap 2.14.0",
+ "parquet",
+ "serde",
+ "serde_json",
+ "serde_with",
+ "wkt 0.14.0",
+]
+
[[package]]
name = "geozero"
version = "0.14.0"
@@ -653,11 +1379,25 @@ dependencies = [
"geo-types",
"geojson 0.24.2",
"log",
+ "scroll",
"serde_json",
"thiserror 1.0.69",
"wkt 0.11.1",
]
+[[package]]
+name = "getrandom"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi 0.11.1+wasi-snapshot-preview1",
+ "wasm-bindgen",
+]
+
[[package]]
name = "getrandom"
version = "0.3.4"
@@ -665,9 +1405,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"r-efi 5.3.0",
"wasip2",
+ "wasm-bindgen",
]
[[package]]
@@ -690,6 +1432,37 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
+[[package]]
+name = "h2"
+version = "0.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http",
+ "indexmap 2.14.0",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "half"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+ "num-traits",
+ "zerocopy",
+]
+
[[package]]
name = "hash32"
version = "0.1.1"
@@ -717,6 +1490,12 @@ dependencies = [
"byteorder",
]
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
[[package]]
name = "hashbrown"
version = "0.15.5"
@@ -784,6 +1563,18 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+[[package]]
+name = "hermit-abi"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
[[package]]
name = "hmac"
version = "0.13.0"
@@ -794,57 +1585,164 @@ dependencies = [
]
[[package]]
-name = "hybrid-array"
-version = "0.4.12"
+name = "http"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da"
+checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425"
dependencies = [
- "typenum",
+ "bytes",
+ "itoa",
]
[[package]]
-name = "i_float"
-version = "1.16.0"
+name = "http-body"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "813145bb0ad5b60f55cbbf3c74cdceda1c0a9d253b35c4cc36ae0df7887cb78f"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
- "libm",
+ "bytes",
+ "http",
]
[[package]]
-name = "i_key_sort"
-version = "0.10.3"
+name = "http-body-util"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d73d122b937fca067feb0ad74f62388920272b27c356d4df2d0cfdd59e044cf0"
+checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [
- "rayon",
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "pin-project-lite",
]
[[package]]
-name = "i_overlay"
-version = "4.5.1"
+name = "httparse"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "934cba666ad1bf60436a190af6eaa3f58f57b5bad28268ce3fb45d9185862232"
-dependencies = [
- "i_float",
- "i_key_sort",
- "i_shape",
- "i_tree",
- "rayon",
-]
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
-name = "i_shape"
-version = "1.18.0"
+name = "humantime"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa9eac533d7509a8ab87672b60ac610c17240f9ea4851d26227689fdfe349c8"
-dependencies = [
- "i_float",
-]
+checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
[[package]]
-name = "i_tree"
-version = "0.18.0"
+name = "hybrid-array"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da"
+dependencies = [
+ "typenum",
+]
+
+[[package]]
+name = "hyper"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.27.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
+dependencies = [
+ "http",
+ "hyper",
+ "hyper-util",
+ "rustls",
+ "rustls-native-certs",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
+dependencies = [
+ "base64",
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "ipnet",
+ "libc",
+ "percent-encoding",
+ "pin-project-lite",
+ "socket2",
+ "system-configuration",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "windows-registry",
+]
+
+[[package]]
+name = "i_float"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "813145bb0ad5b60f55cbbf3c74cdceda1c0a9d253b35c4cc36ae0df7887cb78f"
+dependencies = [
+ "libm",
+]
+
+[[package]]
+name = "i_key_sort"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d73d122b937fca067feb0ad74f62388920272b27c356d4df2d0cfdd59e044cf0"
+dependencies = [
+ "rayon",
+]
+
+[[package]]
+name = "i_overlay"
+version = "4.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "934cba666ad1bf60436a190af6eaa3f58f57b5bad28268ce3fb45d9185862232"
+dependencies = [
+ "i_float",
+ "i_key_sort",
+ "i_shape",
+ "i_tree",
+ "rayon",
+]
+
+[[package]]
+name = "i_shape"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa9eac533d7509a8ab87672b60ac610c17240f9ea4851d26227689fdfe349c8"
+dependencies = [
+ "i_float",
+]
+
+[[package]]
+name = "i_tree"
+version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4804bdc1dc124eb7e1aa9e144ecc04096bcf787a10a15fa44af682b51f0f6cce"
@@ -960,6 +1858,12 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
[[package]]
name = "idna"
version = "1.1.0"
@@ -981,6 +1885,17 @@ dependencies = [
"icu_properties",
]
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+ "serde",
+]
+
[[package]]
name = "indexmap"
version = "2.14.0"
@@ -993,6 +1908,51 @@ dependencies = [
"serde_core",
]
+[[package]]
+name = "indoc"
+version = "2.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
+dependencies = [
+ "rustversion",
+]
+
+[[package]]
+name = "integer-encoding"
+version = "3.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02"
+
+[[package]]
+name = "ipnet"
+version = "2.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
+
+[[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
+dependencies = [
+ "either",
+]
+
[[package]]
name = "itoa"
version = "1.0.18"
@@ -1011,7 +1971,7 @@ dependencies = [
"portable-atomic",
"portable-atomic-util",
"serde_core",
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -1040,6 +2000,65 @@ dependencies = [
"jiff-tzdb",
]
+[[package]]
+name = "jni"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
+dependencies = [
+ "cfg-if",
+ "combine",
+ "jni-macros",
+ "jni-sys",
+ "log",
+ "simd_cesu8",
+ "thiserror 2.0.18",
+ "walkdir",
+ "windows-link",
+]
+
+[[package]]
+name = "jni-macros"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "simd_cesu8",
+ "syn",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2"
+dependencies = [
+ "jni-sys-macros",
+]
+
+[[package]]
+name = "jni-sys-macros"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "jobserver"
+version = "0.1.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
+dependencies = [
+ "getrandom 0.3.4",
+ "libc",
+]
+
[[package]]
name = "js-sys"
version = "0.3.98"
@@ -1102,6 +2121,63 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
+[[package]]
+name = "lexical-core"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d8d125a277f807e55a77304455eb7b1cb52f2b18c143b60e766c120bd64a594"
+dependencies = [
+ "lexical-parse-float",
+ "lexical-parse-integer",
+ "lexical-util",
+ "lexical-write-float",
+ "lexical-write-integer",
+]
+
+[[package]]
+name = "lexical-parse-float"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52a9f232fbd6f550bc0137dcb5f99ab674071ac2d690ac69704593cb4abbea56"
+dependencies = [
+ "lexical-parse-integer",
+ "lexical-util",
+]
+
+[[package]]
+name = "lexical-parse-integer"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a7a039f8fb9c19c996cd7b2fcce303c1b2874fe1aca544edc85c4a5f8489b34"
+dependencies = [
+ "lexical-util",
+]
+
+[[package]]
+name = "lexical-util"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2604dd126bb14f13fb5d1bd6a66155079cb9fa655b37f875b3a742c705dbed17"
+
+[[package]]
+name = "lexical-write-float"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50c438c87c013188d415fbabbb1dceb44249ab81664efbd31b14ae55dabb6361"
+dependencies = [
+ "lexical-util",
+ "lexical-write-integer",
+]
+
+[[package]]
+name = "lexical-write-integer"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "409851a618475d2d5796377cad353802345cba92c867d9fbcde9cf4eac4e14df"
+dependencies = [
+ "lexical-util",
+]
+
[[package]]
name = "libc"
version = "0.2.186"
@@ -1129,6 +2205,12 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7281e4b2b1a1fae03463a7c49dd21464de50251a450f6da9715c40c7b21a70"
+[[package]]
+name = "linux-raw-sys"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
+
[[package]]
name = "litemap"
version = "0.8.2"
@@ -1150,6 +2232,31 @@ version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+[[package]]
+name = "lru-slab"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
+
+[[package]]
+name = "lz4_flex"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef0d4ed8669f8f8826eb00dc878084aa8f253506c4fd5e8f58f5bce72ddb97e"
+dependencies = [
+ "twox-hash",
+]
+
+[[package]]
+name = "md-5"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
+dependencies = [
+ "cfg-if",
+ "digest 0.10.7",
+]
+
[[package]]
name = "md-5"
version = "0.11.0"
@@ -1166,12 +2273,31 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
[[package]]
name = "mio"
version = "1.2.0"
@@ -1180,7 +2306,7 @@ checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
dependencies = [
"libc",
"wasi 0.11.1+wasi-snapshot-preview1",
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -1222,6 +2348,12 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "num-conv"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
+
[[package]]
name = "num-integer"
version = "0.1.46"
@@ -1263,6 +2395,38 @@ dependencies = [
"libm",
]
+[[package]]
+name = "num_cpus"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
+dependencies = [
+ "num_enum_derive",
+ "rustversion",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "objc2-core-foundation"
version = "0.3.2"
@@ -1291,17 +2455,93 @@ dependencies = [
]
[[package]]
-name = "once_cell"
-version = "1.21.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
-
-[[package]]
-name = "outref"
-version = "0.5.2"
+name = "object_store"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e"
-
+checksum = "3cfccb68961a56facde1163f9319e0d15743352344e7808a11795fb99698dcaf"
+dependencies = [
+ "async-trait",
+ "base64",
+ "bytes",
+ "chrono",
+ "futures",
+ "humantime",
+ "hyper",
+ "itertools 0.13.0",
+ "md-5 0.10.6",
+ "parking_lot",
+ "percent-encoding",
+ "quick-xml",
+ "rand 0.8.6",
+ "reqwest 0.12.28",
+ "ring",
+ "serde",
+ "serde_json",
+ "snafu",
+ "tokio",
+ "tracing",
+ "url",
+ "walkdir",
+]
+
+[[package]]
+name = "object_store"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622acbc9100d3c10e2ee15804b0caa40e55c933d5aa53814cd520805b7958a49"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "chrono",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "http",
+ "humantime",
+ "itertools 0.14.0",
+ "parking_lot",
+ "percent-encoding",
+ "thiserror 2.0.18",
+ "tokio",
+ "tracing",
+ "url",
+ "wasm-bindgen-futures",
+ "web-time",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "once_cell_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
+
+[[package]]
+name = "openssl-probe"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
+
+[[package]]
+name = "ordered-float"
+version = "2.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "outref"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e"
+
[[package]]
name = "parking_lot"
version = "0.12.5"
@@ -1325,6 +2565,48 @@ dependencies = [
"windows-link",
]
+[[package]]
+name = "parquet"
+version = "58.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dafa7d01085b62a47dd0c1829550a0a36710ea9c4fe358a05a85477cec8a908"
+dependencies = [
+ "ahash",
+ "arrow-array",
+ "arrow-buffer",
+ "arrow-data",
+ "arrow-ipc",
+ "arrow-schema",
+ "arrow-select",
+ "base64",
+ "brotli",
+ "bytes",
+ "chrono",
+ "flate2",
+ "futures",
+ "half",
+ "hashbrown 0.17.1",
+ "lz4_flex",
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+ "object_store 0.13.2",
+ "paste",
+ "seq-macro",
+ "simdutf8",
+ "snap",
+ "thrift",
+ "tokio",
+ "twox-hash",
+ "zstd",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
[[package]]
name = "pdqselect"
version = "0.1.0"
@@ -1393,16 +2675,41 @@ dependencies = [
name = "pgstac"
version = "0.9.11-dev"
dependencies = [
+ "arrow-array",
+ "async-stream",
+ "async-trait",
+ "base64",
+ "bytes",
+ "chrono",
+ "clap",
+ "deadpool-postgres",
+ "futures",
"geojson 1.0.0",
+ "geoparquet",
+ "geozero",
+ "object_store 0.11.2",
+ "parquet",
+ "pyo3",
+ "pyo3-async-runtimes",
"rstest",
+ "rustls",
+ "rustls-pemfile",
"serde",
"serde_json",
+ "sha2 0.10.9",
"stac",
+ "stac-io",
+ "tar",
+ "tempfile",
"thiserror 2.0.18",
"tokio",
"tokio-postgres",
+ "tokio-postgres-rustls",
"tokio-test",
"tracing",
+ "url",
+ "webpki-roots 0.26.11",
+ "zstd",
]
[[package]]
@@ -1415,6 +2722,15 @@ dependencies = [
"phf_shared 0.11.3",
]
+[[package]]
+name = "phf"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7"
+dependencies = [
+ "phf_shared 0.12.1",
+]
+
[[package]]
name = "phf"
version = "0.13.1"
@@ -1457,6 +2773,15 @@ dependencies = [
"siphasher",
]
+[[package]]
+name = "phf_shared"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981"
+dependencies = [
+ "siphasher",
+]
+
[[package]]
name = "phf_shared"
version = "0.13.1"
@@ -1472,6 +2797,12 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+[[package]]
+name = "pkg-config"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
+
[[package]]
name = "portable-atomic"
version = "1.13.1"
@@ -1498,7 +2829,7 @@ dependencies = [
"bytes",
"fallible-iterator",
"hmac",
- "md-5",
+ "md-5 0.11.0",
"memchr",
"rand 0.10.1",
"sha2 0.11.0",
@@ -1512,6 +2843,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dc729a129e682e8d24170cd30ae1aa01b336b096cbb56df6d534ffec133d186"
dependencies = [
"bytes",
+ "chrono",
"fallible-iterator",
"postgres-protocol",
"serde_core",
@@ -1527,6 +2859,21 @@ dependencies = [
"zerovec",
]
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
[[package]]
name = "prettyplease"
version = "0.2.37"
@@ -1565,6 +2912,148 @@ dependencies = [
"cc",
]
+[[package]]
+name = "pyo3"
+version = "0.23.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872"
+dependencies = [
+ "cfg-if",
+ "indoc",
+ "libc",
+ "memoffset",
+ "once_cell",
+ "portable-atomic",
+ "pyo3-build-config",
+ "pyo3-ffi",
+ "pyo3-macros",
+ "unindent",
+]
+
+[[package]]
+name = "pyo3-async-runtimes"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "977dc837525cfd22919ba6a831413854beb7c99a256c03bf8624ad707e45810e"
+dependencies = [
+ "futures",
+ "once_cell",
+ "pin-project-lite",
+ "pyo3",
+ "tokio",
+]
+
+[[package]]
+name = "pyo3-build-config"
+version = "0.23.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb"
+dependencies = [
+ "once_cell",
+ "target-lexicon",
+]
+
+[[package]]
+name = "pyo3-ffi"
+version = "0.23.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d"
+dependencies = [
+ "libc",
+ "pyo3-build-config",
+]
+
+[[package]]
+name = "pyo3-macros"
+version = "0.23.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da"
+dependencies = [
+ "proc-macro2",
+ "pyo3-macros-backend",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pyo3-macros-backend"
+version = "0.23.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "pyo3-build-config",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "quick-xml"
+version = "0.37.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "quinn"
+version = "0.11.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8"
+dependencies = [
+ "bytes",
+ "cfg_aliases",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash",
+ "rustls",
+ "socket2",
+ "thiserror 2.0.18",
+ "tokio",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e"
+dependencies = [
+ "aws-lc-rs",
+ "bytes",
+ "getrandom 0.3.4",
+ "lru-slab",
+ "rand 0.9.4",
+ "ring",
+ "rustc-hash",
+ "rustls",
+ "rustls-pki-types",
+ "slab",
+ "thiserror 2.0.18",
+ "tinyvec",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
+dependencies = [
+ "cfg_aliases",
+ "libc",
+ "once_cell",
+ "socket2",
+ "tracing",
+ "windows-sys 0.52.0",
+]
+
[[package]]
name = "quote"
version = "1.0.45"
@@ -1592,9 +3081,21 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
dependencies = [
+ "libc",
+ "rand_chacha 0.3.1",
"rand_core 0.6.4",
]
+[[package]]
+name = "rand"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
+dependencies = [
+ "rand_chacha 0.9.0",
+ "rand_core 0.9.5",
+]
+
[[package]]
name = "rand"
version = "0.10.1"
@@ -1606,11 +3107,43 @@ dependencies = [
"rand_core 0.10.1",
]
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.9.5",
+]
+
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom 0.2.17",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
+dependencies = [
+ "getrandom 0.3.4",
+]
[[package]]
name = "rand_core"
@@ -1746,32 +3279,131 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
[[package]]
-name = "robust"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e27ee8bb91ca0adcf0ecb116293afa12d393f9c2b9b9cd54d33e8078fe19839"
-
-[[package]]
-name = "rstar"
-version = "0.8.4"
+name = "reqwest"
+version = "0.12.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a45c0e8804d37e4d97e55c6f258bc9ad9c5ee7b07437009dd152d764949a27c"
+checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
- "heapless 0.6.1",
- "num-traits",
- "pdqselect",
+ "base64",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-rustls",
+ "hyper-util",
+ "js-sys",
+ "log",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls",
+ "rustls-native-certs",
+ "rustls-pki-types",
"serde",
- "smallvec",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tokio-rustls",
+ "tokio-util",
+ "tower",
+ "tower-http",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams",
+ "web-sys",
]
[[package]]
-name = "rstar"
-version = "0.9.3"
+name = "reqwest"
+version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b40f1bfe5acdab44bc63e6699c28b74f75ec43afb59f3eda01e145aff86a25fa"
+checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
dependencies = [
- "heapless 0.7.17",
- "num-traits",
+ "base64",
+ "bytes",
+ "encoding_rs",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-rustls",
+ "hyper-util",
+ "js-sys",
+ "log",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls",
+ "rustls-pki-types",
+ "rustls-platform-verifier",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tokio-rustls",
+ "tower",
+ "tower-http",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.17",
+ "libc",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "robust"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e27ee8bb91ca0adcf0ecb116293afa12d393f9c2b9b9cd54d33e8078fe19839"
+
+[[package]]
+name = "rstar"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a45c0e8804d37e4d97e55c6f258bc9ad9c5ee7b07437009dd152d764949a27c"
+dependencies = [
+ "heapless 0.6.1",
+ "num-traits",
+ "pdqselect",
+ "serde",
+ "smallvec",
+]
+
+[[package]]
+name = "rstar"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b40f1bfe5acdab44bc63e6699c28b74f75ec43afb59f3eda01e145aff86a25fa"
+dependencies = [
+ "heapless 0.7.17",
+ "num-traits",
"serde",
"smallvec",
]
@@ -1841,6 +3473,12 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "rustc-hash"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
+
[[package]]
name = "rustc_version"
version = "0.4.1"
@@ -1850,6 +3488,105 @@ dependencies = [
"semver",
]
+[[package]]
+name = "rustix"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "rustls"
+version = "0.23.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f"
+dependencies = [
+ "aws-lc-rs",
+ "log",
+ "once_cell",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d"
+dependencies = [
+ "openssl-probe",
+ "rustls-pki-types",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
+dependencies = [
+ "web-time",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-platform-verifier"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
+dependencies = [
+ "core-foundation 0.10.1",
+ "core-foundation-sys",
+ "jni",
+ "log",
+ "once_cell",
+ "rustls",
+ "rustls-native-certs",
+ "rustls-platform-verifier-android",
+ "rustls-webpki",
+ "security-framework",
+ "security-framework-sys",
+ "webpki-root-certs",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "rustls-platform-verifier-android"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
+
+[[package]]
+name = "rustls-webpki"
+version = "0.103.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
+dependencies = [
+ "aws-lc-rs",
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
[[package]]
name = "rustversion"
version = "1.0.22"
@@ -1862,18 +3599,95 @@ version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "schannel"
+version = "0.1.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "schemars"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f"
+dependencies = [
+ "dyn-clone",
+ "ref-cast",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemars"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc"
+dependencies = [
+ "dyn-clone",
+ "ref-cast",
+ "serde",
+ "serde_json",
+]
+
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+[[package]]
+name = "scroll"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
+
+[[package]]
+name = "security-framework"
+version = "3.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
+dependencies = [
+ "bitflags",
+ "core-foundation 0.10.1",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "semver"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
+[[package]]
+name = "seq-macro"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
+
[[package]]
name = "serde"
version = "1.0.228"
@@ -1910,7 +3724,7 @@ version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
- "indexmap",
+ "indexmap 2.14.0",
"itoa",
"memchr",
"serde",
@@ -1930,6 +3744,38 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_with"
+version = "3.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c"
+dependencies = [
+ "base64",
+ "bs58",
+ "chrono",
+ "hex",
+ "indexmap 1.9.3",
+ "indexmap 2.14.0",
+ "schemars 0.9.0",
+ "schemars 1.2.1",
+ "serde_core",
+ "serde_json",
+ "serde_with_macros",
+ "time",
+]
+
+[[package]]
+name = "serde_with_macros"
+version = "3.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "sha2"
version = "0.10.9"
@@ -1964,6 +3810,28 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7f45b8998ced5134fb1d75732c77842a3e888f19c1ff98481822e8fbfbf930b"
+[[package]]
+name = "simd-adler32"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
+
+[[package]]
+name = "simd_cesu8"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33"
+dependencies = [
+ "rustc_version",
+ "simdutf8",
+]
+
+[[package]]
+name = "simdutf8"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
+
[[package]]
name = "siphasher"
version = "1.0.3"
@@ -1982,6 +3850,33 @@ version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+[[package]]
+name = "snafu"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e84b3f4eacbf3a1ce05eac6763b4d629d60cbc94d632e4092c54ade71f1e1a2"
+dependencies = [
+ "snafu-derive",
+]
+
+[[package]]
+name = "snafu-derive"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "snap"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b"
+
[[package]]
name = "socket2"
version = "0.6.3"
@@ -1989,7 +3884,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
dependencies = [
"libc",
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -2013,6 +3908,16 @@ dependencies = [
"lock_api",
]
+[[package]]
+name = "spki"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
[[package]]
name = "sqlparser"
version = "0.58.0"
@@ -2043,17 +3948,29 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "stac"
-version = "0.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a069e4454aec3e4cf9340d2b2b242764086e23c452f1ff3deff29b30b2bf0b60"
-dependencies = [
+version = "0.17.2"
+source = "git+https://github.com/stac-utils/rustac.git?branch=pgstac-search-parity#710dffb483640d99595e6a56026544c21f42de63"
+dependencies = [
+ "arrow-array",
+ "arrow-cast",
+ "arrow-json",
+ "arrow-schema",
+ "async-stream",
"bytes",
"chrono",
"cql2",
+ "futures",
+ "futures-core",
+ "geo-traits",
+ "geo-types",
+ "geoarrow-array",
+ "geoarrow-schema",
"geojson 1.0.0",
- "indexmap",
+ "geoparquet",
+ "indexmap 2.14.0",
"log",
"mime",
+ "parquet",
"serde",
"serde_json",
"serde_urlencoded",
@@ -2061,18 +3978,38 @@ dependencies = [
"thiserror 2.0.18",
"tracing",
"url",
+ "wkb",
]
[[package]]
name = "stac-derive"
version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "087057f442c3a980721f249f723db459460ce74221730c9b2115bd63957027ae"
+source = "git+https://github.com/stac-utils/rustac.git?branch=pgstac-search-parity#710dffb483640d99595e6a56026544c21f42de63"
dependencies = [
"quote",
"syn",
]
+[[package]]
+name = "stac-io"
+version = "0.3.0"
+source = "git+https://github.com/stac-utils/rustac.git?branch=pgstac-search-parity#710dffb483640d99595e6a56026544c21f42de63"
+dependencies = [
+ "async-stream",
+ "bytes",
+ "futures",
+ "http",
+ "parquet",
+ "reqwest 0.13.4",
+ "serde",
+ "serde_json",
+ "stac",
+ "thiserror 2.0.18",
+ "tokio",
+ "tracing",
+ "url",
+]
+
[[package]]
name = "stacker"
version = "0.1.24"
@@ -2083,7 +4020,7 @@ dependencies = [
"cfg-if",
"libc",
"psm",
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -2097,6 +4034,18 @@ dependencies = [
"unicode-properties",
]
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
[[package]]
name = "syn"
version = "2.0.117"
@@ -2108,6 +4057,15 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "sync_wrapper"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
+
[[package]]
name = "synstructure"
version = "0.13.2"
@@ -2120,43 +4078,144 @@ dependencies = [
]
[[package]]
-name = "thiserror"
-version = "1.0.69"
+name = "system-configuration"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
+dependencies = [
+ "bitflags",
+ "core-foundation 0.9.4",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "tar"
+version = "0.4.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
+dependencies = [
+ "filetime",
+ "libc",
+ "xattr",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.12.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
+
+[[package]]
+name = "tempfile"
+version = "3.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
+dependencies = [
+ "fastrand",
+ "getrandom 0.4.2",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+dependencies = [
+ "thiserror-impl 2.0.18",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thrift"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09"
+dependencies = [
+ "byteorder",
+ "integer-encoding",
+ "ordered-float",
+]
+
+[[package]]
+name = "time"
+version = "0.3.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327"
dependencies = [
- "thiserror-impl 1.0.69",
+ "deranged",
+ "num-conv",
+ "powerfmt",
+ "serde_core",
+ "time-core",
+ "time-macros",
]
[[package]]
-name = "thiserror"
-version = "2.0.18"
+name = "time-core"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
-dependencies = [
- "thiserror-impl 2.0.18",
-]
+checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
[[package]]
-name = "thiserror-impl"
-version = "1.0.69"
+name = "time-macros"
+version = "0.2.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935"
dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "num-conv",
+ "time-core",
]
[[package]]
-name = "thiserror-impl"
-version = "2.0.18"
+name = "tiny-keccak"
+version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "crunchy",
]
[[package]]
@@ -2185,6 +4244,27 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+[[package]]
+name = "tls_codec"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b"
+dependencies = [
+ "tls_codec_derive",
+ "zeroize",
+]
+
+[[package]]
+name = "tls_codec_derive"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "tokio"
version = "1.52.3"
@@ -2197,7 +4277,7 @@ dependencies = [
"pin-project-lite",
"socket2",
"tokio-macros",
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -2237,6 +4317,31 @@ dependencies = [
"whoami",
]
+[[package]]
+name = "tokio-postgres-rustls"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27d684bad428a0f2481f42241f821db42c54e2dc81d8c00db8536c506b0a0144"
+dependencies = [
+ "const-oid 0.9.6",
+ "ring",
+ "rustls",
+ "tokio",
+ "tokio-postgres",
+ "tokio-rustls",
+ "x509-cert",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
[[package]]
name = "tokio-stream"
version = "0.1.18"
@@ -2287,7 +4392,7 @@ version = "0.25.11+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b"
dependencies = [
- "indexmap",
+ "indexmap 2.14.0",
"toml_datetime",
"toml_parser",
"winnow",
@@ -2302,6 +4407,51 @@ dependencies = [
"winnow",
]
+[[package]]
+name = "tower"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.6.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
+dependencies = [
+ "bitflags",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "pin-project-lite",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "url",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
[[package]]
name = "tracing"
version = "0.1.44"
@@ -2333,6 +4483,18 @@ dependencies = [
"once_cell",
]
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "twox-hash"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
+
[[package]]
name = "typenum"
version = "1.20.0"
@@ -2387,6 +4549,18 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+[[package]]
+name = "unindent"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
[[package]]
name = "url"
version = "2.5.8"
@@ -2406,6 +4580,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
[[package]]
name = "uuid"
version = "1.23.1"
@@ -2439,6 +4619,25 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
@@ -2494,6 +4693,16 @@ dependencies = [
"wasm-bindgen-shared",
]
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.71"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.121"
@@ -2543,11 +4752,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
- "indexmap",
+ "indexmap 2.14.0",
"wasm-encoder",
"wasmparser",
]
+[[package]]
+name = "wasm-streams"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
+dependencies = [
+ "futures-util",
+ "js-sys",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
[[package]]
name = "wasmparser"
version = "0.244.0"
@@ -2556,7 +4778,7 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags",
"hashbrown 0.15.5",
- "indexmap",
+ "indexmap 2.14.0",
"semver",
]
@@ -2570,6 +4792,43 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki-root-certs"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.26.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
+dependencies = [
+ "webpki-roots 1.0.8",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf"
+dependencies = [
+ "rustls-pki-types",
+]
+
[[package]]
name = "whoami"
version = "2.1.2"
@@ -2583,6 +4842,15 @@ dependencies = [
"web-sys",
]
+[[package]]
+name = "winapi-util"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "windows-core"
version = "0.62.2"
@@ -2624,6 +4892,17 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+[[package]]
+name = "windows-registry"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
+dependencies = [
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
[[package]]
name = "windows-result"
version = "0.4.1"
@@ -2642,6 +4921,15 @@ dependencies = [
"windows-link",
]
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
[[package]]
name = "windows-sys"
version = "0.61.2"
@@ -2651,6 +4939,70 @@ dependencies = [
"windows-link",
]
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
[[package]]
name = "winnow"
version = "1.0.2"
@@ -2694,7 +5046,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
- "indexmap",
+ "indexmap 2.14.0",
"prettyplease",
"syn",
"wasm-metadata",
@@ -2725,7 +5077,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags",
- "indexmap",
+ "indexmap 2.14.0",
"log",
"serde",
"serde_derive",
@@ -2744,7 +5096,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
- "indexmap",
+ "indexmap 2.14.0",
"log",
"semver",
"serde",
@@ -2754,6 +5106,18 @@ dependencies = [
"wasmparser",
]
+[[package]]
+name = "wkb"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a120b336c7ad17749026d50427c23d838ecb50cd64aaea6254b5030152f890a9"
+dependencies = [
+ "byteorder",
+ "geo-traits",
+ "num_enum",
+ "thiserror 1.0.69",
+]
+
[[package]]
name = "wkt"
version = "0.11.1"
@@ -2785,6 +5149,28 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
+[[package]]
+name = "x509-cert"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94"
+dependencies = [
+ "const-oid 0.9.6",
+ "der",
+ "spki",
+ "tls_codec",
+]
+
+[[package]]
+name = "xattr"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
+dependencies = [
+ "libc",
+ "rustix",
+]
+
[[package]]
name = "yoke"
version = "0.8.2"
@@ -2849,6 +5235,26 @@ dependencies = [
"synstructure",
]
+[[package]]
+name = "zeroize"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "zerotrie"
version = "0.2.4"
@@ -2882,8 +5288,42 @@ dependencies = [
"syn",
]
+[[package]]
+name = "zlib-rs"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "977347db8caa080403f6b6b7c1cda9479a8e869316f7e13a59b19076a40f94e3"
+
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+
+[[package]]
+name = "zstd"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "7.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
+dependencies = [
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.16+zstd.1.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
diff --git a/src/pgstac-rs/Cargo.toml b/src/pgstac-rs/Cargo.toml
index 82895714..506379b5 100644
--- a/src/pgstac-rs/Cargo.toml
+++ b/src/pgstac-rs/Cargo.toml
@@ -11,14 +11,107 @@ repository = "https://github.com/stac-utils/pgstac"
license = "MIT OR Apache-2.0"
rust-version = "1.88"
+[lib]
+crate-type = ["rlib", "cdylib"]
+
+[features]
+default = []
+# A Python extension module (built with maturin) exposing the read + geoparquet-export API over a
+# connection pool.
+python = ["pool", "export", "dep:pyo3", "dep:pyo3-async-runtimes"]
+migrations = []
+# A `deadpool`-backed connection pool ([`PgstacPool`]) with rustls TLS.
+pool = [
+ "dep:deadpool-postgres",
+ "dep:tokio-postgres-rustls",
+ "dep:rustls",
+ "dep:rustls-pemfile",
+ "dep:webpki-roots",
+ "dep:async-stream",
+]
+# Export/dump library: row sources, hydration glue, formats, sinks, manifest. Pulls in the stac
+# geoparquet writer + checksum/spill helpers, plus the parquet/geoparquet/arrow crates the parallel
+# loader decode drives directly (same versions stac/geoparquet resolves transitively).
+export = [
+ "stac/geoparquet",
+ "dep:async-trait",
+ "dep:tempfile",
+ "dep:arrow-array",
+ "dep:parquet",
+ "dep:geoparquet",
+ "tokio/sync",
+ "tokio/macros",
+]
+# The `object_store`-backed remote dump sink (local fs + S3/GCS/Azure), usable without the whole CLI.
+store = [
+ "export",
+ "dep:object_store",
+ "dep:url",
+ "tokio/fs",
+ "tokio/io-util",
+]
+# The streaming search-result writer: `stream_search_collection` drives the keyset portal + the shared
+# `stac_io::write_item_collection` to emit one flat-memory ItemCollection. Lightweight (pool + stac-io,
+# no clap/tar/zstd) so rustac's pgstac backend can stream via the SAME code without the whole CLI.
+search-writer = ["pool", "dep:stac-io"]
+# The `pgstac` binary (CLI). Implies `export` + `store` (dump/search) + `pool` (load/restore via the Rust loader).
+cli = [
+ "export",
+ "pool",
+ "store",
+ "search-writer",
+ "dep:clap",
+ "dep:tar",
+ "dep:zstd",
+ "tokio/macros",
+ "tokio/rt-multi-thread",
+ "tokio/fs",
+]
+
[dependencies]
+async-stream = { version = "0.3", optional = true }
+base64 = "0.22"
+bytes = "1"
+chrono = { version = "0.4", default-features = false, features = ["clock"] }
+deadpool-postgres = { version = "0.14", optional = true }
+futures = "0.3"
+geozero = { version = "0.14", default-features = false, features = ["with-wkb", "with-geojson"] }
+rustls = { version = "0.23", optional = true }
+rustls-pemfile = { version = "2.2", optional = true }
serde = "1.0"
-serde_json = "1.0"
-stac = "0.17.0"
+serde_json = { version = "1.0", features = ["raw_value", "float_roundtrip"] }
+sha2 = "0.10"
+stac = { version = "0.17.0", features = ["async"] }
thiserror = "2.0"
-tokio = { version = "1.44", features = ["rt"] }
-tokio-postgres = { version = "0.7.12", features = ["with-serde_json-1"] }
+tokio = { version = "1.44", features = ["rt", "sync"] }
+tokio-postgres = { version = "0.7.12", features = ["with-serde_json-1", "with-chrono-0_4"] }
+tokio-postgres-rustls = { version = "0.13", optional = true }
tracing = "0.1.40"
+webpki-roots = { version = "0.26", optional = true }
+
+# export feature (sha2 is a non-optional dep above — canonical.rs always needs it)
+async-trait = { version = "0.1", optional = true }
+tempfile = { version = "3", optional = true }
+# export feature: parallel stac-geoparquet decode reads row groups directly. Versions match what
+# stac's `geoparquet` feature pulls in transitively (parquet 58, geoparquet 0.8, arrow-array 58).
+arrow-array = { version = "58", optional = true }
+parquet = { version = "58", optional = true }
+geoparquet = { version = "0.8", optional = true }
+
+# store feature
+object_store = { version = "0.11", optional = true, features = ["aws", "http"] }
+url = { version = "2", optional = true }
+
+# cli feature
+clap = { version = "4", optional = true, features = ["derive", "env"] }
+# rustac's format enum (also folds in geoparquet compression) for the CLI's --format.
+stac-io = { version = "0.3", optional = true, features = ["geoparquet"] }
+tar = { version = "0.4", optional = true }
+zstd = { version = "0.13", optional = true }
+
+# python feature
+pyo3 = { version = "0.23", optional = true, features = ["macros"] }
+pyo3-async-runtimes = { version = "0.23", optional = true, features = ["tokio-runtime"] }
[dev-dependencies]
geojson = "1.0"
@@ -26,6 +119,18 @@ rstest = "0.26.1"
tokio = { version = "1.44", features = ["rt-multi-thread", "macros"] }
tokio-test = "0.4.4"
+[[bin]]
+name = "pgstac"
+path = "src/bin/pgstac.rs"
+required-features = ["cli"]
+
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
+
+# search-parity branch ONLY: build against the rustac branch carrying the
+# streaming ItemCollection writer (stac_io::write_item_collection) and the
+# search-parity trait changes. Do NOT merge this [patch] to a published-crate branch.
+[patch.crates-io]
+stac = { git = "https://github.com/stac-utils/rustac.git", branch = "pgstac-search-parity" }
+stac-io = { git = "https://github.com/stac-utils/rustac.git", branch = "pgstac-search-parity" }
diff --git a/src/pgstac-rs/README.md b/src/pgstac-rs/README.md
index 6df92d00..707cf747 100644
--- a/src/pgstac-rs/README.md
+++ b/src/pgstac-rs/README.md
@@ -16,6 +16,68 @@ pgstac = "*"
See the [documentation](https://docs.rs/pgstac) for more.
+### Cargo features
+
+| Feature | What it adds |
+| --------- | ------------ |
+| *(default)* | The `Pgstac` trait over any `tokio_postgres::GenericClient`. |
+| `pool` | [`PgstacPool`] — a `deadpool` connection pool (rustls TLS, PgBouncer-safe) with the read API: `search_page`, the flat-memory streaming iterator `search_items` / `stream_ndjson`, `collection_search`, `get_item`/`get_collection`/`get_queryables`, and `search_matched`. |
+| `export` | The export/dump library: scan partitions and write fully-hydrated stac-geoparquet + a sha256'd manifest. |
+| `cli` | The `pgstac` binary (implies `export`): `pgstac dump` and `pgstac search`. |
+| `python` | A pyo3 extension module (`pgstac`) built with [maturin]. |
+
+The read API drives `search_plan` / `collection_search_plan` and does the band-stepping,
+hydration (EWKB→GeoJSON, fragment merge), and keyset token minting **in Rust** — page-equivalent
+to SQL `search()` but with flat memory under streaming and the work moved off the database.
+
+### Pooled read API
+
+```rust,no_run
+use pgstac::{ConnectConfig, PgstacPool};
+use futures::StreamExt;
+use serde_json::json;
+
+# tokio_test::block_on(async {
+let pool = PgstacPool::connect(ConnectConfig::from_env()).await.unwrap();
+
+// A bounded page (keyset tokens included).
+let page = pool.search_page(&json!({"collections": ["landsat-c2-l2"]}), None, 10).await.unwrap();
+
+// Or stream every match with flat memory (one row + the fragment cache at a time).
+let mut items = Box::pin(pool.search_items(json!({"collections": ["landsat-c2-l2"]}), None, None));
+while let Some(item) = items.next().await {
+ let _item = item.unwrap();
+}
+# })
+```
+
+### CLI (`cli` feature)
+
+```sh
+# Dump an instance to fully-hydrated stac-geoparquet + manifest.
+pgstac dump --dsn "$PGSTAC_DSN" --out ./dump # or s3://…, foo.tar.zst, -
+
+# Stream a search as NDJSON / a FeatureCollection page / geoparquet.
+pgstac search --dsn "$PGSTAC_DSN" -c landsat-c2-l2 --datetime 2024-01-01/.. --format ndjson
+```
+
+### Python wheel (`python` feature)
+
+```sh
+maturin build -m src/pgstac-rs/Cargo.toml # builds the pgstac wheel
+```
+
+```python
+import asyncio, orjson, pgstac
+
+async def main():
+ pool = await pgstac.Pgstac.connect() # libpq env, or pass a dsn
+ fc = orjson.loads(await pool.search(orjson.dumps({"collections": ["landsat-c2-l2"], "limit": 10}).decode()))
+ print(fc["numberReturned"])
+
+asyncio.run(main())
+```
+
## Testing
**pgstac** needs a blank **pgstac** database for testing, so is not part of the default workspace build.
@@ -45,3 +107,5 @@ PGSTAC_RS_TEST_DB=postgresql://otherusername:otherpassword@otherhost:7822/otherd
## Other info
This crate used to be part of the [rustac](https://github.com/stac-utils/rustac) monorepo, but was moved here in May 2026.
+
+[maturin]: https://github.com/PyO3/maturin
diff --git a/src/pgstac-rs/pyproject.toml b/src/pgstac-rs/pyproject.toml
new file mode 100644
index 00000000..954ec872
--- /dev/null
+++ b/src/pgstac-rs/pyproject.toml
@@ -0,0 +1,18 @@
+[build-system]
+requires = ["maturin>=1.7,<2.0"]
+build-backend = "maturin"
+
+[project]
+name = "pgstac"
+description = "Rust-accelerated pgstac read API (search, collection search, getters, queryables) for stac-fastapi-pgstac"
+requires-python = ">=3.9"
+license = { text = "MIT" }
+dynamic = ["version"]
+classifiers = [
+ "Programming Language :: Rust",
+ "Programming Language :: Python :: Implementation :: CPython",
+]
+
+[tool.maturin]
+features = ["python", "cli", "pyo3/extension-module"]
+module-name = "pgstac"
diff --git a/src/pgstac-rs/src/api/mod.rs b/src/pgstac-rs/src/api/mod.rs
new file mode 100644
index 00000000..42c6b487
--- /dev/null
+++ b/src/pgstac-rs/src/api/mod.rs
@@ -0,0 +1,4 @@
+//! The rustac-native `stac::api` client-trait implementations over [`PgstacPool`](crate::PgstacPool).
+
+#[cfg(feature = "pool")]
+pub(crate) mod stac_api;
diff --git a/src/pgstac-rs/src/api/stac_api.rs b/src/pgstac-rs/src/api/stac_api.rs
new file mode 100644
index 00000000..072e03ed
--- /dev/null
+++ b/src/pgstac-rs/src/api/stac_api.rs
@@ -0,0 +1,73 @@
+//! Native [`stac::api`] client-trait impls for [`PgstacPool`], so a pgstac database is a first-class
+//! rustac backend (search / collections / transaction). Every method routes through the same engine
+//! the rest of the crate uses — the keyset search in [`crate::search()`] and the Rust loader for writes —
+//! so these traits are the rustac-native API surface over that engine, not a second implementation.
+
+use crate::{Error, PgstacPool};
+use futures::{Stream, StreamExt};
+use stac::api::{
+ CollectionsClient, ItemCollection, ItemsClient, Search, StreamItemsClient, TransactionClient,
+};
+use stac::{Collection, Item};
+
+impl ItemsClient for PgstacPool {
+ type Error = Error;
+
+ async fn search(&self, search: Search) -> Result {
+ self.client().await?.search(search).await
+ }
+
+ async fn item(&self, collection_id: &str, item_id: &str) -> Result