iridium_core 0.1.12

SQL Server-compatible Rust engine core for Iridium SQL
Documentation
use iridium_core::Engine;

#[test]
fn test_after_insert_trigger() {
    let engine = Engine::new();
    engine
        .exec("CREATE TABLE dbo.Users (Id INT PRIMARY KEY, Name NVARCHAR(100))")
        .unwrap();
    engine
        .exec("CREATE TABLE dbo.Logs (Msg NVARCHAR(100))")
        .unwrap();

    let trigger_sql = "
        CREATE TRIGGER tr_Users_Insert
        ON dbo.Users
        AFTER INSERT
        AS
        BEGIN
            INSERT INTO dbo.Logs (Msg)
            SELECT 'User ' + Name + ' added' FROM INSERTED;
        END
    ";
    engine.exec(trigger_sql).unwrap();

    engine
        .exec("INSERT INTO dbo.Users (Id, Name) VALUES (1, 'Alice'), (2, 'Bob')")
        .unwrap();

    let result = engine
        .query("SELECT Msg FROM dbo.Logs ORDER BY Msg")
        .unwrap();
    assert_eq!(result.rows.len(), 2);
    assert_eq!(result.rows[0][0].to_string_value(), "User Alice added");
    assert_eq!(result.rows[1][0].to_string_value(), "User Bob added");
}

#[test]
fn test_after_update_trigger() {
    let engine = Engine::new();
    engine
        .exec("CREATE TABLE dbo.Products (Id INT PRIMARY KEY, Price DECIMAL(10,2))")
        .unwrap();
    engine
        .exec("CREATE TABLE dbo.Audit (OldPrice DECIMAL(10,2), NewPrice DECIMAL(10,2))")
        .unwrap();

    engine
        .exec("INSERT INTO dbo.Products (Id, Price) VALUES (1, 10.00)")
        .unwrap();

    let trigger_sql = "
        CREATE TRIGGER tr_Products_Update
        ON dbo.Products
        AFTER UPDATE
        AS
        BEGIN
            INSERT INTO dbo.Audit (OldPrice, NewPrice)
            SELECT d.Price, i.Price
            FROM DELETED d
            JOIN INSERTED i ON d.Id = i.Id;
        END
    ";
    engine.exec(trigger_sql).unwrap();

    engine
        .exec("UPDATE dbo.Products SET Price = 12.50 WHERE Id = 1")
        .unwrap();

    let result = engine
        .query("SELECT OldPrice, NewPrice FROM dbo.Audit")
        .unwrap();
    assert_eq!(result.rows.len(), 1);
    assert_eq!(result.rows[0][0].to_string_value(), "10.00");
    assert_eq!(result.rows[0][1].to_string_value(), "12.50");
}

#[test]
fn test_instead_of_insert_trigger() {
    let engine = Engine::new();
    engine
        .exec("CREATE TABLE dbo.Base (Id INT PRIMARY KEY, Val NVARCHAR(100))")
        .unwrap();
    engine
        .exec("CREATE TABLE dbo.Audit (Msg NVARCHAR(100))")
        .unwrap();

    let trigger_sql = "
        CREATE TRIGGER tr_Base_InsteadInsert
        ON dbo.Base
        INSTEAD OF INSERT
        AS
        BEGIN
            INSERT INTO dbo.Audit (Msg)
            SELECT 'Intercepted ' + Val FROM INSERTED;

            -- Manually insert with modification
            INSERT INTO dbo.Base (Id, Val)
            SELECT Id, Val + '_mod' FROM INSERTED;
        END
    ";
    engine.exec(trigger_sql).unwrap();

    engine
        .exec("INSERT INTO dbo.Base (Id, Val) VALUES (1, 'Hello')")
        .unwrap();

    let base = engine.query("SELECT Val FROM dbo.Base").unwrap();
    assert_eq!(base.rows[0][0].to_string_value(), "Hello_mod");

    let audit = engine.query("SELECT Msg FROM dbo.Audit").unwrap();
    assert_eq!(audit.rows[0][0].to_string_value(), "Intercepted Hello");
}

#[test]
fn test_qualified_inserted_reference() {
    let engine = Engine::new();
    engine
        .exec("CREATE TABLE dbo.T (Id INT PRIMARY KEY)")
        .unwrap();
    engine.exec("CREATE TABLE dbo.L (Id INT)").unwrap();

    let trigger_sql = "
        CREATE TRIGGER tr_T
        ON dbo.T
        AFTER INSERT
        AS
        BEGIN
            INSERT INTO dbo.L (Id)
            SELECT Id FROM dbo.INSERTED; -- Qualified reference
        END
    ";
    engine.exec(trigger_sql).unwrap();

    engine.exec("INSERT INTO dbo.T (Id) VALUES (42)").unwrap();
    let res = engine.query("SELECT Id FROM dbo.L").unwrap();
    assert_eq!(res.rows[0][0].to_string_value(), "42");
}

#[test]
fn test_recursive_trigger_prevention() {
    let engine = Engine::new();
    engine
        .exec("CREATE TABLE dbo.Rec (Id INT PRIMARY KEY, Val INT)")
        .unwrap();

    let trigger_sql = "
        CREATE TRIGGER tr_Rec
        ON dbo.Rec
        AFTER UPDATE
        AS
        BEGIN
            UPDATE dbo.Rec SET Val = Val + 1 WHERE Id IN (SELECT Id FROM INSERTED);
        END
    ";
    engine.exec(trigger_sql).unwrap();

    engine
        .exec("INSERT INTO dbo.Rec (Id, Val) VALUES (1, 10)")
        .unwrap();

    let res = engine.exec("UPDATE dbo.Rec SET Val = 20 WHERE Id = 1");
    // Should fail with nesting level error
    assert!(res.is_err());
    assert!(res
        .unwrap_err()
        .to_string()
        .contains("Maximum trigger nesting level"));
}