Compare commits

...

6 Commits

5 changed files with 78 additions and 51 deletions

View File

@ -1316,7 +1316,7 @@
"success": true,
"sql": [
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"(SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}') || COALESCE(to_jsonb(t3.*), '{}') || COALESCE(to_jsonb(t4.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
@ -1469,14 +1469,14 @@
"success": true,
"sql": [
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"(SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}') || COALESCE(to_jsonb(t3.*), '{}') || COALESCE(to_jsonb(t4.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
"LEFT JOIN agreego.\"person\" t4 ON t4.id = t1.id",
"WHERE",
" t1.id = '{{uuid:data.id}}'",
"UNION SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"UNION SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}') || COALESCE(to_jsonb(t3.*), '{}') || COALESCE(to_jsonb(t4.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
@ -1631,14 +1631,14 @@
"success": true,
"sql": [
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"(SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}') || COALESCE(to_jsonb(t3.*), '{}') || COALESCE(to_jsonb(t4.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
"LEFT JOIN agreego.\"person\" t4 ON t4.id = t1.id",
"WHERE",
" t1.id = '{{uuid:data.id}}'",
"UNION SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"UNION SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}') || COALESCE(to_jsonb(t3.*), '{}') || COALESCE(to_jsonb(t4.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
@ -1648,7 +1648,7 @@
" AND \"last_name\" = 'LookupLast'",
" AND \"date_of_birth\" = '{{timestamp}}'",
" AND \"pronouns\" = 'they/them')",
"UNION SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"UNION SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}') || COALESCE(to_jsonb(t3.*), '{}') || COALESCE(to_jsonb(t4.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
@ -1802,14 +1802,14 @@
"success": true,
"sql": [
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"(SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}') || COALESCE(to_jsonb(t3.*), '{}') || COALESCE(to_jsonb(t4.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
"LEFT JOIN agreego.\"person\" t4 ON t4.id = t1.id",
"WHERE",
" t1.id = '{{uuid:data.id}}'",
"UNION SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"UNION SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}') || COALESCE(to_jsonb(t3.*), '{}') || COALESCE(to_jsonb(t4.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
@ -1951,7 +1951,7 @@
"success": true,
"sql": [
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"(SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}') || COALESCE(to_jsonb(t3.*), '{}') || COALESCE(to_jsonb(t4.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
@ -2074,7 +2074,7 @@
"success": true,
"sql": [
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"(SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}') || COALESCE(to_jsonb(t3.*), '{}') || COALESCE(to_jsonb(t4.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
@ -2413,7 +2413,7 @@
"success": true,
"sql": [
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
"(SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"order\" t2 ON t2.id = t1.id",
"WHERE",
@ -3215,7 +3215,7 @@
"success": true,
"sql": [
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"(SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}') || COALESCE(to_jsonb(t3.*), '{}') || COALESCE(to_jsonb(t4.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
@ -3568,7 +3568,7 @@
"success": true,
"sql": [
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
"(SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"order_line\" t2 ON t2.id = t1.id",
"WHERE",
@ -3692,7 +3692,7 @@
"success": true,
"sql": [
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
"(SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"invoice\" t2 ON t2.id = t1.id",
"WHERE",
@ -3808,7 +3808,7 @@
"success": true,
"sql": [
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
"(SELECT COALESCE(to_jsonb(t1.*), '{}') || COALESCE(to_jsonb(t2.*), '{}')",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"account\" t2 ON t2.id = t1.id",
"WHERE",

View File

@ -1739,10 +1739,10 @@
" AND entity_1.created_at <= ($16 #>> '{}')::TIMESTAMPTZ",
" AND entity_1.created_at <> ($17 #>> '{}')::TIMESTAMPTZ",
" AND person_3.first_name ILIKE $18 #>> '{}'",
" AND person_3.first_name > ($19 #>> '{)",
" AND person_3.first_name >= ($20 #>> '{)",
" AND person_3.first_name < ($21 #>> '{)",
" AND person_3.first_name <= ($22 #>> '{)",
" AND person_3.first_name > ($19 #>> '{}')",
" AND person_3.first_name >= ($20 #>> '{}')",
" AND person_3.first_name < ($21 #>> '{}')",
" AND person_3.first_name <= ($22 #>> '{}')",
" AND person_3.first_name NOT ILIKE $23 #>> '{}'",
" AND person_3.first_name NOT IN (SELECT value FROM jsonb_array_elements_text(($24 #>> '{}')::jsonb))",
" AND person_3.first_name IN (SELECT value FROM jsonb_array_elements_text(($25 #>> '{}')::jsonb))",

View File

@ -6,8 +6,8 @@ pub mod cache;
use crate::database::Database;
use crate::database::r#type::Type;
use crate::drop::{Drop, Error, ErrorDetails};
use serde_json::Value;
use indexmap::IndexMap;
use serde_json::Value;
use std::sync::Arc;
pub struct Merger {
@ -31,9 +31,10 @@ impl Merger {
None => {
return Drop::with_errors(vec![Error {
code: "SCHEMA_NOT_FOUND".to_string(),
values: Some(IndexMap::from([
("schema".to_string(), schema_id.to_string()),
])),
values: Some(IndexMap::from([(
"schema".to_string(),
schema_id.to_string(),
)])),
details: ErrorDetails {
path: None,
cause: None,
@ -56,9 +57,7 @@ impl Merger {
if let Err(e) = self.db.execute(&notify_sql, None) {
return Drop::with_errors(vec![Error {
code: "MERGE_FAILED".to_string(),
values: Some(IndexMap::from([
("error".to_string(), e.clone()),
])),
values: Some(IndexMap::from([("error".to_string(), e.clone())])),
details: ErrorDetails {
path: None,
cause: Some(e),
@ -121,9 +120,15 @@ impl Merger {
} else {
return Err(Error {
code: "TARGET_SCHEMA_NOT_FOUND".to_string(),
values: Some(IndexMap::from([("target_id".to_string(), target_id.clone())])),
values: Some(IndexMap::from([(
"target_id".to_string(),
target_id.clone(),
)])),
details: ErrorDetails {
cause: Some(format!("Polymorphic mapped target '{}' not found in database registry", target_id)),
cause: Some(format!(
"Polymorphic mapped target '{}' not found in database registry",
target_id
)),
..Default::default()
},
});
@ -141,7 +146,10 @@ impl Merger {
code: "ONE_OF_INDEX_NOT_FOUND".to_string(),
values: Some(IndexMap::from([("index".to_string(), idx.to_string())])),
details: ErrorDetails {
cause: Some(format!("Polymorphic index target '{}' not found in local oneOf array", idx)),
cause: Some(format!(
"Polymorphic index target '{}' not found in local oneOf array",
idx
)),
..Default::default()
},
});
@ -164,7 +172,10 @@ impl Merger {
("value".to_string(), v.to_string()),
])),
details: ErrorDetails {
cause: Some(format!("Polymorphic discriminator {}='{}' matched no compiled options", disc, v)),
cause: Some(format!(
"Polymorphic discriminator {}='{}' matched no compiled options",
disc, v
)),
..Default::default()
},
});
@ -172,9 +183,15 @@ impl Merger {
} else {
return Err(Error {
code: "MISSING_DISCRIMINATOR".to_string(),
values: Some(IndexMap::from([("discriminator".to_string(), disc.to_string())])),
values: Some(IndexMap::from([(
"discriminator".to_string(),
disc.to_string(),
)])),
details: ErrorDetails {
cause: Some(format!("Polymorphic merging failed: missing required discriminator '{}'", disc)),
cause: Some(format!(
"Polymorphic merging failed: missing required discriminator '{}'",
disc
)),
..Default::default()
},
});
@ -281,8 +298,6 @@ impl Merger {
let mut entity_objects = std::collections::BTreeMap::new();
let mut entity_arrays = std::collections::BTreeMap::new();
let is_external = self.db.executor.punc_external().unwrap_or(false);
for (k, v) in obj {
// Always retain system and unmapped core fields natively implicitly mapped to the Postgres tables
if k == "id" || k == "type" || k == "created" {
@ -291,12 +306,6 @@ impl Merger {
}
if let Some(prop_schema) = compiled_props.get(&k) {
if prop_schema.is_immutable(is_external) {
continue;
}
let mut is_edge = false;
if let Some(edges) = schema.obj.compiled_edges.get() {
if edges.contains_key(&k) {
@ -373,7 +382,10 @@ impl Merger {
if let Some(deps) = &schema.obj.dependencies {
if let Some(crate::database::object::Dependency::Props(req_props)) = deps.get("created") {
for req in req_props {
if !entity_fields.contains_key(req) && !entity_objects.contains_key(req) && !entity_arrays.contains_key(req) {
if !entity_fields.contains_key(req)
&& !entity_objects.contains_key(req)
&& !entity_arrays.contains_key(req)
{
return Err(Error {
code: "REQUIRED_FIELD_MISSING".to_string(),
values: Some(IndexMap::from([
@ -382,7 +394,10 @@ impl Merger {
])),
details: ErrorDetails {
path: Some(req.to_string()),
cause: Some(format!("Missing required creation field '{}' for entity {}", req, type_name)),
cause: Some(format!(
"Missing required creation field '{}' for entity {}",
req, type_name
)),
..Default::default()
},
});
@ -772,7 +787,7 @@ impl Merger {
let fetch_sql_template = if let Some(cached) = self.cache.get(entity_type_name) {
cached
} else {
let mut select_list = String::from("to_jsonb(t1.*)");
let mut select_list = String::from("COALESCE(to_jsonb(t1.*), '{}')");
let mut join_clauses = format!("FROM agreego.\"{}\" t1", entity_type.hierarchy[0]);
for (i, table_name) in entity_type.hierarchy.iter().enumerate().skip(1) {
@ -781,7 +796,7 @@ impl Merger {
" LEFT JOIN agreego.\"{}\" {} ON {}.id = t1.id",
table_name, t_alias, t_alias
));
select_list.push_str(&format!(" || to_jsonb({}.*)", t_alias));
select_list.push_str(&format!(" || COALESCE(to_jsonb({}.*), '{{}}')", t_alias));
}
let template = format!("SELECT {} {}", select_list, join_clauses);
@ -829,9 +844,15 @@ impl Merger {
if table.len() > 1 {
Err(Error {
code: "TOO_MANY_LOOKUP_ROWS".to_string(),
values: Some(IndexMap::from([("entity_type".to_string(), entity_type_name.to_string())])),
values: Some(IndexMap::from([(
"entity_type".to_string(),
entity_type_name.to_string(),
)])),
details: ErrorDetails {
cause: Some(format!("Lookup for {} found too many existing rows", entity_type_name)),
cause: Some(format!(
"Lookup for {} found too many existing rows",
entity_type_name
)),
..Default::default()
},
})
@ -904,9 +925,15 @@ impl Merger {
_ => {
return Err(Error {
code: "MISSING_GROUPED_FIELDS".to_string(),
values: Some(IndexMap::from([("type".to_string(), entity_type_name.to_string())])),
values: Some(IndexMap::from([(
"type".to_string(),
entity_type_name.to_string(),
)])),
details: ErrorDetails {
cause: Some(format!("Grouped fields missing for type {}", entity_type_name)),
cause: Some(format!(
"Grouped fields missing for type {}",
entity_type_name
)),
..Default::default()
},
});

View File

@ -119,7 +119,7 @@ impl SqlFormatter {
for (i, val) in val_tokens.iter().enumerate() {
let comma = if i < val_tokens.len() - 1 { "," } else { "" };
if val.starts_with("'{") && val.ends_with("}'") {
if val.starts_with("'{") && val.ends_with("}'") && val.len() > 4 {
let inner = &val[1..val.len() - 1];
// Unescape single quotes from SQL strings
let unescaped = inner.replace("''", "'");
@ -341,7 +341,7 @@ impl SqlFormatter {
}
Expr::Value(sqlparser::ast::ValueWithSpan { value: Value::SingleQuotedString(s), .. }) | Expr::Value(sqlparser::ast::ValueWithSpan { value: Value::EscapedStringLiteral(s), .. }) => {
if s.starts_with('{') && s.ends_with('}') {
if s.starts_with('{') && s.ends_with('}') && s.len() > 2 {
if let Ok(json) = serde_json::from_str::<serde_json::Value>(s) {
if let Ok(pretty) = serde_json::to_string_pretty(&json) {
let lines: Vec<&str> = pretty.split('\n').collect();

View File

@ -1 +1 @@
1.0.187
1.0.191