Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ jobs:
- run: cargo test -p graphql-orm --no-default-features --features sqlite,field-case-pascal,argument-case-snake --test federation_entity_keys_case_features
- run: cargo check -p graphql-orm --no-default-features --features postgres
- run: cargo test -p graphql-orm --no-default-features --features postgres --test federation_entity_keys_postgres
- name: Execute owned runtime migration preservation and compatibility
run: |
cargo test -p graphql-orm --locked --no-default-features --features postgres --test runtime_migrations --test runtime_schema_ir --test runtime_migration_memory --test migration_planner --test migration_apply --test legacy_migration_history -- --test-threads=1
cargo test -p graphql-orm --locked --no-default-features --features mssql --test runtime_migrations_unsupported
for backend in sqlite postgres mssql; do
cargo test --manifest-path crates/graphql-orm/tests/fixtures/migration-api-compatibility/Cargo.toml --locked --no-default-features --features "$backend"
done
- name: Execute host timestamps on owned PostgreSQL
run: cargo test -p graphql-orm --locked --no-default-features --features postgres --test host_timestamps -- --ignored --test-threads=1
- name: Execute the private PostgreSQL host timestamp consumer
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ jobs:
cargo test -p graphql-orm --no-default-features --features sqlite,field-case-pascal,argument-case-snake --test federation_entity_keys_case_features --locked
cargo check -p graphql-orm --no-default-features --features postgres --locked
cargo test -p graphql-orm --no-default-features --features postgres --test federation_entity_keys_postgres --locked
cargo test -p graphql-orm --locked --no-default-features --features postgres --test runtime_migrations --test runtime_schema_ir --test runtime_migration_memory --test migration_planner --test migration_apply --test legacy_migration_history -- --test-threads=1
cargo test -p graphql-orm --locked --no-default-features --features mssql --test runtime_migrations_unsupported
for backend in sqlite postgres mssql; do
cargo test --manifest-path crates/graphql-orm/tests/fixtures/migration-api-compatibility/Cargo.toml --locked --no-default-features --features "$backend"
done
cargo test -p graphql-orm --no-default-features --features postgres --test projection_visibility --test cross_crate_relations_fixture --test repository_counts --locked
cargo test -p graphql-orm --no-default-features --features postgres --test grouped_aggregates_postgres --test portable_group_pages --test host_timestamps --locked -- --ignored --test-threads=1
cargo run --manifest-path crates/graphql-orm/tests/fixtures/repository-aggregate-consumer/Cargo.toml --locked --no-default-features --features sqlite --example policy_projections
Expand Down
35 changes: 34 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,40 @@ This file is the authoritative user-facing release chronology. The former
[release-notes ledger](docs/archive/2026/graphql-orm-release-notes.md) is retained
for historical context.

## 0.39.0 - unreleased
## 0.40.0 - unreleased

Companion macros crate: `graphql-orm-macros` **0.40.0** (alignment only).

- Add owned canonical physical targets from `ValidatedRuntimeSchema`, checked
static/system composition, explicit `ManagedTableSet`, read-only owned planning
and validation, and separate guarded `apply_owned_migration` on SQLite/PostgreSQL.
- Keep the public legacy `MigrationStep` enum and struct literals unchanged.
Static and owned storage share validation, hashing, diffing, risk classification,
rendering and transactional history application; runtime conversion and owned
catalog introspection do not leak index storage.
- Bind immutable plans to complete live baselines and ownership, retain destructive
and additive guards, preserve host PostgreSQL RLS, and reject unsupported live
preservation cases. Add a verified incoming-dependency environment for later
runtime mutations; record mutation and dynamic GraphQL are still later slices.
- Reject unrepresentable live FK actions, deferral and dependency scopes with
`UnsupportedForeignKey`, including incoming system/unowned sources. Recheck on
the pinned apply transaction so SQLite rebuilds cannot drop omitted constraints
and runtime dependency certificates cannot mistake `SET DEFAULT` for `Restrict`.
- Preserve a single-connection SQLite in-memory database after a successful owned
rebuild; discard suspended connections on cancellation or failed restoration.
- Respect SQLite composite primary-key ordinal order during introspection.
- Compare PostgreSQL `TIMESTAMPTZ` and its introspected `timestamp with time zone`
spelling as equivalent in the shared static/owned planner. DateTime targets now
replan to no-op and certify after application; timezone-free timestamps and
precision changes remain distinct. Physical metadata and hash formats are unchanged.
- Reject legacy epoch-second DateTime default conversion with scoped structured
diagnostics while preserving static storage and supported Integer defaults;
preserve escaped quote semantics in converted literal defaults.

