1use crate as eg;
3use eg::db;
4use eg::idl;
5use eg::osrf::message;
6use eg::result::EgResult;
7use eg::EgValue;
8use std::sync::Arc;
9
10const JOIN_WITH_AND: &str = "AND";
11const JOIN_WITH_OR: &str = "OR";
12
13#[derive(Debug, Clone)]
18pub struct SourceDef {
19 idl_class: Arc<idl::Class>,
20 alias: Option<String>,
21}
22
23impl SourceDef {
24 pub fn idl_class(&self) -> &Arc<idl::Class> {
25 &self.idl_class
26 }
27 pub fn classname(&self) -> &str {
28 self.idl_class.classname()
29 }
30}
31
32#[derive(Debug)]
33pub struct JsonQueryCompiler {
34 locale: String,
36
37 disable_i18n: bool,
39
40 controllername: Option<String>,
43
44 sources: Vec<SourceDef>,
47
48 query_string: Option<String>,
50
51 params: Option<Vec<String>>,
53
54 has_aggregate: bool,
56
57 group_by: Vec<usize>,
59
60 select_index: usize,
62}
63
64impl Clone for JsonQueryCompiler {
73 fn clone(self: &JsonQueryCompiler) -> JsonQueryCompiler {
74 let mut new = JsonQueryCompiler::new();
75
76 new.locale.clone_from(&self.locale);
77 new.disable_i18n = self.disable_i18n;
78 new.controllername.clone_from(&self.controllername);
79
80 new
81 }
82}
83
84impl Default for JsonQueryCompiler {
85 fn default() -> Self {
86 Self::new()
87 }
88}
89
90impl JsonQueryCompiler {
92 pub fn new() -> Self {
93 Self {
94 locale: message::thread_locale(),
95 controllername: None,
96 sources: Vec::new(),
97 query_string: None,
98 disable_i18n: false,
99 params: None,
100 group_by: Vec::new(),
101 has_aggregate: false,
102 select_index: 0,
103 }
104 }
105
106 pub fn query_params(&self) -> Vec<&str> {
112 if let Some(params) = self.params.as_ref() {
113 params.iter().map(|s| s.as_str()).collect()
115 } else {
116 vec![]
117 }
118 }
119
120 pub fn debug_params(&self) -> String {
122 let mut array = eg::array![];
123 if let Some(params) = self.params.as_ref() {
124 for (idx, value) in params.iter().enumerate() {
125 let mut obj = eg::hash! {};
126
127 obj[&format!("${}", idx + 1)] = EgValue::from(value.as_str());
129
130 array.push(obj).expect("this is an array");
131 }
132 }
133
134 array.dump()
135 }
136
137 pub fn debug_query_kludge(&self) -> String {
143 let mut sql = match self.query_string.as_ref() {
144 Some(s) => s.to_string(),
145 None => return String::new(),
146 };
147
148 if let Some(params) = self.params.as_ref() {
149 let mut idx = params.len();
153 for value in params.iter().rev() {
154 let target = format!("${idx}");
155
156 idx -= 1;
158
159 if value.contains('\'') {
162 let escaped = value.replace('\'', "''");
164 sql = sql.replace(&target, &format!("'{escaped}'"));
165 } else {
166 sql = sql.replace(&target, &format!("'{value}'"));
167 }
168 }
169 }
170
171 sql
172 }
173
174 pub fn query_string(&self) -> Option<&str> {
176 self.query_string.as_deref()
177 }
178
179 fn take_query_string(&mut self) -> Option<String> {
181 self.query_string.take()
182 }
183
184 fn get_alias_classname(&self, alias: &str) -> EgResult<&str> {
187 Ok(self.get_alias_class(alias)?.classname())
188 }
189
190 pub fn sources(&self) -> &Vec<SourceDef> {
191 &self.sources
192 }
193
194 fn get_alias_class(&self, alias: &str) -> EgResult<&Arc<idl::Class>> {
195 self.sources
196 .iter()
197 .filter(|c| {
198 if let Some(als) = c.alias.as_ref() {
199 alias == als
200 } else {
201 alias == c.classname()
202 }
203 })
204 .map(|c| c.idl_class())
205 .next()
206 .ok_or_else(|| format!("No such class alias: {alias}").into())
207 }
208
209 fn get_idl_class(&self, classname: &str) -> EgResult<Arc<idl::Class>> {
213 idl::get_class(classname).cloned()
214 }
215
216 fn get_base_class(&self) -> EgResult<&Arc<idl::Class>> {
218 self.sources
220 .first()
221 .map(|s| s.idl_class())
222 .ok_or("No bass class has been set".into())
223 }
224
225 fn field_may_be_selected(&self, name: &str, class: &str) -> EgResult<bool> {
231 let idl_class = match idl::get_class(class) {
232 Ok(c) => c,
233 Err(_) => return Err(format!("Invalid IDL class: {class}").into()),
234 };
235
236 let idl_field = match idl_class.fields().get(name) {
237 Some(f) => f,
238 None => return Err(format!("Class {class} has no field '{name}'").into()),
239 };
240
241 if idl_field.is_virtual() {
242 return Err(format!("Field {name} on {class} is virtual").into());
243 }
244
245 if let Some(suppress) = idl_field.suppress_controller() {
246 if let Some(module) = self.controllername.as_ref() {
247 if suppress.contains(module) {
248 return Ok(false);
250 }
251 }
252 }
253
254 Ok(true)
255 }
256
257 pub fn compile(&mut self, query: &EgValue) -> EgResult<()> {
262 if !query.is_object() {
263 return Err("json_query must be a JSON hash".into());
264 }
265
266 if query["no_i18n"].boolish() {
267 self.disable_i18n = true;
268 }
269
270 if query["distinct"].boolish() {
271 self.has_aggregate = true;
272 }
273
274 if query["from"].is_array() {
275 let func_str = self.compile_function_query(&query["from"])?;
278 self.query_string = Some(func_str);
279 return Ok(());
280 }
281
282 if query.has_key("union") || query.has_key("except") || query.has_key("intersect") {
283 let combo_str = self.compile_combo_query(query)?;
284 self.query_string = Some(combo_str);
285 return Ok(());
286 }
287
288 self.set_base_source(&query["from"])?;
289 let base_class = self.get_base_class()?.clone();
290 let cname = base_class.classname();
291
292 let join_op = self.compile_joins_for_class(cname, &query["from"][cname])?;
294
295 let join_str = join_op.map(|joins| format!(" {joins}"));
296
297 let select_str = self.compile_selects(&query["select"])?;
298 let from_str = self.class_table_or_source_def(cname)?;
299 let where_str = self.compile_where_for_class(&query["where"], cname, JOIN_WITH_AND)?;
300
301 let mut sql = format!(
302 r#"SELECT {select_str} FROM {from_str} AS "{cname}"{} WHERE {where_str}"#,
303 join_str.as_deref().unwrap_or("")
304 );
305
306 if self.has_aggregate {
307 let positions: Vec<String> = self.group_by.iter().map(|n| n.to_string()).collect();
308 sql += &format!(" GROUP BY {}", positions.join(", "));
309 }
310
311 if !query["order_by"].is_null() {
312 sql += &format!(" ORDER BY {}", self.compile_order_by(&query["order_by"])?);
313 }
314
315 if let Some(count) = query["limit"].as_usize() {
316 sql += &format!(" LIMIT {count}");
317 }
318
319 if let Some(count) = query["offset"].as_usize() {
320 sql += &format!(" OFFSET {count}");
321 }
322
323 self.query_string = Some(sql);
324
325 Ok(())
326 }
327
328 fn compile_order_by(&mut self, order_by: &EgValue) -> EgResult<String> {
329 if order_by.is_array() {
330 return self.compile_order_by_array(order_by);
331 }
332
333 Ok(String::new())
334 }
335
336 fn compile_order_by_array(&mut self, order_by: &EgValue) -> EgResult<String> {
337 let mut order_bys = Vec::new();
338
339 for hash in order_by.members() {
340 if !hash.is_object() {
341 return Err(format!("Malformed ORDER BY: {}", order_by.dump()).into());
342 }
343
344 let class_alias = hash["class"]
345 .as_str()
346 .ok_or_else(|| format!("ORDER BY has no class: {}", order_by.dump()))?;
347
348 let field_name = hash["field"]
349 .as_str()
350 .ok_or_else(|| format!("ORDER BY has no field: {}", order_by.dump()))?;
351
352 let classname = self.get_alias_classname(class_alias)?;
353
354 if !self.field_may_be_selected(field_name, classname)? {
355 return Err(format!("Field '{field_name}' is not valid in ORDER BY").into());
356 }
357
358 let order_by_str;
359 if !hash["transform"].is_null() {
360 order_by_str = self.select_one_field(
361 class_alias,
362 None, field_name,
364 Some(hash),
365 false,
366 )?;
367 } else if !hash["compare"].is_null() {
368 order_by_str = self.search_predicate(class_alias, field_name, &hash["compare"])?;
370 } else {
371 order_by_str = format!(r#""{class_alias}".{field_name}"#);
373 }
374
375 let mut direction = "ASC";
376 if let Some(dir) = hash["direction"].as_str() {
377 if dir.starts_with('d') || dir.starts_with('D') {
378 direction = "DESC";
379 }
380 }
381
382 order_bys.push(format!("{order_by_str} {direction}"));
383 }
384
385 Ok(order_bys.join(", "))
386 }
387
388 fn compile_combo_query(&mut self, query: &EgValue) -> EgResult<String> {
390 let all = query["all"].boolish();
391 let qtype;
392
393 let query_array = if query["union"].is_array() {
394 qtype = "UNION";
395 &query["union"]
396 } else if query["except"].is_array() {
397 qtype = "EXCEPT";
398 &query["except"]
399 } else if query["intersect"].is_array() {
400 qtype = "INTERSECT";
401 &query["intersect"]
402 } else {
403 return Err(format!("Invalid UNION/INTERSECT/EXCEPT query: {}", query.dump()).into());
404 };
405
406 if !query["order_by"].is_null() {
407 return Err(format!("ORDER BY not supported for query type: {}", query.dump()).into());
408 }
409
410 if query_array.len() < 2 {
412 return Err(format!("Invalid query array for query type: {}", query.dump()).into());
413 }
414
415 if qtype == "EXCEPT" && query_array.len() > 2 {
416 return Err(format!(
417 "EXCEPT operator has too many query operands: {}",
418 query.dump()
419 )
420 .into());
421 }
422
423 let mut sql = String::new();
424 for (idx, hash) in query_array.members().enumerate() {
425 if !hash.is_object() {
426 return Err(format!("Invalid sub-query for query type: {}", query.dump()).into());
427 }
428
429 if idx > 0 {
430 sql += " ";
431 sql += qtype;
432 if all {
433 sql += " ALL ";
434 }
435 }
436
437 sql += &self.compile_sub_query(hash)?;
438 }
439
440 Ok(sql)
441 }
442
443 fn compile_sub_query(&mut self, query: &EgValue) -> EgResult<String> {
445 let mut compiler = self.clone();
446
447 compiler.compile(query)?;
448
449 let sub_sql = compiler
450 .take_query_string()
451 .ok_or_else(|| format!("Sub-query produced no SQL: {}", query.dump()))?;
452
453 if let Some(params) = compiler.params.as_mut() {
454 for value in params.drain(..) {
455 self.add_param_string(value);
456 }
457 }
458
459 Ok(sub_sql)
460 }
461
462 fn compile_selects(&mut self, select_def: &EgValue) -> EgResult<String> {
463 if select_def.is_null() {
464 let base_class = self.get_base_class()?.clone();
465 let cn = base_class.classname(); return self.build_default_select_list(cn, None);
469 } else if !select_def.is_object() {
470 return Err(format!("Invalid SELECT clause: {select_def}").into());
472 }
473
474 let mut selects = Vec::new();
475 for (alias, payload) in select_def.entries() {
476 selects.push(self.compile_selects_for_class(alias, payload)?);
477 }
478
479 Ok(selects.join(", "))
480 }
481
482 fn compile_selects_for_class(
483 &mut self,
484 class_alias: &str,
485 select_def: &EgValue,
486 ) -> EgResult<String> {
487 if select_def.is_null() {
488 return self.build_default_select_list(class_alias, None);
489 }
490
491 let alias_class = self.get_alias_class(class_alias)?.clone();
492 let classname = alias_class.classname();
493
494 if let Some(col) = select_def.as_str() {
495 if col == "*" {
496 return self.build_default_select_list(class_alias, None);
498 } else {
499 if self.field_may_be_selected(col, classname)? {
502 return self.select_one_field(class_alias, None, col, None, true);
503 }
504 }
505 }
506
507 if select_def.is_object() {
508 let fields: Vec<&str> = select_def["exclude"]
512 .members()
513 .filter(|f| f.is_string())
514 .map(|f| f.as_str().unwrap())
515 .collect();
516
517 if !fields.is_empty() {
518 return self.build_default_select_list(class_alias, Some(fields.as_slice()));
519 }
520
521 return Err(format!(
522 "SELECT received invalid 'exclude' query: {}",
523 select_def.dump()
524 )
525 .into());
526 }
527
528 if !select_def.is_array() {
529 return Err("SELECT must be string, null, excluder, or array".into());
530 }
531
532 let mut fields = Vec::new();
533
534 for field_struct in select_def.members() {
535 if let Some(column) = field_struct.as_str() {
536 if self.field_may_be_selected(column, classname)? {
539 fields.push(self.select_one_field(class_alias, None, column, None, true)?);
540 }
541
542 continue;
543 }
544
545 let column = field_struct["column"]
546 .as_str()
547 .ok_or_else(|| format!("SELECT hash requires a 'column': {field_struct}"))?;
548
549 if !self.field_may_be_selected(column, classname)? {
550 continue;
551 }
552
553 fields.push(self.select_one_field(
554 class_alias,
555 field_struct["alias"].as_str(),
556 column,
557 Some(field_struct),
558 true,
559 )?);
560 }
561
562 Ok(fields.join(", "))
563 }
564
565 fn build_default_select_list(
566 &mut self,
567 alias: &str,
568 exclude: Option<&[&str]>,
569 ) -> EgResult<String> {
570 let alias_class = self.get_alias_class(alias)?.clone();
571 let classname = alias_class.classname();
572
573 let idl_class = idl::get_class(classname)?;
575
576 let mut fields = Vec::new();
577 for field in idl_class.real_fields_sorted().iter() {
578 if self.field_may_be_selected(field.name(), classname)? {
579 if let Some(list) = exclude {
580 if list.contains(&field.name()) {
581 continue;
582 }
583 }
584 }
585 fields.push(self.select_one_field(alias, None, field.name(), None, true)?);
586 }
587
588 Ok(fields.join(", "))
589 }
590
591 fn select_one_field(
594 &mut self,
595 class_alias: &str,
596 field_alias: Option<&str>,
597 field_name: &str,
598 field_def: Option<&EgValue>,
599 handle_group_by: bool,
602 ) -> EgResult<String> {
603 if handle_group_by {
604 self.select_index += 1;
605 }
606
607 let mut is_aggregate = false;
608
609 if let Some(fdef) = field_def {
610 is_aggregate = fdef["aggregate"].boolish();
615
616 if let Some(xform) = fdef["transform"].as_str() {
617 let mut sql = String::new();
618
619 sql += self.check_identifier(xform)?;
620 sql += "(";
621
622 if fdef["distinct"].boolish() {
623 sql += "DISTINCT ";
624 }
625
626 sql += &self.format_one_select_field(class_alias, None, field_name)?;
629
630 for param in fdef["params"].members() {
631 let index = self.add_param(param)?;
632 sql += &format!(", ${index}");
633 }
634
635 sql += ")";
636
637 if let Some(rfield) = fdef["result_field"].as_str() {
638 sql = format!(r#"({sql})."{}""#, self.check_identifier(rfield)?);
640 } else if let Some(alias) = field_alias {
641 sql += &format!(r#" AS "{}""#, self.check_identifier(alias)?);
642 }
643
644 if handle_group_by {
645 if is_aggregate {
646 self.has_aggregate = true;
647 } else {
648 self.group_by.push(self.select_index);
649 }
650 }
651
652 return Ok(sql);
653 }
654 }
655
656 if handle_group_by {
657 if is_aggregate {
658 self.has_aggregate = true;
659 } else {
660 self.group_by.push(self.select_index);
661 }
662 }
663
664 self.format_one_select_field(class_alias, field_alias, field_name)
665 }
666
667 fn format_one_select_field(
670 &self,
671 class_alias: &str,
672 field_alias: Option<&str>,
673 field_name: &str,
674 ) -> EgResult<String> {
675 let mut sql;
676
677 let classname = self.get_alias_classname(class_alias)?;
678 let idl_class = idl::get_class(classname)?;
679
680 let idl_field = idl_class
681 .fields()
682 .get(field_name)
683 .ok_or_else(|| format!("Invalid field {}::{field_name}", idl_class.classname()))?;
684
685 if !idl_field.i18n() || self.disable_i18n {
686 sql = format!(
687 r#""{}".{}"#,
688 self.check_identifier(class_alias)?,
689 self.check_identifier(idl_field.name())?
690 );
691 } else {
692 let pkey = idl_class
693 .pkey()
694 .ok_or_else(|| format!("{} has no primary key", idl_class.classname()))?;
695
696 let tablename = idl_class
697 .tablename()
698 .ok_or_else(|| format!("Class {classname} has no table definition"))?;
699
700 sql = format!(
701 r#"oils_i18n_xlate('{}', '{}', '{}', '{}', "{}".{}::TEXT, '{}')"#,
702 self.check_identifier(tablename)?,
703 self.check_identifier(class_alias)?,
704 self.check_identifier(idl_field.name())?,
705 self.check_identifier(pkey)?,
706 self.check_identifier(class_alias)?,
707 self.check_identifier(pkey)?,
708 self.locale.as_str(), );
710 }
711
712 if let Some(alias) = field_alias {
713 sql += &format!(r#" AS "{}""#, self.check_identifier(alias)?);
714 }
715
716 Ok(sql)
717 }
718
719 fn compile_joins_for_class(
721 &mut self,
722 left_alias: &str,
723 joins: &EgValue,
724 ) -> EgResult<Option<String>> {
725 let class_to_hash = |c| {
726 let mut hash = eg::hash! {};
729 hash[c] = EgValue::Null;
730 hash
731 };
732
733 let join_binding;
734
735 let join_list = if let EgValue::Array(list) = joins {
736 list.iter().collect::<Vec<&EgValue>>()
737 } else if let Some(class) = joins.as_str() {
738 join_binding = class_to_hash(class);
739 vec![&join_binding]
740 } else {
741 vec![joins]
742 };
743
744 let mut joins = Vec::new();
745 for join_entry in join_list {
746 let hash_binding;
747
748 let hash_ref = if let Some(class) = join_entry.as_str() {
749 hash_binding = class_to_hash(class);
750 &hash_binding
751 } else {
752 join_entry
753 };
754
755 for (right_alias, join_def) in hash_ref.entries() {
756 joins.push(self.add_one_join(left_alias, right_alias, join_def)?);
757 }
758 }
759
760 if joins.is_empty() {
761 Ok(None)
762 } else {
763 Ok(Some(joins.join(" ")))
764 }
765 }
766
767 fn add_one_join(
768 &mut self,
769 left_alias: &str,
770 right_alias: &str,
771 join_def: &EgValue,
772 ) -> EgResult<String> {
773 let right_class = join_def["class"].as_str().unwrap_or(right_alias);
775 let right_idl_class = self.get_idl_class(right_class)?;
776
777 let left_class = self.get_alias_classname(left_alias)?;
778 let left_idl_class = self.get_idl_class(left_class)?;
779
780 let mut left_join_field = join_def["fkey"].as_str();
782
783 let mut right_join_field = join_def["field"].as_str();
785
786 if right_join_field.is_some() && left_join_field.is_none() {
789 let rfield_name = right_join_field.unwrap(); let idl_link = right_idl_class
794 .links()
795 .get(rfield_name)
796 .ok_or_else(|| format!("No such link for class '{right_class}'"))?;
797
798 let reltype = idl_link.reltype();
799
800 let maybe_left_class = idl_link.class();
801
802 if reltype != idl::RelType::HasMany && maybe_left_class == left_class {
803 left_join_field = Some(idl_link.key());
804 }
805
806 if left_join_field.is_none() {
807 return Err(format!(
808 "No link defined from {right_class}::{rfield_name} to {maybe_left_class}"
809 )
810 .into());
811 }
812 } else if right_join_field.is_none() && left_join_field.is_some() {
813 let lfield_name = left_join_field.unwrap(); let idl_link = left_idl_class
816 .links()
817 .get(lfield_name)
818 .ok_or_else(|| format!("No such link {lfield_name} for class {left_class}"))?;
819
820 let reltype = idl_link.reltype();
821
822 let maybe_right_class = idl_link.class();
823 if reltype != idl::RelType::HasMany && maybe_right_class == right_class {
824 right_join_field = Some(idl_link.key());
825 }
826
827 if right_join_field.is_none() {
828 return Err(format!(
829 "No link defined from {left_class}::{lfield_name} to {maybe_right_class}"
830 )
831 .into());
832 }
833 } else if right_join_field.is_none() && left_join_field.is_none() {
834 for (link_key, cur_link) in left_idl_class.links() {
838 let maybe_right_class = cur_link.class();
839
840 if maybe_right_class == right_class {
841 let reltype = cur_link.reltype();
842 if reltype != idl::RelType::HasMany {
843 left_join_field = Some(link_key);
844 right_join_field = Some(cur_link.key());
845 break;
846 }
847 }
848 }
849
850 if right_join_field.is_none() && left_join_field.is_none() {
852 for (link_key, cur_link) in right_idl_class.links() {
853 let maybe_left_class = cur_link.class();
854
855 if maybe_left_class == left_class {
856 let reltype = cur_link.reltype();
857 if reltype != idl::RelType::HasMany {
858 right_join_field = Some(link_key);
859 left_join_field = Some(cur_link.key());
860 break;
861 }
862 }
863 }
864 }
865
866 if right_join_field.is_none() && left_join_field.is_none() {
867 return Err(format!(
868 "Could not find link between classes {left_class} and {right_class}"
869 )
870 .into());
871 }
872 }
873
874 let join_type = if let Some(jtype) = join_def["type"].as_str() {
875 match jtype {
876 "left" => "LEFT JOIN",
877 "right" => "RIGHT JOIN",
878 "full" => "FULL JOIN",
879 _ => "INNER JOIN",
880 }
881 } else {
882 "INNER JOIN"
883 };
884
885 let source_str = self.class_table_or_source_def(right_idl_class.classname())?;
887
888 let mut sql = format!(
889 r#"{} {} AS "{}" ON ("{}".{} = "{}".{}"#,
890 join_type,
891 source_str,
892 self.check_identifier(right_alias)?,
893 self.check_identifier(right_alias)?,
894 self.check_identifier(right_join_field.unwrap())?,
895 self.check_identifier(left_alias)?,
896 self.check_identifier(left_join_field.unwrap())?,
897 );
898
899 let mut source_def = SourceDef {
903 idl_class: right_idl_class,
904 alias: None,
905 };
906
907 if right_alias != right_class {
908 source_def.alias = Some(right_alias.to_string());
910 }
911
912 self.sources.push(source_def);
913 let filter = &join_def["filter"];
918 if !filter.is_null() {
919 let mut op = " AND ";
920 if let Some(filter_op) = filter["filter_op"].as_str() {
921 if filter_op == "or" {
922 op = " OR ";
923 }
924 }
925 sql += op;
926 sql += &self.compile_where_for_class(filter, right_alias, JOIN_WITH_AND)?;
927 }
928
929 sql += ")";
930
931 let sub_join = &join_def["join"];
933 if !sub_join.is_null() {
934 if let Some(sjoin) = self.compile_joins_for_class(right_alias, sub_join)? {
935 sql += " ";
936 sql += &sjoin;
937 }
938 }
939
940 Ok(sql)
941 }
942
943 fn class_table_or_source_def(&self, classname: &str) -> EgResult<String> {
950 if let Ok(idl_class) = idl::get_class(classname) {
951 if let Some(tablename) = idl_class.tablename() {
952 return Ok(self.check_identifier(tablename)?.to_string());
953 } else if let Some(source_def) = idl_class.source_definition() {
954 return Ok(format!("({source_def})"));
956 }
957 }
958
959 Err(format!("Class {classname} has no table or source definition").into())
960 }
961
962 fn compile_where_for_class(
963 &mut self,
964 where_def: &EgValue,
965 class_alias: &str,
966 join_op: &str,
967 ) -> EgResult<String> {
968 let mut sql = String::new();
971
972 if where_def.is_array() {
973 if where_def.is_empty() {
974 return Err("Invalid WHERE clause / empty array".into());
975 }
976
977 for (idx, part) in where_def.members().enumerate() {
978 if idx > 0 {
979 sql += " ";
980 sql += join_op;
981 sql += " ";
982 }
983 let sub_pred = self.compile_where_for_class(part, class_alias, join_op)?;
984 sql += &format!("({sub_pred})");
985 }
986
987 return Ok(sql);
988 } else if where_def.is_object() {
989 if where_def.is_empty() {
990 return Err("Invalid predicate structure: empty JSON object".into());
991 }
992
993 for (idx, (key, sub_blob)) in where_def.entries().enumerate() {
994 if idx > 0 {
995 sql += " ";
996 sql += join_op;
997 sql += " ";
998 }
999
1000 if key.starts_with('+') && key.len() > 1 {
1001 let alias = &key[1..];
1005 let classname = self.get_alias_classname(alias)?;
1006
1007 if let Some(field) = sub_blob.as_str() {
1008 if !idl::get_class(classname)?.has_real_field(field) {
1012 return Err(
1013 format!("Class {classname} has no field named {field}").into()
1014 );
1015 }
1016
1017 sql += &format!(r#" "{alias}".{field} "#);
1018 } else {
1019 let sub_pred = self.compile_where_for_class(sub_blob, alias, join_op)?;
1022 sql += &format!("({sub_pred})");
1023 }
1024 } else if key.starts_with('-') {
1025 if key == "-or" {
1026 let sub_pred =
1027 self.compile_where_for_class(sub_blob, class_alias, JOIN_WITH_OR)?;
1028 sql += &format!("({sub_pred})");
1029 } else if key == "-and" {
1030 let sub_pred =
1031 self.compile_where_for_class(sub_blob, class_alias, JOIN_WITH_AND)?;
1032 sql += &format!("({sub_pred})");
1033 } else if key == "-not" {
1034 let sub_pred =
1035 self.compile_where_for_class(sub_blob, class_alias, JOIN_WITH_AND)?;
1036 sql += &format!("NOT ({sub_pred})");
1037 } else if key == "-exists" || key == "-not-exists" {
1038 let sub_sql = self.compile_sub_query(sub_blob)?;
1039
1040 let question = if key.contains("not") {
1041 "NOT EXISTS"
1042 } else {
1043 "EXISTS"
1044 };
1045
1046 sql += &format!("{question} ({sub_sql})");
1047 }
1048 } else {
1049 let classname = self.get_alias_classname(class_alias)?;
1052
1053 if !idl::get_class(classname)?.has_real_field(key) {
1056 return Err(format!("Class {classname} has no field called {key}").into());
1057 }
1058
1059 sql += &self.search_predicate(class_alias, key, sub_blob)?;
1060 }
1061 }
1062 } else if where_def.is_null() {
1063 sql = "TRUE".to_string();
1067 } else {
1068 return Err(format!("Invalid WHERE structure: {where_def}").into());
1069 }
1070
1071 Ok(sql)
1072 }
1073
1074 fn search_predicate(
1085 &mut self,
1086 class_alias: &str,
1087 field_name: &str,
1088 value_def: &EgValue,
1089 ) -> EgResult<String> {
1090 if value_def.is_array() {
1093 self.search_in_predicate(class_alias, field_name, value_def, false)
1095 } else if value_def.is_object() {
1096 if value_def.len() != 1 {
1097 return Err(format!(
1098 "Invalid search predicate for field: {field_name} {value_def}",
1099 )
1100 .into());
1101 }
1102
1103 let (key, sub_def) = value_def.entries().next().unwrap(); if key == "between" {
1106 self.search_between_predicate(class_alias, field_name, sub_def)
1107 } else if key == "in" || key == "not in" {
1108 self.search_in_predicate(class_alias, field_name, sub_def, key.contains("not"))
1109 } else if sub_def.is_array() {
1110 self.search_function_predicate(key, class_alias, field_name, sub_def)
1111 } else if sub_def.is_object() {
1112 self.search_field_transform_predicate(key, class_alias, field_name, sub_def)
1113 } else {
1114 self.simple_search_predicate(key, class_alias, field_name, sub_def)
1115 }
1116 } else {
1117 self.simple_search_predicate("=", class_alias, field_name, value_def)
1118 }
1119 }
1120
1121 fn search_field_transform_predicate(
1127 &mut self,
1128 operator: &str,
1129 class_alias: &str,
1130 field_name: &str,
1131 value_def: &EgValue,
1132 ) -> EgResult<String> {
1133 let field_str =
1136 self.select_one_field(class_alias, None, field_name, Some(value_def), false)?;
1137
1138 let value_obj = &value_def["value"];
1139
1140 let mut extra_parens = false;
1141
1142 let value_str = if value_obj.is_null() {
1143 extra_parens = true;
1144 self.compile_where_for_class(value_def, class_alias, JOIN_WITH_AND)?
1145 } else if value_obj.is_array() {
1146 self.compile_function_from(value_obj)?
1147 } else if value_obj.is_object() {
1148 extra_parens = true;
1149 self.compile_where_for_class(value_obj, class_alias, JOIN_WITH_AND)?
1150 } else if value_obj.is_string() || value_obj.is_number() {
1151 self.scalar_param_as_string(class_alias, field_name, value_obj)?
1152 } else {
1153 return Err(format!(
1154 "Invalid predicate for field transform for {field_name}: {}",
1155 value_obj.dump()
1156 )
1157 .into());
1158 };
1159
1160 let left_parens = if extra_parens { "(" } else { "" };
1161 let right_parens = if extra_parens { ")" } else { "" };
1162
1163 Ok(format!(
1164 r#"{}{} {} {}{}{}{}"#,
1165 left_parens,
1166 field_str,
1167 self.check_operator(operator)?,
1168 left_parens,
1169 value_str,
1170 right_parens,
1171 right_parens
1172 ))
1173 }
1174
1175 fn search_function_predicate(
1185 &mut self,
1186 operator: &str,
1187 class_alias: &str,
1188 field_name: &str,
1189 value_def: &EgValue,
1190 ) -> EgResult<String> {
1191 let func_str = self.compile_function_from(value_def)?;
1192
1193 Ok(format!(
1194 r#""{class_alias}".{field_name} {} {func_str}"#,
1195 self.check_operator(operator)?,
1196 ))
1197 }
1198
1199 fn search_between_predicate(
1205 &mut self,
1206 class_alias: &str,
1207 field_name: &str,
1208 value_def: &EgValue,
1209 ) -> EgResult<String> {
1210 let value_def = if !value_def["value"].is_null() {
1211 &value_def["value"]
1213 } else {
1214 value_def
1215 };
1216
1217 if !value_def.is_array() || value_def.len() != 2 {
1218 return Err(format!("Invalid BETWEEN clause for {field_name}: {value_def}").into());
1219 }
1220
1221 Ok(format!(
1222 "{} BETWEEN {} AND {}",
1223 self.select_one_field(class_alias, None, field_name, Some(value_def), false)?,
1224 self.scalar_param_as_string(class_alias, field_name, &value_def[0])?,
1225 self.scalar_param_as_string(class_alias, field_name, &value_def[1])?
1226 ))
1227 }
1228
1229 fn simple_search_predicate(
1237 &mut self,
1238 operator: &str,
1239 class_alias: &str,
1240 field_name: &str,
1241 value: &EgValue,
1242 ) -> EgResult<String> {
1243 if value.is_object() || value.is_array() {
1244 return Err(format!("Invalid simple search predicate: {}", value.dump()).into());
1245 }
1246
1247 let prefix = format!(r#""{class_alias}".{field_name}"#);
1248
1249 if value.is_null() {
1250 let val_str = if operator == "=" || operator.to_uppercase() == "IS" {
1251 "NULL"
1252 } else {
1253 "NOT NULL"
1254 };
1255
1256 return Ok(format!("{prefix} IS {val_str}"));
1257 } else if let Some(b) = value.as_bool() {
1258 let val_str = if b { "TRUE" } else { "FALSE" };
1259
1260 let oper_str = if operator == "=" || operator.to_uppercase() == "IS" {
1261 "IS"
1262 } else {
1263 "IS NOT"
1264 };
1265
1266 return Ok(format!("{prefix} {oper_str} {val_str}"));
1267 }
1268
1269 let param_str = self.scalar_param_as_string(class_alias, field_name, value)?;
1270
1271 Ok(format!(
1273 "{prefix} {} {param_str}",
1274 self.check_operator(operator)?,
1275 ))
1276 }
1277
1278 fn scalar_param_as_string(
1290 &mut self,
1291 class_alias: &str,
1292 field_name: &str,
1293 value: &EgValue,
1294 ) -> EgResult<String> {
1295 if !value.is_string() && !value.is_number() {
1296 return Err(format!("Invalid scalar value for field {field_name}: {value}").into());
1297 }
1298
1299 let classname = self.get_alias_classname(class_alias)?;
1302 let idl_class = idl::get_class(classname)?;
1303
1304 let idl_field = idl_class
1305 .get_field(field_name)
1306 .ok_or_else(|| format!("IDL class {classname} has no field named {field_name}"))?;
1307
1308 if idl_field.datatype().is_numeric() {
1309 if let Some(num) = value.as_int() {
1312 Ok(num.to_string())
1314 } else if let Some(num) = value.as_float() {
1315 Ok(num.to_string())
1317 } else {
1318 return Err(format!(
1319 "Field {field_name} is numeric, but query value isn't: {value}",
1320 )
1321 .into());
1322 }
1323 } else {
1324 Ok(format!("${}", self.add_param(value)?))
1326 }
1327 }
1328
1329 fn search_in_predicate(
1337 &mut self,
1338 class_alias: &str,
1339 field_name: &str,
1340 value_def: &EgValue,
1341 is_not_in: bool,
1342 ) -> EgResult<String> {
1343 let field_str =
1344 self.select_one_field(class_alias, None, field_name, Some(value_def), false)?;
1345
1346 let in_str = self.search_in_list(class_alias, field_name, value_def)?;
1347
1348 Ok(format!(
1349 "{field_str} {} ({in_str})",
1350 if is_not_in { "NOT IN" } else { "IN" }
1351 ))
1352 }
1353
1354 fn search_in_list(
1362 &mut self,
1363 class_alias: &str,
1364 field_name: &str,
1365 value_def: &EgValue,
1366 ) -> EgResult<String> {
1367 if !value_def.is_object() && !value_def.is_array() {
1368 return Err(format!("Unexpected IN clause: {value_def}").into());
1369 }
1370
1371 let value_def = if !value_def["value"].is_null() {
1372 &value_def["value"]
1373 } else {
1374 value_def
1375 };
1376
1377 if value_def.is_object() {
1378 return self.compile_sub_query(value_def);
1379 }
1380
1381 if value_def.is_empty() {
1382 return Err(format!("Empty IN list for field {field_name}"))?;
1383 }
1384
1385 let mut values = Vec::new();
1386 for value in value_def.members() {
1387 values.push(self.scalar_param_as_string(class_alias, field_name, value)?);
1388 }
1389
1390 Ok(values.join(", "))
1391 }
1392
1393 fn check_identifier<'a>(&'a self, s: &'a str) -> EgResult<&'a str> {
1397 if db::is_identifier(s) {
1398 Ok(s)
1399 } else {
1400 Err(format!("Value is not a valid identifier: {s}").into())
1401 }
1402 }
1403
1404 fn check_operator<'a>(&'a self, operator: &'a str) -> EgResult<&'a str> {
1408 if db::is_supported_operator(operator) {
1409 Ok(operator)
1410 } else {
1411 Err(format!("Invalid operator: {operator}").into())
1412 }
1413 }
1414
1415 fn add_param(&mut self, value: &EgValue) -> EgResult<usize> {
1419 let s = value
1420 .to_string()
1421 .ok_or_else(|| format!("Cannot stringify: {value}"))?;
1422 Ok(self.add_param_string(s))
1423 }
1424
1425 fn add_param_string(&mut self, value: String) -> usize {
1434 if let Some(list) = self.params.as_mut() {
1435 list.push(value);
1436 list.len()
1437 } else {
1438 self.params = Some(vec![value]);
1439 1
1440 }
1441 }
1442
1443 fn set_base_source(&mut self, from_blob: &EgValue) -> EgResult<&SourceDef> {
1449 let classname = if from_blob.is_object() && from_blob.len() == 1 {
1450 let (class, _) = from_blob.entries().next().unwrap();
1452 class
1453 } else if let Some(class) = from_blob.as_str() {
1454 class
1456 } else {
1457 return Err(format!("Invalid FROM clause: {from_blob}").into());
1458 };
1459
1460 let idl_class = self.get_idl_class(classname)?;
1461
1462 self.sources.push(SourceDef {
1464 idl_class: idl_class.clone(),
1465 alias: None,
1466 });
1467
1468 Ok(self.sources.first().unwrap())
1469 }
1470
1471 fn compile_function_query(&mut self, from_def: &EgValue) -> EgResult<String> {
1477 let from_str = self.compile_function_from(from_def)?;
1478
1479 let func_name = from_def[0].as_str().unwrap();
1481
1482 Ok(format!(r#"SELECT * FROM {from_str} AS "{func_name}""#))
1483 }
1484
1485 fn compile_function_from(&mut self, from_def: &EgValue) -> EgResult<String> {
1491 if from_def.is_empty() || !from_def.is_array() {
1492 return Err(format!("Invalid FROM function spec: {}", from_def.dump()).into());
1493 }
1494
1495 let func_name = match from_def[0].as_str() {
1496 Some(f) => self.check_identifier(f)?.to_string(),
1497 None => return Err(format!("Invalid function name: {}", from_def[0].dump()).into()),
1498 };
1499
1500 let mut sql = func_name.to_string();
1501
1502 if from_def.len() > 1 {
1503 let mut params = Vec::new();
1504
1505 for value in from_def.members().skip(1) {
1507 if value.is_null() {
1508 params.push("NULL".to_string());
1509 } else if let Some(b) = value.as_bool() {
1510 let s = if b { "TRUE" } else { "FALSE" };
1511 params.push(s.to_string());
1512 } else if value.is_string() {
1513 let index = self.add_param(value)?;
1514 params.push(format!("${index}"));
1515 } else if value.is_number() {
1516 params.push(value.to_string().unwrap());
1517 } else {
1518 return Err(format!("Invalid function parameter: {}", value.dump()).into());
1519 };
1520 }
1521
1522 sql += &format!("({})", ¶ms.join(", "));
1523 }
1524
1525 Ok(sql)
1526 }
1527}