1use crate as eg;
3use chrono::prelude::*;
4use eg::db;
5use eg::idl;
6use eg::util::Pager;
7use eg::EgResult;
8use eg::EgValue;
9use pg::types::ToSql;
10use postgres as pg;
11use rust_decimal::Decimal;
12use std::cell::RefCell;
13use std::collections::HashMap;
14use std::fmt;
15use std::rc::Rc;
16use std::sync::Arc;
17
18pub const MAX_FLESH_DEPTH: i16 = 100;
19
20#[derive(Debug, Clone, PartialEq)]
21pub enum OrderByDir {
22 Asc,
23 Desc,
24}
25
26impl fmt::Display for OrderByDir {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 write!(
29 f,
30 "{}",
31 match *self {
32 OrderByDir::Asc => "ASC",
33 _ => "DESC",
34 }
35 )
36 }
37}
38
39#[derive(Debug, Clone, PartialEq)]
40pub struct OrderBy {
41 field: String,
42 dir: OrderByDir,
43}
44
45impl OrderBy {
46 pub fn new(field: &str, dir: OrderByDir) -> Self {
47 OrderBy {
48 dir,
49 field: field.to_string(),
50 }
51 }
52}
53
54pub struct IdlClassCreate {
56 pub classname: String,
57 pub values: Vec<Vec<(String, EgValue)>>,
60}
61
62impl IdlClassCreate {
63 pub fn new(classname: &str) -> Self {
64 IdlClassCreate {
65 classname: classname.to_string(),
66 values: vec![vec![]],
67 }
68 }
69}
70
71pub struct IdlClassUpdate {
74 pub classname: String,
75 pub values: Vec<(String, EgValue)>,
76 pub filter: Option<EgValue>,
77}
78
79impl IdlClassUpdate {
80 pub fn new(classname: &str) -> Self {
81 IdlClassUpdate {
82 classname: classname.to_string(),
83 values: Vec::new(),
84 filter: None,
85 }
86 }
87 pub fn reset(&mut self) {
88 self.values = Vec::new();
89 self.filter = None;
90 }
91 pub fn values(&self) -> &Vec<(String, EgValue)> {
92 &self.values
93 }
94
95 pub fn add_value(&mut self, field: &str, value: &EgValue) {
96 self.values.push((field.to_string(), value.clone()));
97 }
98
99 pub fn filter(&self) -> &Option<EgValue> {
100 &self.filter
101 }
102
103 pub fn set_filter(&mut self, f: EgValue) {
104 self.filter = Some(f);
105 }
106}
107
108#[derive(Debug, Clone)]
109pub struct FleshDef {
110 pub fields: HashMap<String, Vec<String>>,
111 pub depth: i16,
113}
114
115impl FleshDef {
116 pub fn from_eg_value(obj: &EgValue) -> EgResult<Self> {
133 let mut fields = HashMap::new();
134
135 for (classname, field_names) in obj["flesh_fields"].entries() {
136 let mut list = Vec::new();
137 for name in field_names.members() {
138 let n = name
139 .as_str()
140 .ok_or_else(|| format!("Invalid flesh definition: {}", obj.dump()))?;
141 list.push(n.to_string());
142 }
143
144 fields.insert(classname.to_string(), list);
145 }
146
147 let depth = if let Some(num) = obj["flesh"].as_i16() {
148 if (0..=MAX_FLESH_DEPTH).contains(&num) {
150 num
151 } else {
152 MAX_FLESH_DEPTH
154 }
155 } else {
156 0
157 };
158
159 Ok(FleshDef { depth, fields })
160 }
161}
162
163#[derive(Debug)]
165pub struct IdlClassSearch {
166 pub classname: String,
167 pub filter: Option<EgValue>,
168 pub order_by: Option<Vec<OrderBy>>,
169 pub pager: Option<Pager>,
170 pub flesh: Option<FleshDef>,
171}
172
173impl IdlClassSearch {
174 pub fn new(classname: &str) -> Self {
175 IdlClassSearch {
176 classname: classname.to_string(),
177 filter: None,
178 order_by: None,
179 pager: None,
180 flesh: None,
181 }
182 }
183
184 pub fn set_flesh(&mut self, flesh_def: FleshDef) {
185 self.flesh = Some(flesh_def);
186 }
187
188 pub fn classname(&self) -> &str {
189 &self.classname
190 }
191
192 pub fn filter(&self) -> &Option<EgValue> {
193 &self.filter
194 }
195
196 pub fn set_filter(&mut self, f: EgValue) {
197 self.filter = Some(f);
198 }
199
200 pub fn order_by(&self) -> &Option<Vec<OrderBy>> {
201 &self.order_by
202 }
203
204 pub fn set_order_by(&mut self, v: Vec<OrderBy>) {
205 self.order_by = Some(v);
206 }
207
208 pub fn pager(&self) -> &Option<Pager> {
209 &self.pager
210 }
211
212 pub fn set_pager(&mut self, pager: Pager) {
213 self.pager = Some(pager);
214 }
215}
216
217pub struct Translator {
219 db: Rc<RefCell<db::DatabaseConnection>>,
220}
221
222impl Translator {
223 pub fn new(db: Rc<RefCell<db::DatabaseConnection>>) -> Self {
224 Translator { db }
225 }
226
227 pub fn xact_begin(&mut self) -> EgResult<()> {
229 self.db.borrow_mut().xact_begin()
230 }
231
232 pub fn xact_commit(&mut self) -> EgResult<()> {
234 self.db.borrow_mut().xact_commit()
235 }
236
237 pub fn xact_rollback(&mut self) -> EgResult<()> {
239 self.db.borrow_mut().xact_rollback()
240 }
241
242 pub fn get_idl_object_by_pkey(
247 &self,
248 classname: &str,
249 pkey: &EgValue,
250 flesh_def: Option<FleshDef>,
251 ) -> EgResult<Option<EgValue>> {
252 let idl_class = idl::get_class(classname)?;
253
254 let pkey_field = match idl_class.pkey_field() {
255 Some(f) => f,
256 None => return Err(format!("Class {classname} has no primary key field").into()),
257 };
258
259 let mut filter = EgValue::new_object();
260 filter
261 .insert(pkey_field.name(), pkey.clone())
262 .expect("Is Object");
263
264 let mut search = IdlClassSearch::new(classname);
265 search.set_filter(filter);
266 search.flesh = flesh_def;
267
268 let mut list = self.idl_class_search(&search)?;
269
270 match list.len() {
271 0 => Ok(None),
272 1 => Ok(Some(list.pop().unwrap())),
273 _ => Err(format!("Pkey query for {classname} returned {} results", list.len()).into()),
274 }
275 }
276
277 pub fn flesh_idl_object(&self, object: &mut EgValue, flesh_def: &FleshDef) -> EgResult<()> {
279 if flesh_def.depth == 0 {
280 log::warn!("Attempt to flesh beyond flesh depth");
281 return Ok(());
282 }
283
284 let idl_class = self.get_idl_class_from_object(object)?.clone();
285 let classname = idl_class.classname();
286
287 let fieldnames;
289
290 if let Some(list) = flesh_def.fields.get(classname) {
291 fieldnames = list.clone();
292 } else {
293 return Ok(());
296 }
297
298 for fieldname in fieldnames.iter() {
300 self.flesh_idl_object_field(object, flesh_def, fieldname, &idl_class)?;
301 }
302
303 Ok(())
304 }
305
306 fn flesh_idl_object_field(
308 &self,
309 object: &mut EgValue,
310 flesh_def: &FleshDef,
311 fieldname: &str,
312 idl_class: &idl::Class,
313 ) -> EgResult<()> {
314 let classname = idl_class.classname();
315
316 let mut flesh_def = flesh_def.clone();
319
320 let idl_link = idl_class
321 .links()
322 .get(fieldname)
323 .ok_or_else(|| format!("Field {fieldname} on class {classname} cannot be fleshed"))?;
324
325 let reltype = idl_link.reltype();
326
327 let search_value = if reltype == idl::RelType::HasMany || reltype == idl::RelType::MightHave
328 {
329 object
334 .pkey_value()
335 .ok_or_else(|| format!("Object has no pkey value: {}", object.dump()))?
336 } else {
337 &object[fieldname]
339 };
340
341 if !search_value.is_string() && !search_value.is_number() {
342 return Err(format!(
343 "Class {classname} cannot flesh field {fieldname} on value: {}",
344 search_value.dump()
345 )
346 .into());
347 }
348
349 if let Some(map_field) = idl_link.map() {
354 let cname = idl_link.class();
357 let fname = map_field.to_string();
358
359 if let Some(list) = flesh_def.fields.get_mut(cname) {
360 list.push(fname);
361 } else {
362 flesh_def.fields.insert(cname.to_string(), vec![fname]);
363 }
364 } else {
365 flesh_def.depth -= 1;
368 }
369
370 log::debug!(
371 "Fleshing {}.{}; Link field: {}, remote class: {} , fkey: {}, reltype: {}",
372 classname,
373 fieldname,
374 idl_link.field(),
375 idl_link.class(),
376 idl_link.key(),
377 idl_link.reltype(),
378 );
379
380 let mut class_search = IdlClassSearch::new(idl_link.class());
381 class_search.flesh = Some(flesh_def);
382
383 let mut filter = eg::hash! {};
384 filter[idl_link.key()] = search_value.clone();
385 class_search.set_filter(filter);
386
387 let mut children = self.idl_class_search(&class_search)?;
388
389 log::debug!("Fleshed search returned {} results", children.len());
390
391 if !children.is_empty() {
392 if let Some(map_field) = idl_link.map() {
394 let mut mapped_values = Vec::new();
395 for mut child in children.drain(..) {
396 mapped_values.push(child[map_field].take());
397 }
398 children = mapped_values;
399 }
400 }
401
402 if !children.is_empty()
407 && (reltype == idl::RelType::HasA || reltype == idl::RelType::MightHave)
408 {
409 object[fieldname] = children.remove(0); }
411
412 if reltype == idl::RelType::HasMany {
414 object[fieldname] = EgValue::from(children);
415 }
416
417 Ok(())
418 }
419
420 pub fn get_idl_class_from_object<'a>(&self, obj: &'a EgValue) -> EgResult<&'a Arc<idl::Class>> {
422 obj.idl_class()
423 .ok_or_else(|| format!("Not an IDL object: {}", obj.dump()).into())
424 }
425
426 pub fn create_idl_object(&self, obj: &EgValue) -> EgResult<EgValue> {
430 let idl_class = self.get_idl_class_from_object(obj)?;
431
432 let mut create = IdlClassCreate::new(idl_class.classname());
433 let values = &mut create.values[0]; for name in idl_class.real_field_names_sorted() {
436 values.push((name.to_string(), obj[name].clone()));
437 }
438
439 let mut values = self.idl_class_create(&create)?;
440
441 if let Some(v) = values.pop() {
442 Ok(v)
443 } else {
444 Err(format!(
447 "Could not create new value for class: {}",
448 idl_class.classname()
449 )
450 .into())
451 }
452 }
453
454 pub fn idl_class_create(&self, create: &IdlClassCreate) -> EgResult<Vec<EgValue>> {
458 if !create.values.is_empty() {
459 return Err("No values to create in idl_class_create()".into());
460 }
461
462 if !self.db.borrow().in_transaction() {
463 return Err("idl_class_create() requires a transaction".into());
464 }
465
466 let classname = &create.classname;
467
468 let idl_class = idl::get_class(classname)?;
469
470 let tablename = idl_class.tablename().ok_or_else(|| {
471 format!("Cannot query an IDL class that has no tablename: {classname}")
472 })?;
473
474 let pkey_field = idl_class
475 .pkey()
476 .ok_or_else(|| "Cannot create rows that have no primary key".to_string())?;
477
478 let mut query = format!("INSERT INTO {tablename} (");
479
480 query += &idl_class.real_field_names_sorted().join(", ");
482
483 query += ") VALUES ";
484
485 let mut param_index: usize = 1;
487 let mut param_list: Vec<String> = Vec::new();
488 let mut strings: Vec<String> = Vec::new();
489 for values in &create.values {
490 strings.push(self.compile_class_create(
491 idl_class,
492 values,
493 &mut param_index,
494 &mut param_list,
495 )?);
496 }
497
498 query += &strings.join(", ");
499
500 query += &format!(" RETURNING {pkey_field}");
502
503 let mut params: Vec<&(dyn ToSql + Sync)> = Vec::new();
504 for p in param_list.iter() {
505 params.push(p);
506 }
507
508 log::debug!("create() executing query: {query}; params=[{param_list:?}]");
509
510 let query_res = self.db.borrow_mut().client().query(&query, ¶ms);
511
512 if let Err(ref e) = query_res {
513 log::error!("DB Error: {e} query={query} param={params:?}");
514 return Err("DB query failed. See error logs".into());
515 }
516
517 let mut results: Vec<EgValue> = Vec::new();
520
521 for row in query_res.unwrap() {
522 let pkey_value = Translator::col_value_to_json_value(&row, 0)?;
523
524 match self.get_idl_object_by_pkey(idl_class.classname(), &pkey_value, None)? {
525 Some(pkv) => results.push(pkv),
526 None => return Err("Could not recover newly created value from the DB".into()),
527 };
528 }
529
530 Ok(results)
531 }
532
533 pub fn update_idl_object(&self, obj: &EgValue) -> EgResult<u64> {
535 let idl_class = self.get_idl_class_from_object(obj)?;
536
537 let mut update = IdlClassUpdate::new(idl_class.classname());
538 for name in idl_class.real_field_names_sorted() {
539 update.add_value(name, &obj[name]);
540 }
541
542 let (pkey_field, pkey_value) = obj
543 .pkey_info()
544 .ok_or_else(|| "Object has no primary key field".to_string())?;
545
546 let mut filter = EgValue::new_object();
547 filter
548 .insert(pkey_field.name(), pkey_value.clone())
549 .unwrap();
550
551 update.set_filter(filter);
552
553 self.idl_class_update(&update)
554 }
555
556 pub fn idl_class_update(&self, update: &IdlClassUpdate) -> EgResult<u64> {
560 if update.values.is_empty() {
561 Err("No values to update in idl_class_update()".to_string())?;
562 }
563
564 if !self.db.borrow().in_transaction() {
565 return Err("idl_class_update() requires a transaction".into());
566 }
567
568 let classname = &update.classname;
569
570 let class = idl::get_class(classname)?;
571
572 let tablename = class.tablename().ok_or_else(|| {
573 format!("Cannot query an IDL class that has no tablename: {classname}")
574 })?;
575
576 let mut param_list: Vec<String> = Vec::new();
577 let mut param_index: usize = 1;
578 let updates =
579 self.compile_class_update(class, &update.values, &mut param_index, &mut param_list)?;
580
581 let mut query = format!("UPDATE {tablename} {updates}");
582
583 if let Some(filter) = update.filter() {
584 query +=
585 &self.compile_class_filter(class, filter, &mut param_index, &mut param_list)?;
586 }
587
588 let mut params: Vec<&(dyn ToSql + Sync)> = Vec::new();
589 for p in param_list.iter() {
590 params.push(p);
591 }
592
593 log::debug!("update() executing query: {query}; params=[{param_list:?}]");
594
595 self.execute_one(&query, ¶ms)
596 }
597
598 fn execute_one(&self, query: &str, params: &[&(dyn ToSql + Sync)]) -> EgResult<u64> {
600 log::debug!("update() executing query: {query}; params=[{params:?}]");
601
602 let query_res = self.db.borrow_mut().client().execute(query, params);
603
604 match query_res {
605 Ok(v) => {
606 log::debug!("Update modified {v} rows");
607 Ok(v)
608 }
609 Err(e) => {
610 log::error!("DB Error: {e} query={query} param={params:?}");
611 Err("DB query failed. See error logs".into())
612 }
613 }
614 }
615
616 pub fn delete_idl_object_by_pkey(&self, classname: &str, pkey: &EgValue) -> EgResult<u64> {
620 if !self.db.borrow().in_transaction() {
621 return Err("delete_idl_object_by_pkey requires a transaction".into());
622 }
623
624 let class = idl::get_class(classname)?;
625
626 let tablename = class.tablename().ok_or_else(|| {
627 format!("Cannot query an IDL class that has no tablename: {classname}")
628 })?;
629
630 let pkey_field = class
631 .pkey_field()
632 .ok_or_else(|| format!("IDL class {classname} has no primary key field"))?;
633
634 let mut param_list: Vec<String> = Vec::new();
635 let mut param_index: usize = 1;
636
637 let mut query = format!("DELETE FROM {tablename} WHERE {} ", pkey_field.name());
638
639 query += &self.append_json_literal(
640 &mut param_index,
641 &mut param_list,
642 pkey_field,
643 pkey,
644 Some("="),
645 false,
646 )?;
647
648 let mut params: Vec<&(dyn ToSql + Sync)> = Vec::new();
649 for p in param_list.iter() {
650 params.push(p);
651 }
652
653 self.execute_one(&query, ¶ms)
654 }
655
656 pub fn idl_class_search(&self, search: &IdlClassSearch) -> EgResult<Vec<EgValue>> {
660 let mut results: Vec<EgValue> = Vec::new();
661 let classname = &search.classname;
662
663 log::debug!("idl_class_search() {search:?}");
664
665 let class = idl::get_class(classname)?;
666
667 let tablename = class.tablename().ok_or_else(|| {
668 format!("Cannot query an IDL class that has no tablename: {classname}")
669 })?;
670
671 let columns = class.real_field_names_sorted().join(", ");
672
673 let mut query = format!("SELECT {columns} FROM {tablename}");
674
675 let mut param_list: Vec<String> = Vec::new();
678 let mut param_index: usize = 1;
679
680 if let Some(filter) = &search.filter {
681 query +=
682 &self.compile_class_filter(class, filter, &mut param_index, &mut param_list)?;
683 }
684
685 if let Some(order) = &search.order_by {
686 query += &self.compile_class_order_by(order);
687 }
688
689 if let Some(pager) = &search.pager {
690 query += &self.compile_pager(pager);
691 }
692
693 log::debug!("search() executing query: {query}");
694
695 let mut params: Vec<&(dyn ToSql + Sync)> = Vec::new();
696 for p in param_list.iter() {
697 params.push(p);
698 }
699
700 let query_res = self.db.borrow_mut().client().query(&query, ¶ms);
701
702 if let Err(ref e) = query_res {
703 log::error!("DB Error: {e} query={query} param={params:?}");
704 return Err("DB query failed. See error logs".into());
705 }
706
707 for row in query_res.unwrap() {
708 let mut obj = self.row_to_idl(class, &row)?;
709 if let Some(flesh_def) = search.flesh.as_ref() {
710 self.flesh_idl_object(&mut obj, flesh_def)?;
711 }
712 results.push(obj);
713 }
714
715 Ok(results)
716 }
717
718 fn compile_class_order_by(&self, order: &Vec<OrderBy>) -> String {
720 let mut sql = String::new();
721 let mut count = order.len();
722
723 if count > 0 {
724 sql += " ORDER BY";
725 for order_by in order {
726 sql += &format!(" {} {}", &order_by.field, &order_by.dir);
727 count -= 1;
728 if count > 0 {
729 sql += ",";
730 }
731 }
732 }
733
734 sql
735 }
736
737 fn try_translate_numeric(
745 &self,
746 idl_field: &idl::Field,
747 value: &EgValue,
748 ) -> EgResult<Option<EgValue>> {
749 if !value.is_string() {
750 return Ok(None);
751 }
752
753 if !idl_field.datatype().is_numeric() {
754 return Ok(None);
755 }
756
757 if let Some(n) = value.as_int() {
758 Ok(Some(EgValue::from(n)))
759 } else if let Some(n) = value.as_float() {
760 Ok(Some(EgValue::from(n)))
761 } else {
762 Err(format!("Value cannot be coerced int a number: {value}").into())
763 }
764 }
765
766 fn compile_class_create(
768 &self,
769 class: &idl::Class,
770 values: &Vec<(String, EgValue)>,
771 param_index: &mut usize,
772 param_list: &mut Vec<String>,
773 ) -> EgResult<String> {
774 let mut sql = String::from("(");
775 let mut strings = Vec::new();
776
777 for kvp in values {
778 let field = &kvp.0;
779 let value = &kvp.1;
780
781 let idl_field = class.get_real_field(field).ok_or_else(|| {
782 format!(
783 "No such real field '{field}' on class '{}'",
784 class.classname()
785 )
786 })?;
787
788 strings.push(self.append_json_literal(
789 param_index,
790 param_list,
791 idl_field,
792 value,
793 None,
794 true,
795 )?);
796 }
797
798 sql += &strings.join(", ");
799
800 sql += ")";
801
802 Ok(sql)
803 }
804
805 fn compile_class_update(
807 &self,
808 class: &idl::Class,
809 values: &Vec<(String, EgValue)>,
810 param_index: &mut usize,
811 param_list: &mut Vec<String>,
812 ) -> EgResult<String> {
813 let mut parts = Vec::new();
814
815 for kvp in values {
816 let field = &kvp.0;
817 let value = &kvp.1;
818
819 let idl_field = class.get_real_field(field).ok_or_else(|| {
820 format!(
821 "No such real field '{field}' on class '{}'",
822 class.classname()
823 )
824 })?;
825
826 parts.push(format!(
827 "{field} {}",
828 self.append_json_literal(
829 param_index,
830 param_list,
831 idl_field,
832 value,
833 Some("="),
834 false,
835 )?
836 ));
837 }
838
839 Ok(format!("SET {}", parts.join(", ")))
840 }
841
842 fn compile_pager(&self, pager: &Pager) -> String {
844 format!(" LIMIT {} OFFSET {}", pager.limit(), pager.offset())
845 }
846
847 fn compile_class_filter(
849 &self,
850 class: &idl::Class,
851 filter: &EgValue,
852 param_index: &mut usize,
853 param_list: &mut Vec<String>,
854 ) -> EgResult<String> {
855 if !filter.is_object() {
856 return Err(format!(
857 "Translator class filter must be an object: {}",
858 filter.dump()
859 )
860 .into());
861 }
862
863 let mut filters = Vec::new();
864 for (field, subq) in filter.entries() {
865 log::trace!("compile_class_filter adding filter on field: {field}");
866
867 let idl_field = class.get_real_field(field).ok_or_else(|| {
868 format!(
869 "No such real field '{field}' on class '{}'",
870 class.classname()
871 )
872 })?;
873
874 let filter = match subq {
875 EgValue::Array(_) => {
876 self.compile_class_filter_array(param_index, param_list, idl_field, subq, "IN")?
877 }
878 EgValue::Hash(_) => {
879 self.compile_class_filter_object(param_index, param_list, idl_field, subq)?
880 }
881 EgValue::Number(_) | EgValue::String(_) => self.append_json_literal(
882 param_index,
883 param_list,
884 idl_field,
885 subq,
886 Some("="),
887 false,
888 )?,
889 EgValue::Boolean(_) | EgValue::Null => self.append_json_literal(
890 param_index,
891 param_list,
892 idl_field,
893 subq,
894 Some("IS"),
895 false,
896 )?,
897 EgValue::Blessed(_) => {
898 return Err("Cannot create JSON filter from a blessed value".into())
899 }
900 };
901
902 filters.push(format!(" {field} {filter}"));
903 }
904
905 Ok(format!(" WHERE {}", filters.join(" AND")))
906 }
907
908 fn append_json_literal(
914 &self,
915 param_index: &mut usize,
916 param_list: &mut Vec<String>,
917 idl_field: &idl::Field,
918 obj: &EgValue,
919 operand: Option<&str>,
920 use_default: bool,
921 ) -> EgResult<String> {
922 if obj.is_object() || obj.is_array() {
923 return Err(format!("Cannot format array/object as a literal: {obj:?}").into());
924 }
925
926 if use_default && obj.is_null() {
927 return Ok("DEFAULT".to_string());
928 }
929
930 let opstr = match operand {
931 Some(op) => format!("{op} "),
932 None => String::new(),
933 };
934
935 let new_obj = self.try_translate_numeric(idl_field, obj)?;
937 let obj = new_obj.as_ref().unwrap_or(obj);
938
939 if obj.is_string() {
945 let s = format!("{opstr}${param_index}");
946 param_list.push(obj.to_string().expect("Is String"));
947 *param_index += 1;
948 Ok(s)
949 } else {
950 Ok(format!("{opstr}{}", obj))
952 }
953 }
954
955 fn compile_class_filter_object(
957 &self,
958 param_index: &mut usize,
959 param_list: &mut Vec<String>,
960 idl_field: &idl::Field,
961 obj: &EgValue,
962 ) -> EgResult<String> {
963 let (key, val) = obj
965 .entries()
966 .next()
967 .ok_or_else(|| format!("Invalid query object; {obj:?}"))?;
968
969 let operand = key.to_uppercase();
970
971 if !db::is_supported_operator(&operand) {
972 Err(format!("Unsupported operand: {operand} : {obj}"))?;
973 }
974
975 if val.is_array() {
976 self.compile_class_filter_array(
979 param_index,
980 param_list,
981 idl_field,
982 val,
983 operand.as_str(),
984 )
985 } else {
986 self.append_json_literal(
987 param_index,
988 param_list,
989 idl_field,
990 val,
991 Some(&operand),
992 false,
993 )
994 }
995 }
996
997 fn compile_class_filter_array(
1001 &self,
1002 param_index: &mut usize,
1003 param_list: &mut Vec<String>,
1004 idl_field: &idl::Field,
1005 arr: &EgValue,
1006 operand: &str,
1007 ) -> EgResult<String> {
1008 let operand = operand.to_uppercase();
1009 if !db::is_supported_operator(&operand) {
1010 Err(format!("Unsupported operand: {operand} : {arr}"))?;
1011 }
1012
1013 let mut filters: Vec<String> = Vec::new();
1014 for val in arr.members() {
1015 filters.push(self.append_json_literal(
1016 param_index,
1017 param_list,
1018 idl_field,
1019 val,
1020 None,
1021 false,
1022 )?);
1023 }
1024
1025 Ok(format!("{operand} ({})", filters.join(", ")))
1026 }
1027
1028 fn row_to_idl(&self, class: &idl::Class, row: &pg::Row) -> EgResult<EgValue> {
1030 let mut obj = EgValue::new_object();
1031 obj.bless(class.classname())?;
1032
1033 for (index, name) in class.real_field_names_sorted().into_iter().enumerate() {
1034 obj[name] = Translator::col_value_to_json_value(row, index)?;
1035 }
1036
1037 Ok(obj)
1038 }
1039
1040 pub fn col_value_to_json_value(row: &pg::Row, index: usize) -> EgResult<EgValue> {
1042 let col_type = row.columns().get(index).map(|c| c.type_().name()).unwrap();
1043
1044 match col_type {
1045 "bool" => {
1047 let v: Option<bool> = row.get(index);
1048 Ok(EgValue::from(v))
1049 }
1050 "interval" => {
1051 let v: Option<pg_interval::Interval> = row.get(index);
1052 let s = match v {
1053 Some(val) => val.to_postgres(),
1054 None => return Ok(EgValue::Null),
1055 };
1056 Ok(EgValue::from(s))
1057 }
1058 "varchar" | "char(n)" | "text" | "name" => {
1059 let v: Option<String> = row.get(index);
1060 Ok(EgValue::from(v))
1061 }
1062 "date" => {
1063 let v: Option<chrono::NaiveDate> = row.get(index);
1064 let s = match v {
1065 Some(val) => val.format("%F").to_string(),
1066 None => return Ok(EgValue::Null),
1067 };
1068 Ok(EgValue::from(s))
1069 }
1070 "timestamp" | "timestamptz" => {
1071 let v: Option<chrono::DateTime<Utc>> = row.get(index);
1072 let s = match v {
1073 Some(val) => val.format("%FT%T%z").to_string(),
1074 None => return Ok(EgValue::Null),
1075 };
1076 Ok(EgValue::from(s))
1077 }
1078 "int2" | "smallserial" | "smallint" => {
1079 let v: Option<i16> = row.get(index);
1080 Ok(EgValue::from(v))
1081 }
1082 "int" | "int4" | "serial" => {
1083 let v: Option<i32> = row.get(index);
1084 Ok(EgValue::from(v))
1085 }
1086 "int8" | "bigserial" | "bigint" => {
1087 let v: Option<i64> = row.get(index);
1088 Ok(EgValue::from(v))
1089 }
1090 "float4" | "real" => {
1091 let v: Option<f32> = row.get(index);
1092 Ok(EgValue::from(v))
1093 }
1094 "float8" | "double precision" => {
1095 let v: Option<f64> = row.get(index);
1096 Ok(EgValue::from(v))
1097 }
1098 "numeric" => {
1099 let decimal: Option<Decimal> = row.get(index);
1100 match decimal {
1101 Some(d) => Ok(EgValue::from(d.to_string())),
1102 None => Ok(EgValue::Null),
1103 }
1104 }
1105 "tsvector" => Ok(EgValue::Null),
1106 _ => Err(format!("Unsupported column type: {col_type}").into()),
1107 }
1108 }
1109}