use super::ViewColumns;
use super::derive::{Derivation, uncascaded_tables};
use super::indexes::{create_embed_lookup_indexes, key_index};
use super::relations::relation_oid;
use crate::catalog::plan::TviewPlan;
use crate::ddl::uncascaded::{Declarations, Uncascaded};
use crate::error::TViewResult;
use crate::lineage::Identity;
use pgrx::pg_sys;
pub(crate) struct Registration<'a> {
pub(crate) entity: &'a str,
pub(crate) schema: &'a str,
pub(crate) view_oid: pg_sys::Oid,
pub(crate) definition: &'a str,
pub(crate) columns: &'a ViewColumns,
pub(crate) group_keys: Option<&'a crate::ddl::aggregate::GroupKeys>,
pub(crate) derivation: &'a Derivation,
}
impl Registration<'_> {
pub(crate) fn write(&self, declarations: Declarations, replace: bool) -> TViewResult<()> {
let tview = format!("tv_{}", self.entity);
let table_oid = relation_oid(self.schema, &tview)?;
let qualified = crate::utils::qualified_relname_from_oid(table_oid)?;
let lineage = &self.derivation.lineage;
crate::ddl::uncascaded::report_functions(
&qualified,
&self.derivation.undeclared_functions,
declarations.policy,
);
crate::ddl::uncascaded::report_time(&qualified, &lineage.time_reads, &declarations)?;
crate::ddl::uncascaded::check_declared(&qualified, &declarations, lineage)?;
let uncascaded = Uncascaded {
tables: uncascaded_tables(lineage),
declarations,
time_dependent: !lineage.time_reads.is_empty(),
};
crate::ddl::uncascaded::report(&qualified, &uncascaded);
let plan = &self.derivation.plan;
super::derive::aggregate_embeds(lineage, self.entity)?;
let lookups = embed_lookups(plan);
let mut indexes = if replace && crate::catalog::indexes::unrecorded(table_oid)? {
adopted_indexes(table_oid, &tview, self.columns, &lookups)?
} else {
Vec::new()
};
indexes.extend(create_embed_lookup_indexes(
&lookups,
self.columns,
&tview,
self.schema,
)?);
if let Some(index) = key_index(&tview, self.columns, &lineage.identity.name) {
indexes.extend(index.create(self.schema, &tview)?);
}
MetaRow {
entity: self.entity,
view_oid: self.view_oid,
table_oid,
definition: self.definition,
plan,
group_keys: self.group_keys,
uncascaded: &uncascaded,
identity: &lineage.identity,
}
.write(replace)?;
crate::catalog::indexes::record(table_oid, &indexes)
}
}
struct MetaRow<'a> {
pub(crate) entity: &'a str,
pub(crate) view_oid: pg_sys::Oid,
pub(crate) table_oid: pg_sys::Oid,
pub(crate) definition: &'a str,
pub(crate) plan: &'a TviewPlan,
pub(crate) group_keys: Option<&'a crate::ddl::aggregate::GroupKeys>,
pub(crate) uncascaded: &'a Uncascaded,
pub(crate) identity: &'a Identity,
}
impl MetaRow<'_> {
fn write(&self, replace: bool) -> TViewResult<()> {
let declarations = &self.uncascaded.declarations;
let group_keys = self.group_keys.map(serde_json::to_value).transpose()?;
crate::catalog::queries::write_registration(
crate::catalog::queries::Registration {
entity: self.entity,
view_oid: self.view_oid,
table_oid: self.table_oid,
definition: self.definition,
plan: serde_json::to_value(self.plan)?,
group_keys,
uncascaded_oids: self.uncascaded.oids(),
declared: declarations.catalog_row(self.uncascaded.time_refresh()),
identity_kind: self.identity.kind.name(),
identity_column: self.identity.name.as_str(),
identity_type: pg_sys::Oid::from(self.identity.type_oid),
time_dependent: self.uncascaded.time_dependent,
},
replace,
)?;
crate::catalog::EntityDepGraph::load()?;
Ok(())
}
}
fn embed_lookups(plan: &TviewPlan) -> Vec<String> {
plan.embeds
.iter()
.flat_map(|e| e.lookups.iter().cloned())
.chain(plan.references.iter().map(|r| r.column.clone()))
.collect()
}
fn adopted_indexes(
table: pg_sys::Oid,
tview_name: &str,
schema: &ViewColumns,
lookups: &[String],
) -> TViewResult<Vec<String>> {
let mut adopted = Vec::new();
for index in super::managed_indexes(tview_name, schema, lookups) {
if index.exists_on(table)? {
adopted.push(index.name);
} else if crate::utils::spi::one::<bool>(
"SELECT EXISTS (SELECT 1 FROM pg_catalog.pg_index i \
JOIN pg_catalog.pg_class c ON c.oid = i.indexrelid \
WHERE i.indrelid = $1 AND c.relname = $2)",
&[
crate::utils::spi::oid(table),
crate::utils::spi::text(index.name.as_str()),
],
)? == Some(true)
{
pgrx::warning!(
"index {} on {tview_name} is not the index pg_tviews creates under that name, \
which is reserved: rename it (ALTER INDEX … RENAME TO), or a dump of this \
database will not restore it",
index.name
);
}
}
Ok(adopted)
}