Skip to content

HeliosDB Nano Trigger Parsing Implementation

HeliosDB Nano Trigger Parsing Implementation

Summary

Successfully implemented SQL parser support for CREATE TRIGGER and DROP TRIGGER statements in HeliosDB Nano.

Implementation Files

Modified Files

  • /home/claude/HeliosDB Nano/src/sql/planner.rs - Added trigger parsing and conversion to LogicalPlan

Implementation Details

1. Parser Integration

The implementation leverages sqlparser-rs v0.53.0’s native support for CREATE TRIGGER and DROP TRIGGER statements. No modifications to src/sql/parser.rs were needed as sqlparser already handles the SQL parsing.

2. Planner Conversion

Added two methods to Planner:

  • create_trigger_to_plan() - Converts sqlparser’s CREATE TRIGGER AST to LogicalPlan::CreateTrigger
  • drop_trigger_to_plan() - Converts sqlparser’s DROP TRIGGER AST to LogicalPlan::DropTrigger

3. Supported Features

CREATE TRIGGER supports:

  • ✓ Trigger timing: BEFORE, AFTER, INSTEAD OF
  • ✓ Events: INSERT, UPDATE, DELETE
  • ✓ UPDATE OF specific columns (e.g., UPDATE OF price, discount)
  • ✓ FOR EACH ROW / FOR EACH STATEMENT
  • ✓ Multiple events (e.g., INSERT OR UPDATE OR DELETE)
  • ✓ OR REPLACE clause
  • ✓ Table specification

DROP TRIGGER supports:

  • ✓ Basic DROP TRIGGER
  • ✓ IF EXISTS clause
  • ✓ Table name specification
  • ✓ CASCADE/RESTRICT options (parsed but not enforced yet)

Unsupported features (with proper error messages):

  • ✗ TRUNCATE trigger event
  • ✗ CONSTRAINT triggers
  • ✗ REFERENCING clause
  • ✗ Referenced table names
  • ✗ Trigger characteristics (DEFERRABLE, INITIALLY DEFERRED, etc.)

Test Results

All parsing tests passed successfully:

Test 1: AFTER INSERT trigger

CREATE TRIGGER audit_insert AFTER INSERT ON users FOR EACH ROW EXECUTE FUNCTION audit_log()

✓ Name: audit_insert ✓ Table: users ✓ Timing: After ✓ Events: [Insert] ✓ For Each: Row

Test 2: BEFORE UPDATE trigger

CREATE TRIGGER update_timestamp BEFORE UPDATE ON products FOR EACH ROW EXECUTE FUNCTION update_modified_at()

✓ Name: update_timestamp ✓ Table: products ✓ Timing: Before ✓ Events: [Update(None)] ✓ For Each: Row

Test 3: INSTEAD OF DELETE trigger

CREATE TRIGGER prevent_delete INSTEAD OF DELETE ON users FOR EACH ROW EXECUTE FUNCTION log_delete_attempt()

✓ Name: prevent_delete ✓ Table: users ✓ Timing: InsteadOf ✓ Events: [Delete] ✓ For Each: Row

Test 4: UPDATE OF specific columns

CREATE TRIGGER track_price_change AFTER UPDATE OF price, discount ON products FOR EACH ROW EXECUTE FUNCTION log_price_change()

✓ Name: track_price_change ✓ Table: products ✓ Timing: After ✓ Events: [Update(Some(["price", "discount"]))] ✓ For Each: Row

Test 5: FOR EACH STATEMENT

CREATE TRIGGER bulk_audit AFTER INSERT ON orders FOR EACH STATEMENT EXECUTE FUNCTION audit_bulk_insert()

✓ Name: bulk_audit ✓ Table: orders ✓ Timing: After ✓ Events: [Insert] ✓ For Each: Statement

Test 6: OR REPLACE

CREATE OR REPLACE TRIGGER replace_audit AFTER INSERT ON logs FOR EACH ROW EXECUTE FUNCTION audit_logs()

✓ Name: replace_audit ✓ Table: logs ✓ Timing: After ✓ Events: [Insert] ✓ For Each: Row ✓ OR REPLACE: true

Test 7: Multiple events

CREATE TRIGGER multi_event AFTER INSERT OR UPDATE OR DELETE ON items FOR EACH ROW EXECUTE FUNCTION track_changes()

