Skip to main content

cqlite_core/cql/
mod.rs

1//! # CQL Text Parsing Module
2//!
3//! Parses CQL (Cassandra Query Language) query strings into Abstract Syntax Trees.
4//!
5//! This is one of four parsing subsystems in cqlite-core:
6//!
7//! | Module | Purpose |
8//! |--------|---------|
9//! | **`cql/`** | Full CQL text → AST parsing (this module) |
10//! | `parser/` | SSTable binary format parsing |
11//! | `schema/cql_parser.rs` | CREATE TABLE → TableSchema |
12//! | `query/parser.rs` | Lightweight DML → ParsedQuery |
13//!
14//! See `docs/architecture/parser-overview.md` for the complete architecture overview.
15//!
16//! ## Backend
17//!
18//! - **Nom**: High-performance parser combinator implementation (the only
19//!   built-in backend).
20//!
21//! ## Usage
22//!
23//! ```rust,ignore
24//! use cqlite_core::cql::{create_default_parser, CqlStatement};
25//!
26//! let parser = create_default_parser()?;
27//! let statement = parser.parse("SELECT * FROM users").await?;
28//! ```
29
30pub mod ast;
31pub mod config;
32pub mod error;
33pub mod traits;
34pub mod visitor;
35
36pub mod nom_backend;
37
38#[cfg(feature = "write-support")]
39pub mod mutation_parser;
40
41pub mod factory;
42pub mod schema_integration;
43
44pub use ast::{
45    CqlAssignment, CqlAssignmentOperator, CqlBinaryOperator, CqlColumnDef, CqlCreateTable,
46    CqlDataType, CqlDelete, CqlDropTable, CqlExpression, CqlIdentifier, CqlInsert, CqlInsertValues,
47    CqlLiteral, CqlOrderBy, CqlPrimaryKey, CqlSelect, CqlSelectItem, CqlSortDirection,
48    CqlStatement, CqlTable, CqlTableOptions, CqlUnaryOperator, CqlUpdate, CqlUsing,
49};
50pub use config::{
51    MemorySettings, ParserBackend, ParserConfig, ParserFeature as ConfigFeature,
52    PerformanceSettings, SecuritySettings,
53};
54pub use error::{ErrorCategory as ParserErrorCategory, ErrorSeverity, ParserError, ParserWarning};
55pub use factory::{register_global_factory, ParserFactory, ParserRegistry, UseCase};
56pub use schema_integration::{
57    extract_table_name_enhanced, parse_cql_schema_enhanced, parse_cql_schema_fast,
58    parse_cql_schema_simple, parse_cql_schema_strict, parse_cql_schemas_batch,
59    table_name_matches_enhanced, validate_cql_schema_syntax, SchemaParserConfig,
60};
61pub use traits::{
62    CqlParser, CqlParserFactory, CqlVisitor, FactoryInfo, ParserBackendInfo, ParserFeature,
63    PerformanceCharacteristics, SourcePosition,
64};
65pub use visitor::{
66    DefaultVisitor, IdentifierCollector, SchemaBuilderVisitor, SemanticValidator,
67    TypeCollectorVisitor, ValidationVisitor,
68};
69
70#[allow(deprecated)]
71pub use schema_integration::parse_cql_schema_compat;
72
73pub use nom_backend::NomParser;
74
75pub use crate::error::Result as CqlResult;
76
77use crate::error::Result;
78use std::sync::Arc;
79
80/// Convenience function to create a default parser.
81pub fn create_default_parser() -> Result<Arc<dyn CqlParser + Send + Sync>> {
82    ParserFactory::create_default()
83}
84
85/// Detect whether a CQL statement is a Data Manipulation Language (DML) statement.
86///
87/// Returns `true` for statements that write data — `INSERT`, `UPDATE`, `DELETE`, or
88/// `BEGIN` (which begins a `BATCH` block). Detection is done by case-insensitive
89/// prefix matching on the trimmed input, so it is suitable for routing read vs.
90/// write paths *before* full parsing.
91///
92/// # Examples
93///
94/// ```
95/// use cqlite_core::cql::is_dml_statement;
96///
97/// assert!(is_dml_statement("INSERT INTO t (id) VALUES (1)"));
98/// assert!(is_dml_statement("insert into t (id) values (1)"));
99/// assert!(is_dml_statement("UPDATE t SET x = 1 WHERE id = 1"));
100/// assert!(is_dml_statement("DELETE FROM t WHERE id = 1"));
101/// assert!(is_dml_statement("BEGIN BATCH INSERT INTO t (id) VALUES (1) APPLY BATCH"));
102/// assert!(!is_dml_statement("SELECT * FROM t"));
103/// assert!(!is_dml_statement("  CREATE TABLE t (id int PRIMARY KEY)"));
104/// ```
105pub fn is_dml_statement(s: &str) -> bool {
106    let upper = s.trim().to_uppercase();
107    upper.starts_with("INSERT")
108        || upper.starts_with("UPDATE")
109        || upper.starts_with("DELETE")
110        || upper.starts_with("BEGIN")
111}