mool 0.2.0

A source-first Rust database toolkit for typed SQL queries, migrations, and model metadata
Documentation
//! MySQL typed-query dialect renderer.

use crate::placeholders::Dialect;

use super::super::expr::ColumnRef;
use super::super::validate::validate_identifier;
use super::{DialectFeature, DialectRenderer, common};
use crate::QueryError;

pub(super) struct MysqlSpec;

impl DialectRenderer for MysqlSpec {
    fn dialect(&self) -> Dialect {
        Dialect::Mysql
    }

    fn placeholder(&self, _position: usize) -> String {
        "?".to_string()
    }

    fn validate_feature(&self, feature: DialectFeature) -> Result<(), QueryError> {
        match feature {
            DialectFeature::Returning | DialectFeature::Ilike => {
                Err(common::unsupported(self.dialect(), feature.name()))
            }
            DialectFeature::Upsert | DialectFeature::WindowFunctions => Ok(()),
        }
    }

    fn render_upsert(
        &self,
        _conflict: &[ColumnRef],
        update_columns: &[&str],
    ) -> Result<String, QueryError> {
        validate_mysql_update(update_columns)?;
        Ok(format!(
            " ON DUPLICATE KEY UPDATE {}",
            render_mysql_update(update_columns)?
        ))
    }
}

fn validate_mysql_update(update_columns: &[&str]) -> Result<(), QueryError> {
    if update_columns.is_empty() {
        return Err(QueryError::BindError(
            "mysql upsert requires at least one non-conflict bind column".to_string(),
        ));
    }
    Ok(())
}

fn render_mysql_update(update_columns: &[&str]) -> Result<String, QueryError> {
    let mut sql = String::new();
    for (idx, column) in update_columns.iter().enumerate() {
        if idx > 0 {
            sql.push_str(", ");
        }
        validate_identifier(column)?;
        sql.push_str(column);
        sql.push_str(" = VALUES(");
        sql.push_str(column);
        sql.push(')');
    }
    Ok(sql)
}