From f58d1a32a3b0685a64379392fb0c70765d75a120 Mon Sep 17 00:00:00 2001 From: Alex Groleau Date: Fri, 17 Apr 2026 05:53:44 -0400 Subject: [PATCH] full database extracton --- src/database/mod.rs | 114 +++++++++++++++++++++++++---------- src/lib.rs | 6 +- src/tests/mod.rs | 142 +++++++++++++++++++++++++++++++------------- 3 files changed, 185 insertions(+), 77 deletions(-) diff --git a/src/database/mod.rs b/src/database/mod.rs index 0696433..71214ab 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -29,12 +29,15 @@ use std::collections::HashMap; use std::sync::Arc; use r#type::Type; +#[derive(serde::Serialize)] pub struct Database { pub enums: HashMap, pub types: HashMap, pub puncs: HashMap, pub relations: HashMap, + #[serde(skip)] pub schemas: HashMap>, + #[serde(skip)] pub executor: Box, } @@ -238,23 +241,30 @@ impl Database { // Phase 2: Synthesize Composed Filter References let mut filter_schemas = Vec::new(); - for type_def in self.types.values() { + for (type_name, type_def) in &self.types { for (id, schema_arc) in &type_def.schemas { // Only run synthesis on actual structured, table-backed boundaries. Exclude subschemas! let base_name = id.split('.').last().unwrap_or(id); let is_table_backed = base_name == type_def.name; if is_table_backed && !id.contains('/') { if let Some(filter_schema) = schema_arc.compile_filter(self, id, errors) { - filter_schemas.push((format!("{}.filter", id), Arc::new(filter_schema))); + filter_schemas.push(( + type_name.clone(), + format!("{}.filter", id), + Arc::new(filter_schema), + )); } } } } let mut filter_ids = Vec::new(); - for (id, filter_arc) in filter_schemas { + for (type_name, id, filter_arc) in filter_schemas { filter_ids.push(id.clone()); - self.schemas.insert(id, filter_arc); + self.schemas.insert(id.clone(), filter_arc.clone()); + if let Some(t) = self.types.get_mut(&type_name) { + t.schemas.insert(id, filter_arc); + } } // Now actively compile the newly injected filters to lock all nested compose references natively @@ -269,50 +279,88 @@ impl Database { } fn collect_schemas(&mut self, errors: &mut Vec) { - let mut to_insert = Vec::new(); + let mut type_insert = Vec::new(); + let mut punc_insert = Vec::new(); + let mut enum_insert = Vec::new(); + let mut global_insert = Vec::new(); // Pass 1: Extract all Schemas structurally off top level definitions into the master registry. // Validate every node recursively via string filters natively! - for type_def in self.types.values() { + for (type_name, type_def) in &self.types { for (id, schema_arc) in &type_def.schemas { - to_insert.push((id.clone(), Arc::clone(schema_arc))); + global_insert.push((id.clone(), Arc::clone(schema_arc))); + let mut local_insert = Vec::new(); crate::database::schema::Schema::collect_schemas( schema_arc, id, id.clone(), - &mut to_insert, - errors, - ); - } - } - for punc_def in self.puncs.values() { - for (id, schema_arc) in &punc_def.schemas { - to_insert.push((id.clone(), Arc::clone(schema_arc))); - crate::database::schema::Schema::collect_schemas( - schema_arc, - id, - id.clone(), - &mut to_insert, - errors, - ); - } - } - for enum_def in self.enums.values() { - for (id, schema_arc) in &enum_def.schemas { - to_insert.push((id.clone(), Arc::clone(schema_arc))); - crate::database::schema::Schema::collect_schemas( - schema_arc, - id, - id.clone(), - &mut to_insert, + &mut local_insert, errors, ); + for entry in &local_insert { + type_insert.push((type_name.clone(), entry.0.clone(), Arc::clone(&entry.1))); + global_insert.push((entry.0.clone(), Arc::clone(&entry.1))); + } } } - for (id, schema_arc) in to_insert { + for (punc_name, punc_def) in &self.puncs { + for (id, schema_arc) in &punc_def.schemas { + global_insert.push((id.clone(), Arc::clone(schema_arc))); + let mut local_insert = Vec::new(); + crate::database::schema::Schema::collect_schemas( + schema_arc, + id, + id.clone(), + &mut local_insert, + errors, + ); + for entry in &local_insert { + punc_insert.push((punc_name.clone(), entry.0.clone(), Arc::clone(&entry.1))); + global_insert.push((entry.0.clone(), Arc::clone(&entry.1))); + } + } + } + + for (enum_name, enum_def) in &self.enums { + for (id, schema_arc) in &enum_def.schemas { + global_insert.push((id.clone(), Arc::clone(schema_arc))); + let mut local_insert = Vec::new(); + crate::database::schema::Schema::collect_schemas( + schema_arc, + id, + id.clone(), + &mut local_insert, + errors, + ); + for entry in &local_insert { + enum_insert.push((enum_name.clone(), entry.0.clone(), Arc::clone(&entry.1))); + global_insert.push((entry.0.clone(), Arc::clone(&entry.1))); + } + } + } + + // Apply global inserts + for (id, schema_arc) in global_insert { self.schemas.insert(id, schema_arc); } + + // Apply local scopes + for (origin_name, id, schema_arc) in type_insert { + if let Some(t) = self.types.get_mut(&origin_name) { + t.schemas.insert(id, schema_arc); + } + } + for (origin_name, id, schema_arc) in punc_insert { + if let Some(p) = self.puncs.get_mut(&origin_name) { + p.schemas.insert(id, schema_arc); + } + } + for (origin_name, id, schema_arc) in enum_insert { + if let Some(e) = self.enums.get_mut(&origin_name) { + e.schemas.insert(id, schema_arc); + } + } } /// Inspects the Postgres pg_constraint relations catalog to securely identify diff --git a/src/lib.rs b/src/lib.rs index cbce7ab..6d9e11d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,7 +109,7 @@ pub fn jspg_validate(schema_id: &str, instance: JsonB) -> JsonB { } #[cfg_attr(not(test), pg_extern)] -pub fn jspg_schemas() -> JsonB { +pub fn jspg_database() -> JsonB { let engine_opt = { let lock = GLOBAL_JSPG.read().unwrap(); lock.clone() @@ -117,9 +117,9 @@ pub fn jspg_schemas() -> JsonB { match engine_opt { Some(engine) => { - let schemas_json = serde_json::to_value(&engine.database.schemas) + let database_json = serde_json::to_value(&engine.database) .unwrap_or(serde_json::Value::Object(serde_json::Map::new())); - let drop = crate::drop::Drop::success_with_val(schemas_json); + let drop = crate::drop::Drop::success_with_val(database_json); JsonB(serde_json::to_value(drop).unwrap()) } None => jspg_failure(), diff --git a/src/tests/mod.rs b/src/tests/mod.rs index d911e2c..1830140 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -81,54 +81,114 @@ fn test_library_api() { }) ); - // 3. Validate jspg_schemas - let schemas_drop = jspg_schemas(); + // 3. Validate jspg_database mapping natively! + let db_drop = jspg_database(); assert_eq!( - schemas_drop.0, + db_drop.0, json!({ "type": "drop", "response": { - "source_schema": { - "type": "object", - "properties": { - "type": { "type": "string" }, - "name": { "type": "string" }, - "target": { - "type": "target_schema", - "compiledPropertyNames": ["value"] - } - }, - "required": ["name"], - "compiledPropertyNames": ["name", "target", "type"], - "compiledEdges": { - "target": { - "constraint": "fk_test_target", - "forward": true - } + "enums": {}, + "puncs": {}, + "relations": { + "fk_test_target": { + "constraint": "fk_test_target", + "destination_columns": ["id"], + "destination_type": "target_schema", + "prefix": "target", + "source_columns": ["target_id"], + "source_type": "source_schema" } }, - "source_schema.filter": { - "type": "object", - "properties": { - "type": { "type": ["string.condition", "null"] }, - "name": { "type": ["string.condition", "null"] }, - "target": { "type": ["target_schema.filter", "null"] } + "types": { + "source_schema": { + "default_fields": [], + "field_types": null, + "fields": [], + "grouped_fields": null, + "hierarchy": ["source_schema", "entity"], + "historical": false, + "id": "", + "longevity": null, + "lookup_fields": [], + "module": "", + "name": "source_schema", + "notify": false, + "null_fields": [], + "ownable": false, + "relationship": false, + "schemas": { + "source_schema": { + "compiledEdges": { + "target": { + "constraint": "fk_test_target", + "forward": true + } + }, + "compiledPropertyNames": ["name", "target", "type"], + "properties": { + "name": { "type": "string" }, + "target": { + "compiledPropertyNames": ["value"], + "type": "target_schema" + }, + "type": { "type": "string" } + }, + "required": ["name"], + "type": "object" + }, + "source_schema.filter": { + "compiledPropertyNames": ["name", "target", "type"], + "properties": { + "name": { "type": ["string.condition", "null"] }, + "target": { "type": ["target_schema.filter", "null"] }, + "type": { "type": ["string.condition", "null"] } + }, + "type": "object" + } + }, + "sensitive": false, + "source": "", + "type": "", + "variations": ["source_schema"] }, - "compiledPropertyNames": ["name", "target", "type"] - }, - "target_schema": { - "type": "object", - "properties": { - "value": { "type": "number" } - }, - "compiledPropertyNames": ["value"] - }, - "target_schema.filter": { - "type": "object", - "properties": { - "value": { "type": ["number.condition", "null"] } - }, - "compiledPropertyNames": ["value"] + "target_schema": { + "default_fields": [], + "field_types": null, + "fields": [], + "grouped_fields": null, + "hierarchy": ["target_schema", "entity"], + "historical": false, + "id": "", + "longevity": null, + "lookup_fields": [], + "module": "", + "name": "target_schema", + "notify": false, + "null_fields": [], + "ownable": false, + "relationship": false, + "schemas": { + "target_schema": { + "compiledPropertyNames": ["value"], + "properties": { + "value": { "type": "number" } + }, + "type": "object" + }, + "target_schema.filter": { + "compiledPropertyNames": ["value"], + "properties": { + "value": { "type": ["number.condition", "null"] } + }, + "type": "object" + } + }, + "sensitive": false, + "source": "", + "type": "", + "variations": ["target_schema"] + } } } })