No automatic application, host catalog, transport, durable delivery or release
publication is introduced. See the owned runtime migration reference and example.

## 0.39.0 - 2026-10-03

Companion macros crate: `graphql-orm-macros` **0.39.0**.

Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ cynic-parser = { version = "=0.11.2", features = ["pretty"] }
futures = "0.3"
getrandom = "0.3"
graphql-composition = "=0.12.2"
graphql-orm = { path = "crates/graphql-orm", version = "0.39.0", default-features = false }
graphql-orm = { path = "crates/graphql-orm", version = "0.40.0", default-features = false }
graphql-orm-ai-tool-profiles = { path = "crates/graphql-orm-ai-tool-profiles", version = "0.15.0" }
graphql-orm-backup = { path = "crates/graphql-orm-backup", version = "0.7.2", default-features = false }
graphql-orm-operation-catalog = { path = "crates/graphql-orm-operation-catalog", version = "0.4.0" }
Expand Down
43 changes: 43 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,49 @@ supersedes: []
`graphql-orm` is distributed from GitHub only. Use a reviewed full 40-character commit in `rev`;
neither the runtime nor macros crate is published to crates.io.

## 0.39.0 to 0.40.0: owned runtime migration targets

Adopt runtime and macros 0.40.0 together. This unpublished identity retains the released
0.39.0 static capabilities without changing the approved migration contract. Existing static enums, variant imports,
constructors, exhaustive matches, struct literals and function pointers remain
source-compatible; no call-site or stored-data migration is required.

Runtime hosts may opt into `ValidatedRuntimeSchema::physical_schema`, compose
static metadata, explicitly declare owned physical tables, preview a plan, then
apply its immutable artifact with ordinary `ApplyOptions`. Retain intentionally
removed tables in the ownership set to review drops. Baseline validation is always
required for owned plans; `require_clean_schema = false` does not disable their
immutable baseline binding. Replan after drift and after successful application.
Owned application does not reconcile PostgreSQL RLS. Unsupported backends fail
before connection acquisition; unsupported live preservation cases return structured
rejections. See [owned runtime migrations](docs/reference/graphql-orm/runtime-migrations.md).

Owned runtime operations reject unsupported live FK semantics with a scoped
`UnsupportedForeignKey` diagnostic. Use explicit delete `RESTRICT`, `CASCADE` or
`SET NULL`, default update `NO ACTION` and non-deferrable constraints in the initial
profile. Delete `SET DEFAULT`/`NO ACTION`, nondefault update actions and deferral
cannot be faithfully represented by the legacy model. Relevant system/unowned
incoming constraints are checked, including on the pinned apply transaction;
unrelated tables are left intact. Static migration APIs/storage behavior are
unchanged. See the reference for additional PostgreSQL scope/match limitations.

PostgreSQL planning recognizes the unmodified built-in `TIMESTAMPTZ` /
`timestamp with time zone` alias without rewriting metadata, defaults or hashes.
This prevents false alterations and certification failures after applying runtime
DateTime targets. It requires no data migration. Timestamp without time zone,
precision modifiers, arrays and qualified/quoted type names remain distinct;
this is not general SQL type-name normalization. SQLite comparison is unchanged.

