Skip to content
Merged
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
6 changes: 3 additions & 3 deletions Cargo.lock

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

9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,11 @@ lance-graph-contract = { git = "https://github.com/AdaWorldAPI/lance-graph.git"
# FieldDefinition, IndexDefinition, Kind, Schema}` are the upstream input
# types for the C16c bridge (the `From<op_surreal_ast::*> for catalog::*`
# impls in surrealdb-core::catalog::op_bridge, gated by `op-bridge` feature).
# Pinned to PR Z's branch until that PR merges; switch to `branch = "main"`
# after merge. The crate is zero-async-deps (depends on lance-graph-contract
# only), so pulling it in is cheap.
op-surreal-ast = { git = "https://github.com/AdaWorldAPI/openproject-nexgen-rs", branch = "claude/op-surreal-ast-from-triples" }
# Pinned to the D-AR-5.2 nexgen branch (PR #29) which adds the 7 new Kind
# variants (String/Bool/Float/Decimal/Datetime/Bytes/Uuid) + the
# `FieldDefinition.assert` slot. Flip to `branch = "main"` after PR #29
# merges. The crate is zero-async-deps so the pull stays cheap.
op-surreal-ast = { git = "https://github.com/AdaWorldAPI/openproject-nexgen-rs", branch = "claude/op-surreal-ast-field-types-stacked" }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve AST field assertions in the bridge

This dependency bump selects the AST branch that now carries FieldDefinition.assert and populates it from validates_constraint, but From<ast::FieldDefinition> still only transfers the name/table/kind via .with_kind(Some(kind)). For schemas containing validations, the AST can render ASSERT $value != NONE while the bridged catalog field silently drops that clause, so the catalog accepts values that the generated schema intended to reject; please map the new assertion slot into the catalog with_assert path or avoid this branch until that is supported.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in follow-up PR #39 (04fd090). From<ast::FieldDefinition> for catalog::FieldDefinition now calls .with_assert(rails_assert_to_expr(s)) so the catalog carries the same $value != NONE constraint the AST renders. Lowering is structural (Binary(Param "value", NotEqual, Literal::None)) rather than parser-based — one known AR-shape expression today, one explicit arm; unknown expressions fall back to None (safe no-op preserving the pre-PR behaviour).

PR #28's normalize: annotation that renders as $value != NONE /* normalized */ is handled by stripping the block comment + canonicalising whitespace before matching, so both forms lower to the same Expr.

Regression tests:

  • d_ar_6_3_field_definition_bridges_assert_clause
  • d_ar_6_3_assert_normalized_comment_is_stripped
  • d_ar_6_3_no_assert_when_ast_field_has_none
  • d_ar_6_3_unknown_assert_string_lowers_to_none

Thanks for the catch — declared-vs-enforced drift is exactly what the bridge exists to prevent.

nix = { version = "0.30.1", default-features = false }
num-traits = "0.2.19"
num_cpus = "1.17.0"
Expand Down
51 changes: 51 additions & 0 deletions surrealdb/core/src/catalog/op_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ impl From<ast::Kind> for CatalogKind {
match k {
ast::Kind::Any => CatalogKind::Any,
ast::Kind::Int => CatalogKind::Int,
// D-AR-6.2: 7 scalar variants added in PR #29 (ast crate's
// D-AR-5.2). All map 1:1 to surrealdb-core's catalog Kind
// variants — these were the Rails-AR types the AST didn't
// surface until the `field_type` predicate landed.
ast::Kind::String => CatalogKind::String,
ast::Kind::Bool => CatalogKind::Bool,
ast::Kind::Float => CatalogKind::Float,
ast::Kind::Decimal => CatalogKind::Decimal,
ast::Kind::Datetime => CatalogKind::Datetime,
ast::Kind::Bytes => CatalogKind::Bytes,
ast::Kind::Uuid => CatalogKind::Uuid,
ast::Kind::Record(targets) => {
let tables = targets.into_iter().map(TableName::from).collect();
CatalogKind::Record(tables)
Expand Down Expand Up @@ -251,6 +262,46 @@ mod tests {
assert_eq!(format!("{}", targets[0]), "Project");
}

/// **D-AR-6.2** — the 7 scalar variants added in PR #29 map 1:1
/// to surrealdb-core's catalog Kind variants.
#[test]
fn d_ar_6_2_scalar_variants_map_one_to_one() {
let cases: Vec<(ast::Kind, CatalogKind)> = vec![
(ast::Kind::String, CatalogKind::String),
(ast::Kind::Bool, CatalogKind::Bool),
(ast::Kind::Float, CatalogKind::Float),
(ast::Kind::Decimal, CatalogKind::Decimal),
(ast::Kind::Datetime, CatalogKind::Datetime),
(ast::Kind::Bytes, CatalogKind::Bytes),
(ast::Kind::Uuid, CatalogKind::Uuid),
];
for (ast_kind, expected) in cases {
let bridged: CatalogKind = ast_kind.clone().into();
assert_eq!(
bridged, expected,
"ast::Kind::{ast_kind:?} did not map to {expected:?}",
);
}
}

/// **D-AR-6.2** — the option-wrapped form (Rails-nullable, per
/// codex P1 PR #29 fix) bridges through correctly: an AST
/// `Option<String>` becomes catalog `Either(None, String)`.
#[test]
fn d_ar_6_2_option_wrapped_scalar_bridges_via_either() {
let ast_optional_string = ast::Kind::String.optional();
let bridged: CatalogKind = ast_optional_string.into();
let CatalogKind::Either(arms) = bridged else {
panic!("expected Either(None, String); got non-Either");
};
assert_eq!(arms.len(), 2);
// One arm is None, the other String.
let has_none = arms.iter().any(|k| matches!(k, CatalogKind::None));
let has_string = arms.iter().any(|k| matches!(k, CatalogKind::String));
assert!(has_none, "Option<T> must include None arm");
assert!(has_string, "Option<String> must include String arm");
}

#[test]
fn kind_option_nests_correctly() {
let k: CatalogKind = ast::Kind::Int.optional().into();
Expand Down