use crate::ir::IrError;
use crate::ir::catalog::Catalog;
pub fn run(cat: &mut Catalog) -> Result<(), IrError> {
for dict in &mut cat.ts_dictionaries {
dict.options.sort_by(|a, b| a.0.cmp(&b.0));
}
cat.ts_dictionaries.sort_by_key(|d| d.qname.render_sql());
for w in cat.ts_dictionaries.windows(2) {
if w[0].qname == w[1].qname {
return Err(IrError::DuplicateTsDictionary(w[0].qname.clone()));
}
}
for cfg in &mut cat.ts_configurations {
cfg.mappings.sort_by(|a, b| a.token_type.cmp(&b.token_type));
}
cat.ts_configurations.sort_by_key(|c| c.qname.render_sql());
for w in cat.ts_configurations.windows(2) {
if w[0].qname == w[1].qname {
return Err(IrError::DuplicateTsConfiguration(w[0].qname.clone()));
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::identifier::{Identifier, QualifiedName};
use crate::ir::text_search::{TsConfiguration, TsDictionary, TsMapping};
fn id(s: &str) -> Identifier {
Identifier::from_unquoted(s).unwrap()
}
fn qname(schema: &str, name: &str) -> QualifiedName {
QualifiedName::new(id(schema), id(name))
}
fn dict(schema: &str, name: &str) -> TsDictionary {
TsDictionary {
qname: qname(schema, name),
template: qname("pg_catalog", "snowball"),
options: vec![],
owner: None,
comment: None,
}
}
fn cfg(schema: &str, name: &str) -> TsConfiguration {
TsConfiguration {
qname: qname(schema, name),
parser: qname("pg_catalog", "default"),
mappings: vec![],
owner: None,
comment: None,
}
}
#[test]
fn dictionaries_sorted_by_qname() {
let mut cat = Catalog::empty();
cat.ts_dictionaries.push(dict("public", "zzz_dict"));
cat.ts_dictionaries.push(dict("public", "aaa_dict"));
run(&mut cat).unwrap();
assert_eq!(cat.ts_dictionaries[0].qname.name.as_str(), "aaa_dict");
assert_eq!(cat.ts_dictionaries[1].qname.name.as_str(), "zzz_dict");
}
#[test]
fn dictionaries_sorted_across_schemas() {
let mut cat = Catalog::empty();
cat.ts_dictionaries.push(dict("zns", "alpha"));
cat.ts_dictionaries.push(dict("ans", "alpha"));
run(&mut cat).unwrap();
assert_eq!(cat.ts_dictionaries[0].qname.schema.as_str(), "ans");
assert_eq!(cat.ts_dictionaries[1].qname.schema.as_str(), "zns");
}
#[test]
fn rejects_duplicate_dictionary() {
let mut cat = Catalog::empty();
cat.ts_dictionaries.push(dict("public", "english_stem"));
cat.ts_dictionaries.push(dict("public", "english_stem"));
assert!(matches!(
run(&mut cat).unwrap_err(),
IrError::DuplicateTsDictionary(_)
));
}
#[test]
fn dictionary_options_sorted_by_key() {
let mut cat = Catalog::empty();
let mut d = dict("public", "my_dict");
d.options = vec![
("stopwords".to_string(), "english".to_string()),
("language".to_string(), "english".to_string()),
];
cat.ts_dictionaries.push(d);
run(&mut cat).unwrap();
assert_eq!(cat.ts_dictionaries[0].options[0].0, "language");
assert_eq!(cat.ts_dictionaries[0].options[1].0, "stopwords");
}
#[test]
fn configurations_sorted_by_qname() {
let mut cat = Catalog::empty();
cat.ts_configurations.push(cfg("public", "zzz_config"));
cat.ts_configurations.push(cfg("public", "aaa_config"));
run(&mut cat).unwrap();
assert_eq!(cat.ts_configurations[0].qname.name.as_str(), "aaa_config");
assert_eq!(cat.ts_configurations[1].qname.name.as_str(), "zzz_config");
}
#[test]
fn rejects_duplicate_configuration() {
let mut cat = Catalog::empty();
cat.ts_configurations.push(cfg("public", "english_config"));
cat.ts_configurations.push(cfg("public", "english_config"));
assert!(matches!(
run(&mut cat).unwrap_err(),
IrError::DuplicateTsConfiguration(_)
));
}
#[test]
fn configuration_mappings_sorted_by_token_type() {
let mut cat = Catalog::empty();
let mut c = cfg("public", "english_config");
c.mappings = vec![
TsMapping {
token_type: "word".to_string(),
dictionaries: vec![qname("public", "english_stem")],
},
TsMapping {
token_type: "asciiword".to_string(),
dictionaries: vec![qname("public", "english_stem")],
},
TsMapping {
token_type: "numword".to_string(),
dictionaries: vec![qname("pg_catalog", "simple")],
},
];
cat.ts_configurations.push(c);
run(&mut cat).unwrap();
let mappings = &cat.ts_configurations[0].mappings;
assert_eq!(mappings[0].token_type, "asciiword");
assert_eq!(mappings[1].token_type, "numword");
assert_eq!(mappings[2].token_type, "word");
}
#[test]
fn both_kinds_pass_when_clean() {
let mut cat = Catalog::empty();
cat.ts_dictionaries.push(dict("public", "dict_a"));
cat.ts_dictionaries.push(dict("public", "dict_b"));
cat.ts_configurations.push(cfg("public", "cfg_a"));
cat.ts_configurations.push(cfg("public", "cfg_b"));
run(&mut cat).unwrap();
assert_eq!(cat.ts_dictionaries.len(), 2);
assert_eq!(cat.ts_configurations.len(), 2);
}
#[test]
fn dictionary_dup_does_not_affect_configurations() {
let mut cat = Catalog::empty();
cat.ts_dictionaries.push(dict("public", "dup_dict"));
cat.ts_dictionaries.push(dict("public", "dup_dict"));
cat.ts_configurations.push(cfg("public", "cfg_a"));
assert!(matches!(
run(&mut cat).unwrap_err(),
IrError::DuplicateTsDictionary(_)
));
}
}