The physical dependency environment is not a record-write API or an authorization
certificate. Hosts must fence external DDL and validate their complete public/policy
revision before future mutations; ORM fingerprints exclude host policy revisions.

Static-to-runtime conversion now rejects an epoch-second default on a DateTime
field with a scoped `UnsupportedDefault` diagnostic. Existing static defaults and
storage remain unchanged; Integer epoch-second defaults remain supported. Canonical
runtime datetime defaults retain RFC3339/native timestamp semantics. No implicit
legacy datetime storage conversion or data migration is performed.

## Repository host timestamps (0.39.0)

Adopt aligned ORM/macros 0.39.0. Existing declarations require no edits and keep
Expand Down
2 changes: 1 addition & 1 deletion crates/graphql-orm-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "graphql-orm-macros"
version = "0.39.0"
version = "0.40.0"
edition = "2024"
authors = ["Toby Martin"]
description = "Procedural macros for async-graphql and ORM-backed entities, relations, and CRUD operations."
Expand Down
8 changes: 6 additions & 2 deletions crates/graphql-orm-macros/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ macro/runtime versions aligned:

```toml
[dependencies]
graphql-orm = { git = "https://github.com/Dastari/graphql-orm.git", rev = "<reviewed-full-40-character-commit-sha>", version = "0.39.0", default-features = false, features = ["sqlite"] }
graphql-orm = { git = "https://github.com/Dastari/graphql-orm.git", rev = "<reviewed-full-40-character-commit-sha>", version = "0.40.0", default-features = false, features = ["sqlite"] }
```

Direct use is supported for tooling that needs the macro package:

```toml
graphql-orm-macros = { git = "https://github.com/Dastari/graphql-orm.git", rev = "<reviewed-full-40-character-commit-sha>", version = "0.39.0", default-features = false, features = ["sqlite"] }
graphql-orm-macros = { git = "https://github.com/Dastari/graphql-orm.git", rev = "<reviewed-full-40-character-commit-sha>", version = "0.40.0", default-features = false, features = ["sqlite"] }
```

