Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d3de3961c1 | |||
| d0ae6eeddd | |||
| e741a7197f | |||
| cf3cabd1f5 | |||
| d608f5e963 |
@ -87,6 +87,34 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"schedule": {
|
||||||
|
"type": [
|
||||||
|
"opening_hours",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"opening_hours": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"open": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"seasons": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "season"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"season": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"label": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -211,6 +239,13 @@
|
|||||||
"gender": {},
|
"gender": {},
|
||||||
"gender.condition": {
|
"gender.condition": {
|
||||||
"type": "condition",
|
"type": "condition",
|
||||||
|
"compiledPropertyNames": [
|
||||||
|
"kind",
|
||||||
|
"$eq",
|
||||||
|
"$ne",
|
||||||
|
"$of",
|
||||||
|
"$nof"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"$eq": {
|
"$eq": {
|
||||||
"type": [
|
"type": [
|
||||||
@ -224,15 +259,6 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"$of": {
|
|
||||||
"type": [
|
|
||||||
"array",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"items": {
|
|
||||||
"type": "gender"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"$nof": {
|
"$nof": {
|
||||||
"type": [
|
"type": [
|
||||||
"array",
|
"array",
|
||||||
@ -241,158 +267,20 @@
|
|||||||
"items": {
|
"items": {
|
||||||
"type": "gender"
|
"type": "gender"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"$of": {
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"items": {
|
||||||
|
"type": "gender"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"compiledPropertyNames": [
|
|
||||||
"kind",
|
|
||||||
"$eq",
|
|
||||||
"$ne",
|
|
||||||
"$of",
|
|
||||||
"$nof"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"person": {},
|
"person": {},
|
||||||
"person.filter": {
|
"person.filter": {
|
||||||
"type": "filter",
|
|
||||||
"properties": {
|
|
||||||
"first_name": {
|
|
||||||
"type": [
|
|
||||||
"string.condition",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"compiledPropertyNames": [
|
|
||||||
"kind",
|
|
||||||
"$eq"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"age": {
|
|
||||||
"type": [
|
|
||||||
"integer.condition",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"compiledPropertyNames": [
|
|
||||||
"kind",
|
|
||||||
"$eq"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"billing_address": {
|
|
||||||
"type": [
|
|
||||||
"address.filter",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"compiledPropertyNames": [
|
|
||||||
"city",
|
|
||||||
"$and",
|
|
||||||
"$or"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"gender": {
|
|
||||||
"type": [
|
|
||||||
"gender.condition",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"compiledPropertyNames": [
|
|
||||||
"kind",
|
|
||||||
"$eq",
|
|
||||||
"$ne",
|
|
||||||
"$of",
|
|
||||||
"$nof"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"birth_date": {
|
|
||||||
"type": [
|
|
||||||
"date.condition",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"compiledPropertyNames": [
|
|
||||||
"kind",
|
|
||||||
"$eq"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"uuid_field": {
|
|
||||||
"type": [
|
|
||||||
"uuid.condition",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"compiledPropertyNames": [
|
|
||||||
"kind",
|
|
||||||
"$eq"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"type": [
|
|
||||||
"string.condition",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"compiledPropertyNames": [
|
|
||||||
"kind",
|
|
||||||
"$eq"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ad_hoc": {
|
|
||||||
"type": [
|
|
||||||
"filter",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"foo": {
|
|
||||||
"type": [
|
|
||||||
"string.condition",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"compiledPropertyNames": [
|
|
||||||
"kind",
|
|
||||||
"$eq"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compiledPropertyNames": [
|
|
||||||
"foo"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"$and": {
|
|
||||||
"type": [
|
|
||||||
"array",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"items": {
|
|
||||||
"type": "person.filter",
|
|
||||||
"compiledPropertyNames": [
|
|
||||||
"first_name",
|
|
||||||
"age",
|
|
||||||
"billing_address",
|
|
||||||
"gender",
|
|
||||||
"birth_date",
|
|
||||||
"uuid_field",
|
|
||||||
"tags",
|
|
||||||
"ad_hoc",
|
|
||||||
"$and",
|
|
||||||
"$or"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"$or": {
|
|
||||||
"type": [
|
|
||||||
"array",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"items": {
|
|
||||||
"type": "person.filter",
|
|
||||||
"compiledPropertyNames": [
|
|
||||||
"first_name",
|
|
||||||
"age",
|
|
||||||
"billing_address",
|
|
||||||
"gender",
|
|
||||||
"birth_date",
|
|
||||||
"uuid_field",
|
|
||||||
"tags",
|
|
||||||
"ad_hoc",
|
|
||||||
"$and",
|
|
||||||
"$or"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"compiledPropertyNames": [
|
"compiledPropertyNames": [
|
||||||
"first_name",
|
"first_name",
|
||||||
"age",
|
"age",
|
||||||
@ -404,34 +292,133 @@
|
|||||||
"ad_hoc",
|
"ad_hoc",
|
||||||
"$and",
|
"$and",
|
||||||
"$or"
|
"$or"
|
||||||
]
|
],
|
||||||
|
"properties": {
|
||||||
|
"$and": {
|
||||||
|
"items": {
|
||||||
|
"compiledPropertyNames": [
|
||||||
|
"first_name",
|
||||||
|
"age",
|
||||||
|
"billing_address",
|
||||||
|
"gender",
|
||||||
|
"birth_date",
|
||||||
|
"uuid_field",
|
||||||
|
"tags",
|
||||||
|
"ad_hoc",
|
||||||
|
"$and",
|
||||||
|
"$or"
|
||||||
|
],
|
||||||
|
"type": "person.filter"
|
||||||
|
},
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"$or": {
|
||||||
|
"items": {
|
||||||
|
"compiledPropertyNames": [
|
||||||
|
"first_name",
|
||||||
|
"age",
|
||||||
|
"billing_address",
|
||||||
|
"gender",
|
||||||
|
"birth_date",
|
||||||
|
"uuid_field",
|
||||||
|
"tags",
|
||||||
|
"ad_hoc",
|
||||||
|
"$and",
|
||||||
|
"$or"
|
||||||
|
],
|
||||||
|
"type": "person.filter"
|
||||||
|
},
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ad_hoc": {
|
||||||
|
"compiledPropertyNames": [
|
||||||
|
"foo"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"foo": {
|
||||||
|
"type": [
|
||||||
|
"string.condition",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": [
|
||||||
|
"filter",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"age": {
|
||||||
|
"type": [
|
||||||
|
"integer.condition",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"billing_address": {
|
||||||
|
"type": [
|
||||||
|
"address.filter",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"birth_date": {
|
||||||
|
"type": [
|
||||||
|
"date.condition",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"uuid_field": {
|
||||||
|
"type": [
|
||||||
|
"uuid.condition",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"first_name": {
|
||||||
|
"type": [
|
||||||
|
"string.condition",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gender": {
|
||||||
|
"type": [
|
||||||
|
"gender.condition",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": [
|
||||||
|
"string.condition",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "filter"
|
||||||
},
|
},
|
||||||
"address": {},
|
"address": {},
|
||||||
"address.filter": {
|
"address.filter": {
|
||||||
"type": "filter",
|
"type": "filter",
|
||||||
|
"compiledPropertyNames": [
|
||||||
|
"city",
|
||||||
|
"$and",
|
||||||
|
"$or"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"city": {
|
|
||||||
"type": [
|
|
||||||
"string.condition",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"compiledPropertyNames": [
|
|
||||||
"kind",
|
|
||||||
"$eq"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"$and": {
|
"$and": {
|
||||||
"type": [
|
"type": [
|
||||||
"array",
|
"array",
|
||||||
"null"
|
"null"
|
||||||
],
|
],
|
||||||
"items": {
|
"items": {
|
||||||
"type": "address.filter",
|
|
||||||
"compiledPropertyNames": [
|
"compiledPropertyNames": [
|
||||||
"city",
|
"city",
|
||||||
"$and",
|
"$and",
|
||||||
"$or"
|
"$or"
|
||||||
]
|
],
|
||||||
|
"type": "address.filter"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"$or": {
|
"$or": {
|
||||||
@ -440,20 +427,21 @@
|
|||||||
"null"
|
"null"
|
||||||
],
|
],
|
||||||
"items": {
|
"items": {
|
||||||
"type": "address.filter",
|
|
||||||
"compiledPropertyNames": [
|
"compiledPropertyNames": [
|
||||||
"city",
|
"city",
|
||||||
"$and",
|
"$and",
|
||||||
"$or"
|
"$or"
|
||||||
]
|
],
|
||||||
|
"type": "address.filter"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"city": {
|
||||||
|
"type": [
|
||||||
|
"string.condition",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"compiledPropertyNames": [
|
|
||||||
"city",
|
|
||||||
"$and",
|
|
||||||
"$or"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"condition": {},
|
"condition": {},
|
||||||
"filter": {},
|
"filter": {},
|
||||||
@ -464,47 +452,28 @@
|
|||||||
"search": {},
|
"search": {},
|
||||||
"search.filter": {
|
"search.filter": {
|
||||||
"type": "filter",
|
"type": "filter",
|
||||||
|
"compiledPropertyNames": [
|
||||||
|
"kind",
|
||||||
|
"name",
|
||||||
|
"filter",
|
||||||
|
"$and",
|
||||||
|
"$or"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"kind": {
|
|
||||||
"type": [
|
|
||||||
"string.condition",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"compiledPropertyNames": [
|
|
||||||
"kind",
|
|
||||||
"$eq"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"type": [
|
|
||||||
"string.condition",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"compiledPropertyNames": [
|
|
||||||
"kind",
|
|
||||||
"$eq"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"type": [
|
|
||||||
"$kind.filter",
|
|
||||||
"null"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"$and": {
|
"$and": {
|
||||||
"type": [
|
"type": [
|
||||||
"array",
|
"array",
|
||||||
"null"
|
"null"
|
||||||
],
|
],
|
||||||
"items": {
|
"items": {
|
||||||
"type": "search.filter",
|
|
||||||
"compiledPropertyNames": [
|
"compiledPropertyNames": [
|
||||||
"kind",
|
"kind",
|
||||||
"name",
|
"name",
|
||||||
"filter",
|
"filter",
|
||||||
"$and",
|
"$and",
|
||||||
"$or"
|
"$or"
|
||||||
]
|
],
|
||||||
|
"type": "search.filter"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"$or": {
|
"$or": {
|
||||||
@ -513,25 +482,38 @@
|
|||||||
"null"
|
"null"
|
||||||
],
|
],
|
||||||
"items": {
|
"items": {
|
||||||
"type": "search.filter",
|
|
||||||
"compiledPropertyNames": [
|
"compiledPropertyNames": [
|
||||||
"kind",
|
"kind",
|
||||||
"name",
|
"name",
|
||||||
"filter",
|
"filter",
|
||||||
"$and",
|
"$and",
|
||||||
"$or"
|
"$or"
|
||||||
]
|
],
|
||||||
|
"type": "search.filter"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"filter": {
|
||||||
|
"type": [
|
||||||
|
"$kind.filter",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"kind": {
|
||||||
|
"type": [
|
||||||
|
"string.condition",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": [
|
||||||
|
"string.condition",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"compiledPropertyNames": [
|
},
|
||||||
"kind",
|
"opening_hours": {},
|
||||||
"name",
|
"season": {}
|
||||||
"filter",
|
|
||||||
"$and",
|
|
||||||
"$or"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -191,9 +191,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "request",
|
"name": "request",
|
||||||
"field_types": {
|
|
||||||
"inv": "jsonb"
|
|
||||||
},
|
|
||||||
"schemas": {
|
"schemas": {
|
||||||
"request": {
|
"request": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
1
flow
1
flow
@ -36,6 +36,7 @@ pgrx-down() {
|
|||||||
info "Taking pgrx down..."
|
info "Taking pgrx down..."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
local version
|
local version
|
||||||
version=$(get-version) || return $?
|
version=$(get-version) || return $?
|
||||||
|
|||||||
@ -29,21 +29,10 @@ impl Schema {
|
|||||||
if db.types.contains_key(&base_type_name) {
|
if db.types.contains_key(&base_type_name) {
|
||||||
parent_type_name = Some(base_type_name);
|
parent_type_name = Some(base_type_name);
|
||||||
}
|
}
|
||||||
} else if let Some(t_or_arr) = &self.obj.type_ {
|
} else if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &self.obj.type_ {
|
||||||
match t_or_arr {
|
// 3. Nested graphs trust their explicit struct pointer reference
|
||||||
crate::database::object::SchemaTypeOrArray::Single(t) => {
|
if !crate::database::object::is_primitive_type(t) {
|
||||||
if !crate::database::object::is_primitive_type(t) {
|
parent_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
|
||||||
parent_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
crate::database::object::SchemaTypeOrArray::Multiple(types) => {
|
|
||||||
for t in types {
|
|
||||||
if !crate::database::object::is_primitive_type(t) {
|
|
||||||
parent_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,23 +46,13 @@ impl Schema {
|
|||||||
let mut is_array = false;
|
let mut is_array = false;
|
||||||
|
|
||||||
// Structurally unpack the inner target entity if the object maps to an array list
|
// Structurally unpack the inner target entity if the object maps to an array list
|
||||||
if let Some(t_or_arr) = &prop_schema.obj.type_ {
|
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) =
|
||||||
match t_or_arr {
|
&prop_schema.obj.type_
|
||||||
crate::database::object::SchemaTypeOrArray::Single(t) => {
|
{
|
||||||
if t == "array" {
|
if t == "array" {
|
||||||
is_array = true;
|
is_array = true;
|
||||||
if let Some(items) = &prop_schema.obj.items {
|
if let Some(items) = &prop_schema.obj.items {
|
||||||
target_schema = items.clone();
|
target_schema = items.clone();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
crate::database::object::SchemaTypeOrArray::Multiple(types) => {
|
|
||||||
if types.contains(&"array".to_string()) {
|
|
||||||
is_array = true;
|
|
||||||
if let Some(items) = &prop_schema.obj.items {
|
|
||||||
target_schema = items.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,21 +60,11 @@ impl Schema {
|
|||||||
// Determine the physical Postgres table backing the nested child schema recursively
|
// Determine the physical Postgres table backing the nested child schema recursively
|
||||||
if let Some(family) = &target_schema.obj.family {
|
if let Some(family) = &target_schema.obj.family {
|
||||||
child_type_name = Some(family.split('.').next_back().unwrap_or(family).to_string());
|
child_type_name = Some(family.split('.').next_back().unwrap_or(family).to_string());
|
||||||
} else if let Some(t_or_arr) = &target_schema.obj.type_ {
|
} else if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) =
|
||||||
match t_or_arr {
|
&target_schema.obj.type_
|
||||||
crate::database::object::SchemaTypeOrArray::Single(t) => {
|
{
|
||||||
if !crate::database::object::is_primitive_type(t) {
|
if !crate::database::object::is_primitive_type(t) {
|
||||||
child_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
|
child_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
|
||||||
}
|
|
||||||
}
|
|
||||||
crate::database::object::SchemaTypeOrArray::Multiple(types) => {
|
|
||||||
for t in types {
|
|
||||||
if !crate::database::object::is_primitive_type(t) {
|
|
||||||
child_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if let Some(arr) = &target_schema.obj.one_of {
|
} else if let Some(arr) = &target_schema.obj.one_of {
|
||||||
if let Some(first) = arr.first() {
|
if let Some(first) = arr.first() {
|
||||||
|
|||||||
@ -165,8 +165,16 @@ impl Schema {
|
|||||||
} else if db.enums.contains_key(custom) {
|
} else if db.enums.contains_key(custom) {
|
||||||
Some(vec![format!("{}.condition", custom)])
|
Some(vec![format!("{}.condition", custom)])
|
||||||
} else {
|
} else {
|
||||||
// Assume anything else is a Relational cross-boundary that already has its own .filter dynamically built
|
// Only a Table-Backed boundary has a synthesized Composed Filter to proxy to.
|
||||||
Some(vec![format!("{}.filter", custom)])
|
// A Field-Backed JSONB Bubble has none — omit it like an inline object rather
|
||||||
|
// than emit a dangling proxy reference, which breaks eager consumers of the
|
||||||
|
// exported registry (downstream code generators).
|
||||||
|
let base = custom.split('.').next_back().unwrap_or(custom);
|
||||||
|
if db.types.contains_key(base) {
|
||||||
|
Some(vec![format!("{}.filter", custom)])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,6 @@ impl Schema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut props = IndexMap::new();
|
let mut props = IndexMap::new();
|
||||||
let mut inherited_edges = IndexMap::new();
|
|
||||||
|
|
||||||
// 1. Resolve INHERITANCE dependencies first
|
// 1. Resolve INHERITANCE dependencies first
|
||||||
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &self.obj.type_ {
|
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &self.obj.type_ {
|
||||||
@ -50,9 +49,6 @@ impl Schema {
|
|||||||
if let Some(p_props) = parent.obj.compiled_properties.get() {
|
if let Some(p_props) = parent.obj.compiled_properties.get() {
|
||||||
props.extend(p_props.clone());
|
props.extend(p_props.clone());
|
||||||
}
|
}
|
||||||
if let Some(p_edges) = parent.obj.compiled_edges.get() {
|
|
||||||
inherited_edges.extend(p_edges.clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,12 +77,6 @@ impl Schema {
|
|||||||
if !crate::database::object::is_primitive_type(t) && !t.starts_with('$') {
|
if !crate::database::object::is_primitive_type(t) && !t.starts_with('$') {
|
||||||
if let Some(parent) = db.schemas.get(t).cloned() {
|
if let Some(parent) = db.schemas.get(t).cloned() {
|
||||||
parent.as_ref().compile(db, t, t.clone(), errors);
|
parent.as_ref().compile(db, t, t.clone(), errors);
|
||||||
if let Some(p_props) = parent.obj.compiled_properties.get() {
|
|
||||||
props.extend(p_props.clone());
|
|
||||||
}
|
|
||||||
if let Some(p_edges) = parent.obj.compiled_edges.get() {
|
|
||||||
inherited_edges.extend(p_edges.clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,10 +116,7 @@ impl Schema {
|
|||||||
let _ = self.obj.compiled_property_names.set(names);
|
let _ = self.obj.compiled_property_names.set(names);
|
||||||
|
|
||||||
// 5. Compute Edges natively
|
// 5. Compute Edges natively
|
||||||
let mut schema_edges = self.compile_edges(db, root_id, &path, &props, errors);
|
let schema_edges = self.compile_edges(db, root_id, &path, &props, errors);
|
||||||
for (k, v) in inherited_edges {
|
|
||||||
schema_edges.entry(k).or_insert(v);
|
|
||||||
}
|
|
||||||
let _ = self.obj.compiled_edges.set(schema_edges);
|
let _ = self.obj.compiled_edges.set(schema_edges);
|
||||||
|
|
||||||
// 5. Build our inline children properties recursively NOW! (Depth-first search)
|
// 5. Build our inline children properties recursively NOW! (Depth-first search)
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use crate::database::Database;
|
use crate::database::Database;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use serde_json::Value;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct Compiler<'a> {
|
pub struct Compiler<'a> {
|
||||||
@ -87,29 +86,13 @@ impl<'a> Compiler<'a> {
|
|||||||
.next_back()
|
.next_back()
|
||||||
.unwrap_or(family_target);
|
.unwrap_or(family_target);
|
||||||
resolved_type = self.db.types.get(base_type_name);
|
resolved_type = self.db.types.get(base_type_name);
|
||||||
} else if let Some(t_or_arr) = &items.obj.type_ {
|
} else if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &items.obj.type_
|
||||||
match t_or_arr {
|
{
|
||||||
crate::database::object::SchemaTypeOrArray::Single(t) => {
|
if !crate::database::object::is_primitive_type(t) {
|
||||||
if !crate::database::object::is_primitive_type(t) {
|
resolved_type = self
|
||||||
resolved_type = self
|
.db
|
||||||
.db
|
.types
|
||||||
.types
|
.get(&t.split('.').next_back().unwrap_or(t).to_string());
|
||||||
.get(&t.split('.').next_back().unwrap_or(t).to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
crate::database::object::SchemaTypeOrArray::Multiple(types) => {
|
|
||||||
for t in types {
|
|
||||||
if !crate::database::object::is_primitive_type(t) {
|
|
||||||
resolved_type = self
|
|
||||||
.db
|
|
||||||
.types
|
|
||||||
.get(&t.split('.').next_back().unwrap_or(t).to_string());
|
|
||||||
if resolved_type.is_some() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,25 +135,12 @@ impl<'a> Compiler<'a> {
|
|||||||
.next_back()
|
.next_back()
|
||||||
.unwrap_or(family_target);
|
.unwrap_or(family_target);
|
||||||
resolved_type = self.db.types.get(base_type_name);
|
resolved_type = self.db.types.get(base_type_name);
|
||||||
} else if let Some(t_or_arr) = &node.schema.obj.type_ {
|
} else if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) =
|
||||||
match t_or_arr {
|
&node.schema.obj.type_
|
||||||
crate::database::object::SchemaTypeOrArray::Single(t) => {
|
{
|
||||||
if !crate::database::object::is_primitive_type(t) {
|
if !crate::database::object::is_primitive_type(t) {
|
||||||
let base_type_name = t.split('.').next_back().unwrap_or(t).to_string();
|
let base_type_name = t.split('.').next_back().unwrap_or(t).to_string();
|
||||||
resolved_type = self.db.types.get(&base_type_name);
|
resolved_type = self.db.types.get(&base_type_name);
|
||||||
}
|
|
||||||
}
|
|
||||||
crate::database::object::SchemaTypeOrArray::Multiple(types) => {
|
|
||||||
for t in types {
|
|
||||||
if !crate::database::object::is_primitive_type(t) {
|
|
||||||
let base_type_name = t.split('.').next_back().unwrap_or(t).to_string();
|
|
||||||
resolved_type = self.db.types.get(&base_type_name);
|
|
||||||
if resolved_type.is_some() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -469,9 +439,6 @@ impl<'a> Compiler<'a> {
|
|||||||
Some(crate::database::object::SchemaTypeOrArray::Single(s)) => {
|
Some(crate::database::object::SchemaTypeOrArray::Single(s)) => {
|
||||||
!crate::database::object::is_primitive_type(s)
|
!crate::database::object::is_primitive_type(s)
|
||||||
}
|
}
|
||||||
Some(crate::database::object::SchemaTypeOrArray::Multiple(v)) => {
|
|
||||||
v.iter().any(|s| !crate::database::object::is_primitive_type(s))
|
|
||||||
}
|
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -582,9 +549,8 @@ impl<'a> Compiler<'a> {
|
|||||||
where_clauses.push(format!("NOT {}.archived", entity_alias));
|
where_clauses.push(format!("NOT {}.archived", entity_alias));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.compile_filter_conditions(r#type, type_aliases, &node, &base_alias, &mut where_clauses);
|
self.compile_filter_conditions(r#type, type_aliases, &node, &base_alias, &mut where_clauses)?;
|
||||||
self.compile_polymorphic_bounds(r#type, type_aliases, &node, &mut where_clauses);
|
self.compile_polymorphic_bounds(r#type, type_aliases, &node, &mut where_clauses);
|
||||||
self.compile_static_property_conditions(r#type, type_aliases, &node, &base_alias, &mut where_clauses);
|
|
||||||
|
|
||||||
let start_len = where_clauses.len();
|
let start_len = where_clauses.len();
|
||||||
self.compile_relation_conditions(
|
self.compile_relation_conditions(
|
||||||
@ -723,6 +689,11 @@ impl<'a> Compiler<'a> {
|
|||||||
|| pg_type.contains("int")
|
|| pg_type.contains("int")
|
||||||
|| pg_type == "real"
|
|| pg_type == "real"
|
||||||
|| pg_type == "double precision"
|
|| pg_type == "double precision"
|
||||||
|
// pg catalog typnames for real / double precision — the SQL
|
||||||
|
// names above never appear in field_types (they come from
|
||||||
|
// pg_type.typname), so these are what actually arrives
|
||||||
|
|| pg_type == "float4"
|
||||||
|
|| pg_type == "float8"
|
||||||
{
|
{
|
||||||
cast = "::numeric";
|
cast = "::numeric";
|
||||||
} else if pg_type == "text" || pg_type.contains("char") {
|
} else if pg_type == "text" || pg_type.contains("char") {
|
||||||
@ -749,7 +720,7 @@ impl<'a> Compiler<'a> {
|
|||||||
node: &Node,
|
node: &Node,
|
||||||
base_alias: &str,
|
base_alias: &str,
|
||||||
where_clauses: &mut Vec<String>,
|
where_clauses: &mut Vec<String>,
|
||||||
) {
|
) -> Result<(), String> {
|
||||||
for (i, filter_key) in self.filter_keys.iter().enumerate() {
|
for (i, filter_key) in self.filter_keys.iter().enumerate() {
|
||||||
let mut parts = filter_key.split(':');
|
let mut parts = filter_key.split(':');
|
||||||
let full_field_path = parts.next().unwrap_or(filter_key);
|
let full_field_path = parts.next().unwrap_or(filter_key);
|
||||||
@ -779,6 +750,83 @@ impl<'a> Compiler<'a> {
|
|||||||
let param_index = i + 1;
|
let param_index = i + 1;
|
||||||
let p_val = format!("${}#>>'{{}}'", param_index);
|
let p_val = format!("${}#>>'{{}}'", param_index);
|
||||||
|
|
||||||
|
// jsonb columns never type-check against the text parameter, and for
|
||||||
|
// array-valued properties (tag lists) the meaning of a condition is
|
||||||
|
// CONTAINMENT, not equality. Compile them explicitly; reject the rest
|
||||||
|
// loudly at compile time instead of failing at execution.
|
||||||
|
let is_jsonb = r#type
|
||||||
|
.field_types
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|v| v.as_object())
|
||||||
|
.and_then(|ft| ft.get(field_name))
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
== Some("jsonb");
|
||||||
|
if is_jsonb {
|
||||||
|
// Compiled schemas normalize to the list form ("type": ["array"]),
|
||||||
|
// hand-written ones may use the single form — accept both
|
||||||
|
let is_array_prop = node
|
||||||
|
.schema
|
||||||
|
.obj
|
||||||
|
.properties
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|p| p.get(field_name))
|
||||||
|
.map(|ps| match &ps.obj.type_ {
|
||||||
|
Some(crate::database::object::SchemaTypeOrArray::Single(t)) => t == "array",
|
||||||
|
Some(crate::database::object::SchemaTypeOrArray::Multiple(ts)) => {
|
||||||
|
ts.iter().any(|t| t == "array") && ts.iter().all(|t| t == "array" || t == "null")
|
||||||
|
}
|
||||||
|
None => false,
|
||||||
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if is_array_prop {
|
||||||
|
match op {
|
||||||
|
// "the array contains this value"
|
||||||
|
"$eq" => where_clauses.push(format!(
|
||||||
|
"{}.{} ? ({})",
|
||||||
|
filter_alias, field_name, p_val
|
||||||
|
)),
|
||||||
|
"$ne" => where_clauses.push(format!(
|
||||||
|
"NOT ({}.{} ? ({}))",
|
||||||
|
filter_alias, field_name, p_val
|
||||||
|
)),
|
||||||
|
// "the array contains ANY of these values"
|
||||||
|
"$of" => where_clauses.push(format!(
|
||||||
|
"{}.{} ?| ARRAY(SELECT jsonb_array_elements_text(({})::jsonb))",
|
||||||
|
filter_alias, field_name, p_val
|
||||||
|
)),
|
||||||
|
"$nof" => where_clauses.push(format!(
|
||||||
|
"NOT ({}.{} ?| ARRAY(SELECT jsonb_array_elements_text(({})::jsonb)))",
|
||||||
|
filter_alias, field_name, p_val
|
||||||
|
)),
|
||||||
|
other => {
|
||||||
|
return Err(format!(
|
||||||
|
"operator {} is not supported on array property '{}' (jsonb containment supports $eq/$ne/$of/$nof)",
|
||||||
|
other, field_name
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match op {
|
||||||
|
"$eq" => where_clauses.push(format!(
|
||||||
|
"{}.{} = ({})::jsonb",
|
||||||
|
filter_alias, field_name, p_val
|
||||||
|
)),
|
||||||
|
"$ne" => where_clauses.push(format!(
|
||||||
|
"{}.{} != ({})::jsonb",
|
||||||
|
filter_alias, field_name, p_val
|
||||||
|
)),
|
||||||
|
other => {
|
||||||
|
return Err(format!(
|
||||||
|
"operator {} is not supported on jsonb property '{}' (only $eq/$ne)",
|
||||||
|
other, field_name
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if op == "$of" || op == "$nof" {
|
if op == "$of" || op == "$nof" {
|
||||||
let sql_op = if op == "$of" { "IN" } else { "NOT IN" };
|
let sql_op = if op == "$of" { "IN" } else { "NOT IN" };
|
||||||
let subquery = format!(
|
let subquery = format!(
|
||||||
@ -830,6 +878,7 @@ impl<'a> Compiler<'a> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_relation_conditions(
|
fn compile_relation_conditions(
|
||||||
@ -899,69 +948,4 @@ impl<'a> Compiler<'a> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_static_property_conditions(
|
|
||||||
&self,
|
|
||||||
r#type: &crate::database::r#type::Type,
|
|
||||||
type_aliases: &std::collections::HashMap<String, String>,
|
|
||||||
node: &Node,
|
|
||||||
base_alias: &str,
|
|
||||||
where_clauses: &mut Vec<String>,
|
|
||||||
) {
|
|
||||||
if let Some(props) = node.schema.obj.properties.as_ref() {
|
|
||||||
for (prop_name, prop_schema) in props {
|
|
||||||
let filter_alias = Self::resolve_filter_alias(r#type, type_aliases, base_alias, prop_name);
|
|
||||||
if let Some(const_val) = prop_schema.obj.const_.as_ref() {
|
|
||||||
let sql_val = Self::quote_literal(const_val);
|
|
||||||
where_clauses.push(format!("{}.{} = {}", filter_alias, prop_name, sql_val));
|
|
||||||
}
|
|
||||||
if let Some(enum_vals) = prop_schema.obj.enum_.as_ref() {
|
|
||||||
if !enum_vals.is_empty() {
|
|
||||||
let sql_vals: Vec<String> = enum_vals
|
|
||||||
.iter()
|
|
||||||
.map(|v| Self::quote_literal(v))
|
|
||||||
.collect();
|
|
||||||
where_clauses.push(format!(
|
|
||||||
"{}.{} IN ({})",
|
|
||||||
filter_alias,
|
|
||||||
prop_name,
|
|
||||||
sql_vals.join(", ")
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn quote_literal(val: &Value) -> String {
|
|
||||||
match val {
|
|
||||||
Value::Null => "NULL".to_string(),
|
|
||||||
Value::Bool(b) => {
|
|
||||||
if *b {
|
|
||||||
"true".to_string()
|
|
||||||
} else {
|
|
||||||
"false".to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Number(n) => {
|
|
||||||
if let Some(f) = n.as_f64() {
|
|
||||||
if f.fract() == 0.0 {
|
|
||||||
return f.trunc().to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n.to_string()
|
|
||||||
}
|
|
||||||
Value::String(s) => {
|
|
||||||
if s.is_empty() {
|
|
||||||
"NULL".to_string()
|
|
||||||
} else {
|
|
||||||
format!("'{}'", s.replace('\'', "''"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => format!(
|
|
||||||
"'{}'",
|
|
||||||
serde_json::to_string(val).unwrap().replace('\'', "''")
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1302,9 +1302,9 @@ fn test_queryer_0_15() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_queryer_1_0() {
|
fn test_queryer_0_16() {
|
||||||
let path = format!("{}/fixtures/queryer.json", env!("CARGO_MANIFEST_DIR"));
|
let path = format!("{}/fixtures/queryer.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
crate::tests::runner::run_test_case(&path, 1, 0).unwrap();
|
crate::tests::runner::run_test_case(&path, 0, 16).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -268,8 +268,7 @@ impl SqlFormatter {
|
|||||||
match &join.join_operator {
|
match &join.join_operator {
|
||||||
JoinOperator::Inner(JoinConstraint::On(expr))
|
JoinOperator::Inner(JoinConstraint::On(expr))
|
||||||
| JoinOperator::Left(JoinConstraint::On(expr))
|
| JoinOperator::Left(JoinConstraint::On(expr))
|
||||||
| JoinOperator::Right(JoinConstraint::On(expr))
|
| JoinOperator::Right(JoinConstraint::On(expr)) => {
|
||||||
| JoinOperator::Join(JoinConstraint::On(expr)) => {
|
|
||||||
self.push_str(" ON ");
|
self.push_str(" ON ");
|
||||||
self.format_expr(expr);
|
self.format_expr(expr);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -157,10 +157,7 @@ fn test_library_api() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"name": { "type": ["string.condition", "null"] },
|
"name": { "type": ["string.condition", "null"] },
|
||||||
"target": {
|
"target": { "type": ["target_schema.filter", "null"] },
|
||||||
"type": ["target_schema.filter", "null"],
|
|
||||||
"compiledPropertyNames": ["value", "$and", "$or"]
|
|
||||||
},
|
|
||||||
"type": { "type": ["string.condition", "null"] }
|
"type": { "type": ["string.condition", "null"] }
|
||||||
},
|
},
|
||||||
"type": "filter"
|
"type": "filter"
|
||||||
|
|||||||
@ -259,25 +259,3 @@ pub fn update_sql_fixture(path: &str, suite_idx: usize, case_idx: usize, queries
|
|||||||
let formatted_json = serde_json::to_string_pretty(&file_data).unwrap();
|
let formatted_json = serde_json::to_string_pretty(&file_data).unwrap();
|
||||||
fs::write(path, formatted_json).unwrap();
|
fs::write(path, formatted_json).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_schemas_fixture(path: &str, suite_idx: usize, case_idx: usize, db: &crate::database::Database) {
|
|
||||||
let content = fs::read_to_string(path).unwrap();
|
|
||||||
let mut file_data: Value = serde_json::from_str(&content).unwrap();
|
|
||||||
|
|
||||||
if let Some(expect) = file_data[suite_idx]["tests"][case_idx].get_mut("expect") {
|
|
||||||
if let Some(schemas_map) = expect.get_mut("schemas").and_then(|v| v.as_object_mut()) {
|
|
||||||
for (key, expected_val) in schemas_map {
|
|
||||||
if expected_val.is_object() && expected_val.as_object().unwrap().is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Some(actual_ast) = db.schemas.get(key) {
|
|
||||||
let actual_val = serde_json::to_value(actual_ast).unwrap();
|
|
||||||
*expected_val = actual_val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let formatted_json = serde_json::to_string_pretty(&file_data).unwrap();
|
|
||||||
fs::write(path, formatted_json).unwrap();
|
|
||||||
}
|
|
||||||
|
|||||||
@ -57,9 +57,6 @@ impl Case {
|
|||||||
|
|
||||||
if env::var("UPDATE_EXPECT").is_ok() {
|
if env::var("UPDATE_EXPECT").is_ok() {
|
||||||
update_validation_fixture(path, suite_idx, case_idx, &result.errors);
|
update_validation_fixture(path, suite_idx, case_idx, &result.errors);
|
||||||
if let Ok(db) = db_res {
|
|
||||||
crate::tests::runner::update_schemas_fixture(path, suite_idx, case_idx, db);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expect.assert_drop(&result)?;
|
expect.assert_drop(&result)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user