use super::build_keys::IndexBuildKeys;
use crate::mutation::constraints::{
context::{ConstraintContext, MutationRead},
index_keys::{index_key_values, index_predicate_accepts, IndexExpressionContext},
};
use crate::query::runtime::QueryMemorySettings;
use uqa_core::Value;
use uqa_sql::{
ast::{Expr, IndexKey},
ColumnType, SQLError,
};
pub trait IndexKeyDescription {
fn describe_index_key(
&self,
table: &str,
keys: &[IndexKey],
key_types: &[ColumnType],
values: &[Value],
) -> Result<Option<String>, SQLError>;
}
#[derive(Clone, Copy)]
pub struct UniqueBuildContext<'a> {
pub reads: &'a dyn MutationRead,
pub expressions: IndexExpressionContext<'a>,
pub memory: &'a dyn QueryMemorySettings,
pub description: &'a dyn IndexKeyDescription,
}
impl<'a> UniqueBuildContext<'a> {
pub fn of(constraints: &'a ConstraintContext<'a>) -> Self {
Self {
reads: constraints.reads,
expressions: constraints.index_expressions(),
memory: constraints.memory,
description: constraints,
}
}
}
pub struct UniqueIndexBuild<'a> {
pub table: &'a str,
pub name: &'a str,
pub keys: &'a [IndexKey],
pub key_types: &'a [ColumnType],
pub predicate: Option<&'a Expr>,
pub nulls_not_distinct: bool,
}
pub fn duplicated_index_key(name: &str, key: Option<String>) -> SQLError {
SQLError::Diagnostic {
sqlstate: "23505".into(),
message: format!("could not create unique index \"{name}\""),
detail: Some(key.map_or_else(
|| "Duplicate keys exist.".into(),
|key| format!("Key {key} is duplicated."),
)),
hint: None,
}
}
pub fn validate_unique_index_build(
context: UniqueBuildContext<'_>,
build: &UniqueIndexBuild<'_>,
) -> Result<(), SQLError> {
let mut keys = IndexBuildKeys::new(build.keys.len(), context.memory.work_mem_bytes()?);
for id in context.reads.live_table_doc_ids(build.table)? {
let document = context
.reads
.get_document(build.table, id)?
.ok_or_else(|| SQLError::Internal("index build lost a visible row".into()))?;
if !index_predicate_accepts(context.expressions, build.table, build.predicate, &document)? {
continue;
}
keys.push(index_key_values(
context.expressions,
build.table,
build.keys,
&document,
)?)?;
}
let Some(values) = keys.first_duplicate(true, build.nulls_not_distinct)? else {
return Ok(());
};
Err(duplicated_index_key(
build.name,
context.description.describe_index_key(
build.table,
build.keys,
build.key_types,
&values,
)?,
))
}