oxisql-parse 0.1.0

SQL parsing utilities for OxiSQL — thin sqlparser facade
Documentation

oxisql-parse — SQL parsing utilities for OxiSQL

Crates.io License

oxisql-parse provides SQL parsing, normalization, query building, logical planning, optimization, and an LRU parse cache. It re-exports the most commonly needed sqlparser AST types and wraps them with OxiSQL-idiomatic APIs.

Installation

[dependencies]
oxisql-parse = "0.1.0"

Quick Start

use oxisql_parse::{parse, parse_one, is_read_only, QueryBuilder, ParseCache, SqlDialect};

// Parse multiple statements
let stmts = parse("SELECT 1; SELECT 2").unwrap();
assert_eq!(stmts.len(), 2);

// Parse a single statement
let stmt = parse_one("SELECT 42").unwrap();
assert!(is_read_only(&stmt));

// Fluent query builder
let sql = QueryBuilder::select(&["id", "name", "email"])
    .from("users")
    .join("orders", "users.id = orders.user_id")
    .where_clause("users.active = TRUE")
    .order_by("users.name", true)
    .limit(10)
    .offset(20)
    .build()
    .expect("valid query");

// LRU parse cache
let cache = ParseCache::new(32);
let stmts = cache.parse("SELECT 1", SqlDialect::Generic).unwrap();
assert_eq!(cache.len(), 1);

API Overview

Parsing functions

Function Description
parse(sql) Parse SQL using the generic dialect, return Vec<Statement>
parse_one(sql) Parse exactly one statement, error if 0 or 2+
parse_with_dialect(sql, dialect) Parse with a specific SqlDialect
parse_one_with_dialect(sql, dialect) Single-statement parse with dialect
parse_postgres(sql) Shorthand for PostgreSQL dialect
parse_mysql(sql) Shorthand for MySQL dialect
parse_sqlite(sql) Shorthand for SQLite dialect
format(stmt) Serialize a parsed Statement back to SQL text

SqlDialect enum

Generic, Postgres, MySQL, SQLite — selects the sqlparser dialect for parsing.

Analysis utilities (from analysis module)

Function Description
is_read_only(stmt) True if statement is a SELECT (no side effects)
normalize(sql) Normalize whitespace and casing
extract_tables(stmt) Extract referenced table names
extract_columns(stmt) Extract referenced column names
count_params(sql) Count $N positional parameters in SQL text

QueryBuilder

Fluent builder for SELECT statements. All methods return Self for chaining.

Method Description
QueryBuilder::select(columns) Start a SELECT with given columns
QueryBuilder::select_all() Start a SELECT *
.distinct() Add DISTINCT
.from(table) Set the FROM table
.join(table, on) Add INNER JOIN
.left_join(table, on) Add LEFT JOIN
.right_join(table, on) Add RIGHT JOIN
.where_clause(condition) Add a WHERE condition (multiple → AND)
.group_by(expr) Add a GROUP BY expression
.having(condition) Set the HAVING clause
.order_by(expr, ascending) Add ORDER BY (true=ASC, false=DESC)
.limit(n) Set LIMIT
.offset(n) Set OFFSET
.build() Produce the SQL string
.build_and_parse() Build and immediately validate with sqlparser
.build_ref() Build without consuming self

Static DML helpers (return String directly):

QueryBuilder::insert("users", &["id", "name"], &["1", "'Alice'"])
// → "INSERT INTO users (id, name) VALUES (1, 'Alice')"

QueryBuilder::update("users", &[("name", "'Bob'")], Some("id = 42"))
// → "UPDATE users SET name = 'Bob' WHERE id = 42"

QueryBuilder::delete("users", Some("id = 99"))
// → "DELETE FROM users WHERE id = 99"

ParseCache

Thread-safe LRU cache for parsed SQL ASTs. Cache key is (sql_text, SqlDialect).

Method Description
ParseCache::new(capacity) Create cache; capacity 0 is promoted to 1
cache.parse(sql, dialect) Return cached result or parse and cache
cache.len() Number of cached entries
cache.is_empty() True when no entries cached
cache.clear() Evict all entries

Logical planner

Function / Type Description
plan_query(stmt) Convert a parsed Statement into LogicalPlan
plan_statement(stmt) Alias for plan_query
LogicalPlan Enum: Scan, Filter, Projection, Join, Aggregate, Sort, Limit, SetOp, Subquery, Values, Empty
JoinType Inner, Left, Right, Full, Cross
SortExpr { expr, ascending, nulls_first }

Optimizer

Type / Function Description
optimize(plan) Apply the default four-pass optimizer
Optimizer::new() Build a custom multi-pass optimizer
PredicatePushdown Push WHERE filters below joins (predicate pushdown)
ProjectionPruning Remove unused column projections
ConstantFolding Evaluate constant expressions at plan time
LimitPushThrough Push LIMIT through projections
JoinAlgorithmPass Apply JoinAlgoHint to join nodes
OptPass Trait implemented by all optimization passes

DML planning

Type / Function Description
plan_dml(stmt) Convert INSERT/UPDATE/DELETE to DmlPlan
DmlPlan Insert { table, columns, values }, Update { ... }, Delete { ... }

Cost model

Type Description
CostModel Estimates query cost from a TableStats map
TableStats { row_count, avg_row_bytes, index_names }
CostEstimate { total_cost, estimated_rows }

Schema validator

Type Description
SchemaValidator Validates column/table references against a schema map
ValidationError UnknownTable, UnknownColumn, AmbiguousColumn

Aggregate helpers

Function / Type Description
extract_aggregates(expr) Extract aggregate expressions from an AST node
is_aggregate_expr(expr) True if expression contains an aggregate call
AggFunc Count, Sum, Avg, Min, Max
AggregateExpr { func, arg, alias }

Explain

use oxisql_parse::{plan_query, explain, parse_one};
let stmt = parse_one("SELECT id FROM users WHERE id = 1").unwrap();
let plan = plan_query(&stmt);
let text = explain(&plan, false); // false = no verbose output

Test Status

As of 2026-05-30: 118 tests passing.

License

Apache-2.0 — COOLJAPAN OU (Team Kitasan)