1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
use crate::{
schemasync::mockmake::{Mockmaker, field_value::FieldValueGenerator},
schemasync::table::TableConfig,
types::{FieldType, StructField},
};
use convert_case::{Case, Casing};
use tracing::{debug, info};
/// Check if a field is nullable (wrapped in Option)
fn is_nullable_field(field: &StructField) -> bool {
matches!(&field.field_type, FieldType::Option(_))
}
/// Check if a field is a nullable partial struct that needs conditional wrapping
fn is_nullable_partial_struct(field: &StructField, original_table: Option<&TableConfig>) -> bool {
if let FieldType::Struct(_) = &field.field_type
&& let Some(table) = original_table
&& let Some(original_field) = table
.struct_config
.fields
.iter()
.find(|f| f.field_name == field.field_name)
{
return is_nullable_field(original_field);
}
false
}
/// Check if any field needs null-preserving conditional logic
fn needs_null_preservation(field: &StructField, original_table: Option<&TableConfig>) -> bool {
if is_nullable_field(field) {
return true;
}
if is_nullable_partial_struct(field, original_table) {
return true;
}
false
}
impl Mockmaker<'_> {
pub fn generate_upsert_statements(
&self,
table_name: &str,
table_config: &TableConfig,
) -> String {
info!(table_name = %table_name, "Generating upsert statements for table");
debug!("Table config: {:?}", table_config);
let mut output = String::new();
let config = self
.tables
.get(table_name)
.expect("TableConfig was not found");
let n = config
.mock_generation_config
.as_ref()
.map(|c| c.n)
.unwrap_or(self.schemasync_config.mock_gen_config.default_record_count);
// Step 3: Generate UPSERT statements for each record
for i in 0..n {
let mut field_assignments = Vec::new();
// Determine the record ID (default from id_map)
let default_record_id = if let Some(ids) = self.id_map.get(table_name) {
if i < ids.len() {
ids[i].clone()
} else {
format!("{}:{}", table_name.to_case(Case::Snake), i + 1)
}
} else {
format!("{}:{}", table_name.to_case(Case::Snake), i + 1)
};
// Allow plugin to override the record ID
#[cfg(feature = "wasm-plugins")]
let record_id = {
let plugin_name = table_config
.mock_generation_config
.as_ref()
.and_then(|c| c.plugin.as_ref());
if let Some(plugin_name) = plugin_name {
if let Some(ref pm_cell) = self.plugin_manager {
let pm = &mut *pm_cell.borrow_mut();
let id_input =
crate::schemasync::mockmake::plugin_types::PluginFieldInput {
table_name: table_name.to_string(),
field_name: "id".to_string(),
field_type: "EvenframeRecordId".to_string(),
record_index: i,
total_records: n,
record_id: default_record_id.clone(),
};
match pm.generate_field_value(plugin_name, &id_input) {
Ok(val) => val,
Err(_) => default_record_id,
}
} else {
default_record_id
}
} else {
default_record_id
}
};
#[cfg(not(feature = "wasm-plugins"))]
let record_id = default_record_id;
// Then, process remaining fields that weren't coordinated
for table_field in &table_config.struct_config.fields {
// Skip explicit 'id': the record id is already pinned in the
// UPSERT target (plugin override > id_map > fallback), and a
// generated `id` in CONTENT would conflict with it.
if table_field.field_name == "id" {
continue;
}
if table_field.edge_config.is_none()
&& (table_field.define_config.is_some()
&& !table_field.define_config.as_ref().unwrap().should_skip
// Skip readonly fields
&& table_field
.define_config
.as_ref()
.unwrap()
.readonly
.is_none())
{
let field_val = FieldValueGenerator::builder()
.field(table_field)
.id_index(&i)
.mockmaker(self)
.table_config(table_config)
.registry(self.registry)
.build()
.run();
// Check if this field needs null preservation
let needs_conditional =
needs_null_preservation(table_field, self.tables.get(table_name));
if needs_conditional {
// Wrap in conditional to preserve NULL state
field_assignments.push(format!(
"{}: (IF {} != NULL THEN {} ELSE NULL END)",
table_field.field_name, table_field.field_name, field_val
));
} else {
field_assignments.push(format!("{}: {field_val}", table_field.field_name));
}
}
}
let fields_str = field_assignments.join(", ");
// Generate UPSERT statement with CONTENT for each record
if table_config.relation.is_some() {
// For relation tables, we need special handling
output.push_str(&format!("UPSERT {record_id} CONTENT {{ {fields_str} }};\n"));
} else {
// For regular tables, use UPSERT with CONTENT
output.push_str(&format!("UPSERT {record_id} CONTENT {{ {fields_str} }};\n"));
}
}
output
}
}