nodedb-sql 0.6.1

SQL parser, planner, and optimizer for NodeDB
Documentation
// SPDX-License-Identifier: Apache-2.0

//! Engine rules for plain columnar collections.

use crate::engine_rules::*;
use crate::error::{Result, SqlError};
use crate::types::*;

pub struct ColumnarRules;

impl EngineRules for ColumnarRules {
    fn plan_insert(&self, p: InsertParams) -> Result<Vec<SqlPlan>> {
        Ok(vec![SqlPlan::Insert(InsertPlan {
            collection: p.collection,
            engine: EngineType::Columnar,
            route: WriteRoute::ColumnarFamily,
            rows: p.rows,
            volatile_defaults: p.volatile_defaults,
            if_absent: p.if_absent,
            column_schema: p.column_schema,
            primary_key: p.primary_key,
        })])
    }

    /// `UPSERT` / `INSERT ... ON CONFLICT (pk) DO UPDATE` on columnar.
    /// Duplicate PK tombstones the prior row via the segment's delete
    /// bitmap; the new row (or its merged form, when `on_conflict_updates`
    /// is non-empty) is appended. Sort-key semantics are unchanged.
    fn plan_insert_select(&self, p: InsertSelectParams) -> Result<Vec<SqlPlan>> {
        Err(SqlError::Unsupported {
            detail: format!(
                "INSERT ... SELECT into columnar collection '{}' is not supported; \
                 the target must be a document collection",
                p.collection
            ),
        })
    }

    fn plan_upsert(&self, p: UpsertParams) -> Result<Vec<SqlPlan>> {
        Ok(vec![SqlPlan::Upsert(UpsertPlan {
            collection: p.collection,
            engine: EngineType::Columnar,
            route: WriteRoute::ColumnarFamily,
            rows: p.rows,
            volatile_defaults: p.volatile_defaults,
            on_conflict_updates: p.on_conflict_updates,
            column_schema: p.column_schema,
            primary_key: p.primary_key,
        })])
    }

    fn plan_scan(&self, p: ScanParams) -> Result<SqlPlan> {
        if p.temporal.is_temporal() && !p.bitemporal {
            return Err(SqlError::Unsupported {
                detail: format!(
                    "FOR SYSTEM_TIME / FOR VALID_TIME requires a bitemporal \
                     collection; '{}' was not created WITH bitemporal = true",
                    p.collection
                ),
            });
        }
        Ok(SqlPlan::Scan {
            collection: p.collection,
            alias: p.alias,
            engine: EngineType::Columnar,
            filters: p.filters,
            projection: p.projection,
            sort_keys: p.sort_keys,
            limit: p.limit,
            offset: p.offset,
            distinct: p.distinct,
            window_functions: p.window_functions,
            temporal: p.temporal,
        })
    }

    fn plan_point_get(&self, p: PointGetParams) -> Result<SqlPlan> {
        Ok(SqlPlan::PointGet {
            collection: p.collection,
            alias: p.alias,
            engine: EngineType::Columnar,
            key_column: p.key_column,
            key_value: p.key_value,
            projection: p.projection,
        })
    }

    fn plan_update(&self, p: UpdateParams) -> Result<Vec<SqlPlan>> {
        Ok(vec![SqlPlan::Update {
            collection: p.collection,
            engine: EngineType::Columnar,
            assignments: p.assignments,
            filters: p.filters,
            target_keys: p.target_keys,
            returning: p.returning,
        }])
    }

    /// `UPDATE ... FROM` writes through the document join-update, which
    /// columnar storage does not take.
    fn plan_update_from(&self, p: UpdateFromParams) -> Result<Vec<SqlPlan>> {
        Err(SqlError::Unsupported {
            detail: format!(
                "UPDATE ... FROM is not supported on columnar collection '{}'; \
                 the target must be a document collection",
                p.collection
            ),
        })
    }

    fn plan_delete(&self, p: DeleteParams) -> Result<Vec<SqlPlan>> {
        Ok(vec![SqlPlan::Delete {
            collection: p.collection,
            engine: EngineType::Columnar,
            filters: p.filters,
            target_keys: p.target_keys,
        }])
    }

    fn plan_truncate(&self, p: TruncateParams) -> Result<Vec<SqlPlan>> {
        Ok(vec![SqlPlan::Truncate {
            collection: p.collection,
            engine: EngineType::Columnar,
            restart_identity: p.restart_identity,
        }])
    }

    fn plan_aggregate(&self, p: AggregateParams) -> Result<SqlPlan> {
        if p.temporal.is_temporal() && !p.bitemporal {
            return Err(SqlError::Unsupported {
                detail: format!(
                    "FOR SYSTEM_TIME / FOR VALID_TIME requires a bitemporal \
                     collection; '{}' was not created WITH bitemporal = true",
                    p.collection
                ),
            });
        }
        let base_scan = SqlPlan::Scan {
            collection: p.collection,
            alias: p.alias,
            engine: EngineType::Columnar,
            filters: p.filters,
            projection: Vec::new(),
            sort_keys: Vec::new(),
            limit: None,
            offset: 0,
            distinct: false,
            window_functions: Vec::new(),
            temporal: p.temporal,
        };
        Ok(SqlPlan::Aggregate {
            input: Box::new(base_scan),
            group_by: p.group_by,
            group_by_aliases: Vec::new(),
            output_order: Vec::new(),
            aggregates: p.aggregates,
            having: p.having,
            limit: p.limit,
            grouping_sets: p.grouping_sets,
            sort_keys: Vec::new(),
        })
    }

    fn plan_merge(&self, p: MergeParams) -> Result<Vec<SqlPlan>> {
        Err(SqlError::Unsupported {
            detail: format!(
                "MERGE is not supported on columnar collection '{}'",
                p.collection
            ),
        })
    }

    /// Every column is declared at creation and encoded per column.
    fn accepts_undeclared_columns(&self) -> bool {
        false
    }
}