The direct dependency still requires a compatible `graphql-orm` runtime in the
Expand Down Expand Up @@ -179,3 +179,7 @@ Repository-only Integer timestamps can use per-field `#[graphql_orm(timestamp =
See [host timestamp inputs, migration compatibility and cancellation limits](../../docs/reference/graphql-orm/repository-only-entities.md#host-managed-integer-timestamps).

Complete SQLite/PostgreSQL text-group pages and SQL-visible ordinary aggregates: see [typed aggregates](../../docs/reference/graphql-orm/typed-aggregates.md).

Version 0.40.0 aligns with the owned runtime migration addition. Released host-managed
timestamps, framework-neutral repository aggregate helpers and static GraphQL SDL
remain unchanged.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions crates/graphql-orm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "graphql-orm"
version = "0.39.0"
version = "0.40.0"
edition = "2024"
description = "Runtime support crate for graphql-orm-macros"
license = "MIT"
Expand Down Expand Up @@ -68,7 +68,7 @@ futures = "0.3"
geo = { version = "0.33", optional = true, default-features = false }
geo-types = { version = "0.7", optional = true }
geojson = { version = "1", optional = true, default-features = true }
graphql-orm-macros = { path = "../graphql-orm-macros", version = "0.39.0", default-features = false }
graphql-orm-macros = { path = "../graphql-orm-macros", version = "0.40.0", default-features = false }
graphql-orm-operation-catalog = { workspace = true }
rust_decimal = { workspace = true }
serde = { version = "1", features = ["derive"] }
Expand Down
8 changes: 7 additions & 1 deletion crates/graphql-orm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ backend:

```toml
[dependencies]
graphql-orm = { git = "https://github.com/Dastari/graphql-orm.git", rev = "<reviewed-full-40-character-commit-sha>", version = "0.39.0", default-features = false, features = ["sqlite"] }
graphql-orm = { git = "https://github.com/Dastari/graphql-orm.git", rev = "<reviewed-full-40-character-commit-sha>", version = "0.40.0", default-features = false, features = ["sqlite"] }
```

This unpublished package has no docs.rs release. Use this Git README and the
Expand Down Expand Up @@ -197,3 +197,9 @@ Repository-only Integer timestamps can use per-field `#[graphql_orm(timestamp =
See [host timestamp inputs, migration compatibility and cancellation limits](../../docs/reference/graphql-orm/repository-only-entities.md#host-managed-integer-timestamps).

Complete SQLite/PostgreSQL text-group pages and SQL-visible ordinary aggregates: see [typed aggregates](../../docs/reference/graphql-orm/typed-aggregates.md).

Owned runtime targets, explicit table ownership, read-only plans and guarded apply
are documented in [runtime migrations](../../docs/reference/graphql-orm/runtime-migrations.md).
Live capability checks reject FK actions/deferral the physical model cannot retain,
including incoming system/unowned constraints, and recheck on the apply transaction.
Static migration APIs remain unchanged.
83 changes: 83 additions & 0 deletions crates/graphql-orm/examples/runtime_owned_migration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//! Run with `cargo run -p graphql-orm --example runtime_owned_migration -- --apply`.
//! The example uses a disposable in-memory SQLite database; the default is preview-only.
#[cfg(feature = "sqlite")]
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
use graphql_orm::db::Database;
use graphql_orm::graphql::orm::*;
let definition: RuntimeSchema = serde_json::from_value(serde_json::json!({
"format_version": 1,
"collections": [{
"id": "notes", "api_type_name": "Note", "api_plural_name": "Notes",
"physical_table": "notes", "primary_key": ["note_id"],
"append_only": false, "retention_purge": false,
"fields": [{
"id": "note_id", "api_name": "id", "physical_column": "id",
"value_kind": "uuid", "nullable": false, "unique": false,
"filterable": false, "sortable": true, "generated": true, "default": null
}, {
"id": "note_label", "api_name": "label", "physical_column": "label",
"value_kind": "string", "nullable": false, "unique": false,
"filterable": true, "sortable": false, "generated": false, "default": null
}],
"relations": [], "indexes": [], "composite_unique": [],
"default_order": [{"field": "note_id", "direction": "asc"}]
}]
}))?;
let schema = std::sync::Arc::new(definition.validate()?);
let target = schema
.physical_schema::<SqliteBackend>(RuntimeMigrationLimits::default())?
.with_static_entities(&[])?; // Add host system metadata here, without replacing host RLS.
let ownership = ManagedTableSet::new(["notes".to_owned()])?;
let db = Database::<SqliteBackend>::connect_sqlite("sqlite::memory:").await?;
let manager = db.schema();
let plan = manager
.plan_owned_migration(
"notes-v1",
"initial notes",
&target,
&ownership,
PlanOptions::strict(),
)
.await?;
println!(
"source={} target={} plan={}",
plan.source_schema_hash(),
plan.target_schema_hash(),
plan.plan_hash()
);
for step in plan.steps() {
println!("{:?}: {}", step.risk(), step.reason());
}
if std::env::args().any(|arg| arg == "--apply") {
let report = manager
.apply_owned_migration(
&plan,
ApplyOptions {
additive_only: true,
..Default::default()
},
)
.await?;
println!("committed {} statements", report.statements_applied);
let no_op = manager
.plan_owned_migration(
"notes-v1",
"replan",
&target,
&ownership,
PlanOptions::strict(),
)
.await?;
assert!(no_op.steps().is_empty());
let environment = manager
.runtime_mutation_environment(schema, &target, &ownership)
.await?;
println!("verified baseline={}", environment.physical_baseline_hash());
}
Ok(())
}
#[cfg(not(feature = "sqlite"))]
fn main() {
println!("This disposable example requires --features sqlite.");
}
Loading