use rupg_common::{Error, Object, Result, SqlState};
use super::{Catalog, DepKind, ObjRef, PG_NAMESPACE, PG_PROC};
pub const PG_TS_TEMPLATE: u32 = 3764;
pub const PG_TS_DICT: u32 = 3600;
pub const PG_TS_PARSER: u32 = 3601;
pub const PG_TS_CONFIG: u32 = 3602;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TsTemplate {
pub oid: u32,
pub name: String,
pub namespace: u32,
pub init: u32,
pub lexize: u32,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TsParser {
pub oid: u32,
pub name: String,
pub namespace: u32,
pub start: u32,
pub token: u32,
pub end: u32,
pub headline: u32,
pub lextype: u32,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TsDictionary {
pub oid: u32,
pub name: String,
pub namespace: u32,
pub owner: u32,
pub template: u32,
pub options: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TsConfig {
pub oid: u32,
pub name: String,
pub namespace: u32,
pub owner: u32,
pub parser: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TsMapping {
pub config: u32,
pub token: i32,
pub seqno: i32,
pub dict: u32,
}
fn duplicate(catalog: &str, prefix: &str, name: &str, namespace: u32) -> Error {
Error::new(
SqlState::UNIQUE_VIOLATION,
format!("duplicate key value violates unique constraint \"{catalog}_{prefix}name_index\""),
)
.with_detail(format!(
"Key ({prefix}name, {prefix}namespace)=({name}, {namespace}) already exists."
))
.with_object(Object::Schema, "pg_catalog")
.with_object(Object::Table, catalog)
.with_object(Object::Constraint, format!("{catalog}_{prefix}name_index"))
}
impl Catalog {
pub fn create_ts_template(&mut self, mut new: TsTemplate) -> Result<u32> {
let oid = self.new_oid();
if self.ts_template_by_name(new.namespace, &new.name).is_some() {
return Err(duplicate("pg_ts_template", "tmpl", &new.name, new.namespace));
}
new.oid = oid;
let mut refs =
vec![ObjRef::new(PG_NAMESPACE, new.namespace), ObjRef::new(PG_PROC, new.lexize)];
if new.init != 0 {
refs.push(ObjRef::new(PG_PROC, new.init));
}
self.depend_all(ObjRef::new(PG_TS_TEMPLATE, oid), refs);
self.ts_templates.insert(oid, new);
Ok(oid)
}
pub fn create_ts_parser(&mut self, mut new: TsParser) -> Result<u32> {
let oid = self.new_oid();
if self.ts_parser_by_name(new.namespace, &new.name).is_some() {
return Err(duplicate("pg_ts_parser", "prs", &new.name, new.namespace));
}
new.oid = oid;
let mut refs = vec![ObjRef::new(PG_NAMESPACE, new.namespace)];
for function in [new.start, new.token, new.end, new.lextype, new.headline] {
if function != 0 {
refs.push(ObjRef::new(PG_PROC, function));
}
}
self.depend_all(ObjRef::new(PG_TS_PARSER, oid), refs);
self.ts_parsers.insert(oid, new);
Ok(oid)
}
pub fn create_ts_dictionary(&mut self, mut new: TsDictionary) -> Result<u32> {
let oid = self.new_oid();
if self.ts_dictionary_by_name(new.namespace, &new.name).is_some() {
return Err(duplicate("pg_ts_dict", "dict", &new.name, new.namespace));
}
new.oid = oid;
let refs = vec![
ObjRef::new(PG_NAMESPACE, new.namespace),
ObjRef::new(PG_TS_TEMPLATE, new.template),
];
self.depend_all(ObjRef::new(PG_TS_DICT, oid), refs);
self.ts_dictionaries.insert(oid, new);
Ok(oid)
}
pub fn create_ts_config(&mut self, mut new: TsConfig, copy: &[TsMapping]) -> Result<u32> {
let oid = self.new_oid();
if self.ts_config_by_name(new.namespace, &new.name).is_some() {
return Err(duplicate("pg_ts_config", "cfg", &new.name, new.namespace));
}
new.oid = oid;
self.ts_configs.insert(oid, new);
self.ts_mappings.extend(copy.iter().map(|m| TsMapping { config: oid, ..*m }));
self.ts_config_depends(oid);
Ok(oid)
}
fn ts_config_depends(&mut self, oid: u32) {
let Some(config) = self.ts_configs.get(&oid) else { return };
let me = ObjRef::new(PG_TS_CONFIG, oid);
let mut refs = vec![
ObjRef::new(PG_NAMESPACE, config.namespace),
ObjRef::new(PG_TS_PARSER, config.parser),
];
refs.extend(
self.ts_mappings
.iter()
.filter(|m| m.config == oid)
.map(|m| ObjRef::new(PG_TS_DICT, m.dict)),
);
self.depends.retain(|d| d.object != me || d.kind == DepKind::Extension);
self.depend_all(me, refs);
}
pub(crate) fn depend_all(&mut self, me: ObjRef, mut refs: Vec<ObjRef>) {
refs.sort_unstable_by(|a, b| {
b.oid.cmp(&a.oid).then(a.class.cmp(&b.class)).then(a.sub.cmp(&b.sub))
});
refs.dedup();
for referenced in refs {
self.depend(me, referenced, DepKind::Normal);
}
}
pub fn add_ts_mapping(&mut self, config: u32, tokens: &[i32], dicts: &[u32], replace: bool) {
if replace {
self.ts_mappings.retain(|m| m.config != config || !tokens.contains(&m.token));
}
for &token in tokens {
for (seqno, &dict) in (1..).zip(dicts) {
self.ts_mappings.push(TsMapping { config, token, seqno, dict });
}
}
self.ts_config_depends(config);
}
pub fn replace_ts_dictionary(&mut self, config: u32, tokens: &[i32], old: u32, new: u32) {
for m in &mut self.ts_mappings {
if m.config == config
&& m.dict == old
&& (tokens.is_empty() || tokens.contains(&m.token))
{
m.dict = new;
}
}
self.ts_config_depends(config);
}
pub fn drop_ts_mapping(&mut self, config: u32, token: i32) -> bool {
let before = self.ts_mappings.len();
self.ts_mappings.retain(|m| m.config != config || m.token != token);
self.ts_mappings.len() != before
}
pub fn update_ts_config_depends(&mut self, config: u32) {
self.ts_config_depends(config);
}
pub fn ts_templates(&self) -> impl Iterator<Item = &TsTemplate> {
self.ts_templates.values()
}
pub fn ts_template(&self, oid: u32) -> Option<&TsTemplate> {
self.ts_templates.get(&oid)
}
pub fn ts_template_by_name(&self, namespace: u32, name: &str) -> Option<&TsTemplate> {
self.ts_templates.values().find(|t| t.namespace == namespace && t.name == name)
}
pub fn ts_parsers(&self) -> impl Iterator<Item = &TsParser> {
self.ts_parsers.values()
}
pub fn ts_parser(&self, oid: u32) -> Option<&TsParser> {
self.ts_parsers.get(&oid)
}
pub fn ts_parser_by_name(&self, namespace: u32, name: &str) -> Option<&TsParser> {
self.ts_parsers.values().find(|p| p.namespace == namespace && p.name == name)
}
pub fn ts_dictionaries(&self) -> impl Iterator<Item = &TsDictionary> {
self.ts_dictionaries.values()
}
pub fn ts_dictionary(&self, oid: u32) -> Option<&TsDictionary> {
self.ts_dictionaries.get(&oid)
}
pub fn ts_dictionary_by_name(&self, namespace: u32, name: &str) -> Option<&TsDictionary> {
self.ts_dictionaries.values().find(|d| d.namespace == namespace && d.name == name)
}
pub fn ts_configs(&self) -> impl Iterator<Item = &TsConfig> {
self.ts_configs.values()
}
pub fn ts_config(&self, oid: u32) -> Option<&TsConfig> {
self.ts_configs.get(&oid)
}
pub fn ts_config_by_name(&self, namespace: u32, name: &str) -> Option<&TsConfig> {
self.ts_configs.values().find(|c| c.namespace == namespace && c.name == name)
}
pub fn ts_mappings(&self) -> &[TsMapping] {
&self.ts_mappings
}
}