|
1 | 1 | //! APIs for building a query of a database. |
2 | 2 |
|
3 | | -use std::{iter::once, sync::Arc}; |
| 3 | +use std::{iter::once, mem, sync::Arc}; |
4 | 4 |
|
5 | 5 | use crate::numeric_id::{DenseIdMap, IdVec, NumericId, define_id}; |
6 | 6 | use smallvec::SmallVec; |
@@ -444,7 +444,60 @@ impl RuleBuilder<'_, '_> { |
444 | 444 | } |
445 | 445 | } |
446 | 446 |
|
| 447 | + fn normalize_single_external_arg_order(&mut self) { |
| 448 | + let Some(arg_vars) = single_external_arg_vars(&self.qb.instrs) else { |
| 449 | + return; |
| 450 | + }; |
| 451 | + |
| 452 | + let mut preferred = SmallVec::<[Variable; 4]>::new(); |
| 453 | + preferred.extend(arg_vars.iter().copied()); |
| 454 | + preferred.extend( |
| 455 | + self.qb |
| 456 | + .query |
| 457 | + .var_info |
| 458 | + .iter() |
| 459 | + .map(|(var, _)| var) |
| 460 | + .filter(|var| !arg_vars.contains(var)), |
| 461 | + ); |
| 462 | + |
| 463 | + let current = SmallVec::<[Variable; 4]>::from_iter( |
| 464 | + self.qb.query.var_info.iter().map(|(var, _)| var), |
| 465 | + ); |
| 466 | + if preferred == current { |
| 467 | + return; |
| 468 | + } |
| 469 | + |
| 470 | + let mut remap = DenseIdMap::with_capacity(self.qb.query.var_info.n_ids()); |
| 471 | + for (idx, old_var) in preferred.iter().enumerate() { |
| 472 | + remap.insert(*old_var, Variable::from_usize(idx)); |
| 473 | + } |
| 474 | + |
| 475 | + let mut old_var_info = mem::take(&mut self.qb.query.var_info); |
| 476 | + for old_var in preferred { |
| 477 | + self.qb |
| 478 | + .query |
| 479 | + .var_info |
| 480 | + .push(old_var_info.take(old_var).expect("all vars must be present")); |
| 481 | + } |
| 482 | + |
| 483 | + for (_, atom) in self.qb.query.atoms.iter_mut() { |
| 484 | + let old_var_to_column = mem::take(&mut atom.var_to_column); |
| 485 | + atom.var_to_column = old_var_to_column |
| 486 | + .into_iter() |
| 487 | + .map(|(var, col)| (remap[var], col)) |
| 488 | + .collect(); |
| 489 | + for (_, var) in atom.column_to_var.iter_mut() { |
| 490 | + *var = remap[*var]; |
| 491 | + } |
| 492 | + } |
| 493 | + |
| 494 | + for instr in self.qb.instrs.iter_mut() { |
| 495 | + remap_instr(instr, &remap); |
| 496 | + } |
| 497 | + } |
| 498 | + |
447 | 499 | pub fn build_with_description(mut self, desc: impl Into<String>) -> RuleId { |
| 500 | + self.normalize_single_external_arg_order(); |
448 | 501 | let var_info = &self.qb.query.var_info; |
449 | 502 | let symbol_map = self.build_symbol_map(); |
450 | 503 | // Generate an id for our actions and slot them in. |
@@ -769,6 +822,127 @@ impl RuleBuilder<'_, '_> { |
769 | 822 | } |
770 | 823 | } |
771 | 824 |
|
| 825 | +fn single_external_arg_vars(instrs: &[Instr]) -> Option<SmallVec<[Variable; 4]>> { |
| 826 | + let [Instr::External { args, .. }] = instrs else { |
| 827 | + return None; |
| 828 | + }; |
| 829 | + |
| 830 | + let mut vars = SmallVec::<[Variable; 4]>::new(); |
| 831 | + for arg in args { |
| 832 | + let QueryEntry::Var(var) = arg else { |
| 833 | + return None; |
| 834 | + }; |
| 835 | + if vars.contains(var) { |
| 836 | + return None; |
| 837 | + } |
| 838 | + vars.push(*var); |
| 839 | + } |
| 840 | + Some(vars) |
| 841 | +} |
| 842 | + |
| 843 | +fn remap_query_entry(entry: &mut QueryEntry, remap: &DenseIdMap<Variable, Variable>) { |
| 844 | + if let QueryEntry::Var(var) = entry { |
| 845 | + *var = remap[*var]; |
| 846 | + } |
| 847 | +} |
| 848 | + |
| 849 | +fn remap_write_val(val: &mut WriteVal, remap: &DenseIdMap<Variable, Variable>) { |
| 850 | + if let WriteVal::QueryEntry(entry) = val { |
| 851 | + remap_query_entry(entry, remap); |
| 852 | + } |
| 853 | +} |
| 854 | + |
| 855 | +fn remap_instr(instr: &mut Instr, remap: &DenseIdMap<Variable, Variable>) { |
| 856 | + match instr { |
| 857 | + Instr::LookupOrInsertDefault { |
| 858 | + args, |
| 859 | + default, |
| 860 | + dst_var, |
| 861 | + .. |
| 862 | + } => { |
| 863 | + args.iter_mut() |
| 864 | + .for_each(|entry| remap_query_entry(entry, remap)); |
| 865 | + default |
| 866 | + .iter_mut() |
| 867 | + .for_each(|val| remap_write_val(val, remap)); |
| 868 | + *dst_var = remap[*dst_var]; |
| 869 | + } |
| 870 | + Instr::LookupWithDefault { |
| 871 | + args, |
| 872 | + default, |
| 873 | + dst_var, |
| 874 | + .. |
| 875 | + } => { |
| 876 | + args.iter_mut() |
| 877 | + .for_each(|entry| remap_query_entry(entry, remap)); |
| 878 | + remap_query_entry(default, remap); |
| 879 | + *dst_var = remap[*dst_var]; |
| 880 | + } |
| 881 | + Instr::Lookup { |
| 882 | + args, dst_var, .. |
| 883 | + } => { |
| 884 | + args.iter_mut() |
| 885 | + .for_each(|entry| remap_query_entry(entry, remap)); |
| 886 | + *dst_var = remap[*dst_var]; |
| 887 | + } |
| 888 | + Instr::LookupWithFallback { |
| 889 | + table_key, |
| 890 | + func_args, |
| 891 | + dst_var, |
| 892 | + .. |
| 893 | + } => { |
| 894 | + table_key |
| 895 | + .iter_mut() |
| 896 | + .for_each(|entry| remap_query_entry(entry, remap)); |
| 897 | + func_args |
| 898 | + .iter_mut() |
| 899 | + .for_each(|entry| remap_query_entry(entry, remap)); |
| 900 | + *dst_var = remap[*dst_var]; |
| 901 | + } |
| 902 | + Instr::Insert { vals, .. } => { |
| 903 | + vals.iter_mut() |
| 904 | + .for_each(|entry| remap_query_entry(entry, remap)); |
| 905 | + } |
| 906 | + Instr::InsertIfEq { l, r, vals, .. } => { |
| 907 | + remap_query_entry(l, remap); |
| 908 | + remap_query_entry(r, remap); |
| 909 | + vals.iter_mut() |
| 910 | + .for_each(|entry| remap_query_entry(entry, remap)); |
| 911 | + } |
| 912 | + Instr::Remove { args, .. } => { |
| 913 | + args.iter_mut() |
| 914 | + .for_each(|entry| remap_query_entry(entry, remap)); |
| 915 | + } |
| 916 | + Instr::External { args, dst, .. } => { |
| 917 | + args.iter_mut() |
| 918 | + .for_each(|entry| remap_query_entry(entry, remap)); |
| 919 | + *dst = remap[*dst]; |
| 920 | + } |
| 921 | + Instr::ExternalWithFallback { |
| 922 | + args1, args2, dst, .. |
| 923 | + } => { |
| 924 | + args1 |
| 925 | + .iter_mut() |
| 926 | + .for_each(|entry| remap_query_entry(entry, remap)); |
| 927 | + args2 |
| 928 | + .iter_mut() |
| 929 | + .for_each(|entry| remap_query_entry(entry, remap)); |
| 930 | + *dst = remap[*dst]; |
| 931 | + } |
| 932 | + Instr::AssertEq(l, r) | Instr::AssertNe(l, r) => { |
| 933 | + remap_query_entry(l, remap); |
| 934 | + remap_query_entry(r, remap); |
| 935 | + } |
| 936 | + Instr::AssertAnyNe { ops, .. } => { |
| 937 | + ops.iter_mut() |
| 938 | + .for_each(|entry| remap_query_entry(entry, remap)); |
| 939 | + } |
| 940 | + Instr::ReadCounter { dst, .. } => { |
| 941 | + *dst = remap[*dst]; |
| 942 | + } |
| 943 | + } |
| 944 | +} |
| 945 | + |
772 | 946 | #[derive(Debug, Clone)] |
773 | 947 | pub(crate) struct Atom { |
774 | 948 | pub(crate) table: TableId, |
|
0 commit comments