✓ Name: multi_event ✓ Table: items ✓ Timing: After ✓ Events: [Insert, Update(None), Delete] ✓ For Each: Row

Test 8: DROP TRIGGER

DROP TRIGGER audit_insert ON users

✓ Name: audit_insert ✓ Table: Some("users") ✓ IF EXISTS: false

Test 9: DROP TRIGGER IF EXISTS

DROP TRIGGER IF EXISTS old_trigger ON products

✓ Name: old_trigger ✓ Table: Some("products") ✓ IF EXISTS: true

Test 10: DROP TRIGGER CASCADE

DROP TRIGGER legacy_trigger ON orders CASCADE

✓ Name: legacy_trigger ✓ Table: Some("orders") ✓ IF EXISTS: false

Architecture

Data Flow

SQL String
sqlparser::Parser (sqlparser-rs)
sqlparser::ast::Statement::CreateTrigger / DropTrigger
Planner::statement_to_plan()
Planner::create_trigger_to_plan() / drop_trigger_to_plan()
LogicalPlan::CreateTrigger / DropTrigger

Conversion Details

CREATE TRIGGER conversion:

  1. Extract trigger name from ObjectName
  2. Convert TriggerPeriod → TriggerTiming
  3. Convert Vec → Vec
  4. Convert TriggerObject → TriggerFor
  5. Convert optional condition Expr → Option<Box>
  6. Validate unsupported features and return errors
  7. Construct LogicalPlan::CreateTrigger

DROP TRIGGER conversion:

  1. Extract trigger name from ObjectName
  2. Extract table name from ObjectName
  3. Parse if_exists flag
  4. Handle CASCADE/RESTRICT options (currently only parsed)
  5. Construct LogicalPlan::DropTrigger

Limitations and Future Work

Current Limitations

  1. Trigger Body: The trigger body (function/procedure) is not yet parsed into executable statements. Currently stored as an empty Vec. This is intentional as the body execution will be implemented in a later phase.

  2. WHEN Clause: While the parser accepts WHEN clauses, they are converted to LogicalExpr but not yet validated or executed.

  3. Function Resolution: The EXECUTE FUNCTION/PROCEDURE clause is parsed but the function name is not yet resolved or stored in the LogicalPlan. This will be added when trigger execution is implemented.

Next Steps for Full Trigger Support

  1. Executor Integration: Update /home/claude/HeliosDB Nano/src/executor/mod.rs to handle CreateTrigger and DropTrigger operations
  2. Catalog Storage: Store trigger definitions in the catalog
  3. Trigger Execution: Implement trigger firing logic in DML operations (INSERT, UPDATE, DELETE)
  4. Function/Procedure Support: Add support for trigger functions and stored procedures
  5. OLD/NEW Row Context: Implement row-level trigger context variables

Error Handling

The implementation includes comprehensive error handling:

// Example: TRUNCATE event rejection
SqlTriggerEvent::Truncate => {
Err(Error::query_execution(
"TRUNCATE trigger event not yet supported"
))
}
// Example: CONSTRAINT trigger rejection
if is_constraint {
return Err(Error::query_execution(
"CONSTRAINT triggers not yet supported"
));
}

Verification

To verify the implementation:

Terminal window
# Compile the library
cargo build --lib
# Run the manual test
rustc --edition 2021 tests/test_trigger_manual.rs \
-L target/debug/deps \
--extern heliosdb_nano=target/debug/libheliosdb_nano.rlib \
-o /tmp/test_trigger_manual
/tmp/test_trigger_manual

All 10 test cases passed successfully with proper LogicalPlan construction.

Conclusion

The CREATE TRIGGER and DROP TRIGGER parsing implementation is complete and working. The parser successfully:

  1. ✓ Parses all standard CREATE TRIGGER syntax variations
  2. ✓ Handles timing options (BEFORE, AFTER, INSTEAD OF)
  3. ✓ Supports all event types (INSERT, UPDATE, DELETE) with column specifications
  4. ✓ Handles FOR EACH ROW/STATEMENT options
  5. ✓ Supports OR REPLACE clause
  6. ✓ Parses DROP TRIGGER with IF EXISTS and CASCADE options
  7. ✓ Converts parsed statements to appropriate LogicalPlan variants
  8. ✓ Provides clear error messages for unsupported features

The implementation is ready for integration with the executor and catalog storage systems.