use crate::clang::*;
use crate::x86::*;
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use std::fmt;
use std::sync::atomic::{AtomicU64, Ordering};
pub const X86_SQL_MAX_STATEMENT_LENGTH: usize = 1_048_576;
pub const X86_SQL_MAX_BIND_PARAMS: usize = 999;
pub const X86_SQL_MAX_COLUMNS: usize = 4096;
pub const X86_SQL_MAX_IDENTIFIER_LEN: usize = 128;
pub const X86_SQLITE_MAX_LENGTH: usize = 1_000_000_000;
pub const X86_SQLITE_DEFAULT_PAGE_SIZE: u32 = 4096;
pub const X86_SQLITE_MAX_PAGE_SIZE: u32 = 65536;
pub const X86_SQLITE_MIN_PAGE_SIZE: u32 = 512;
pub const X86_PG_DEFAULT_PORT: u16 = 5432;
pub const X86_MYSQL_DEFAULT_PORT: u16 = 3306;
pub const X86_REDIS_DEFAULT_PORT: u16 = 6379;
pub const X86_ODBC_MAX_CONN_STR: usize = 1024;
pub const X86_ODBC_FETCH_BATCH: usize = 128;
pub const X86_SQLITE_WAL_CHECKPOINT_THRESHOLD: u32 = 1000;
pub const X86_SQLITE_WAL_AUTOCHECKPOINT: u32 = 1000;
pub const X86_SQLITE_DEFAULT_CACHE_SIZE: i32 = -2000;
pub const X86_SQLITE_JOURNAL_MODE: &str = "WAL";
pub const X86_SQLITE_SYNCHRONOUS: &str = "NORMAL";
pub const X86_DB_POINTER_SIZE: usize = 8;
pub const X86_DB_CACHELINE_SIZE: usize = 64;
#[derive(Debug, Clone)]
pub struct X86Database {
pub sql_support: X86SQLSupport,
pub sqlite_support: X86SQLiteSupport,
pub postgresql_support: X86PostgreSQLSupport,
pub mysql_support: X86MySQLSupport,
pub redis_support: X86RedisSupport,
pub odbc_support: X86ODBCSupport,
pub arch: X86DBArch,
pub endianness: String,
pub has_sse2: bool,
pub has_avx2: bool,
pub has_crc32c: bool,
pub has_aesni: bool,
pub has_clmul: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86DBArch {
X86_32,
X86_64,
X86_X32,
}
impl fmt::Display for X86DBArch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::X86_32 => write!(f, "x86-32"),
Self::X86_64 => write!(f, "x86-64"),
Self::X86_X32 => write!(f, "x86-x32"),
}
}
}
impl X86Database {
pub fn new() -> Self {
Self {
sql_support: X86SQLSupport::new(),
sqlite_support: X86SQLiteSupport::new(),
postgresql_support: X86PostgreSQLSupport::new(),
mysql_support: X86MySQLSupport::new(),
redis_support: X86RedisSupport::new(),
odbc_support: X86ODBCSupport::new(),
arch: X86DBArch::X86_64,
endianness: "little".to_string(),
has_sse2: true,
has_avx2: true,
has_crc32c: true,
has_aesni: true,
has_clmul: true,
}
}
pub fn new_x86_32() -> Self {
Self {
arch: X86DBArch::X86_32,
endianness: "little".to_string(),
has_sse2: true,
has_avx2: false,
has_crc32c: false,
has_aesni: false,
has_clmul: false,
..Self::new()
}
}
pub fn new_x86_x32() -> Self {
Self {
arch: X86DBArch::X86_X32,
endianness: "little".to_string(),
has_sse2: true,
has_avx2: true,
has_crc32c: true,
has_aesni: true,
has_clmul: true,
..Self::new()
}
}
pub fn validate(&self) -> X86DBValidationResult {
let mut errors = Vec::new();
let mut warnings = Vec::new();
if !self.sql_support.validate() {
errors.push("SQL support validation failed".to_string());
}
if !self.sqlite_support.validate() {
errors.push("SQLite support validation failed".to_string());
}
if !self.postgresql_support.validate() {
errors.push("PostgreSQL support validation failed".to_string());
}
if !self.mysql_support.validate() {
errors.push("MySQL support validation failed".to_string());
}
if !self.redis_support.validate() {
errors.push("Redis support validation failed".to_string());
}
if !self.odbc_support.validate() {
errors.push("ODBC support validation failed".to_string());
}
if self.arch == X86DBArch::X86_32 && self.has_avx2 {
warnings.push("AVX2 detected on 32-bit target — may not be available".to_string());
}
X86DBValidationResult {
valid: errors.is_empty(),
errors,
warnings,
}
}
pub fn compiler_flags(&self) -> Vec<String> {
let mut flags = vec![
"-std=c11".to_string(),
"-Wall".to_string(),
"-fPIC".to_string(),
"-D_GNU_SOURCE".to_string(),
];
if self.arch == X86DBArch::X86_64 || self.arch == X86DBArch::X86_X32 {
flags.push("-m64".to_string());
} else {
flags.push("-m32".to_string());
}
if self.has_sse2 {
flags.push("-msse2".to_string());
}
if self.has_avx2 {
flags.push("-mavx2".to_string());
}
if self.has_crc32c {
flags.push("-mcrc32".to_string());
}
if self.has_aesni {
flags.push("-maes".to_string());
}
if self.has_clmul {
flags.push("-mpclmul".to_string());
}
flags
}
pub fn linker_flags(&self) -> Vec<String> {
let mut flags = vec![
"-lpthread".to_string(),
"-ldl".to_string(),
"-lm".to_string(),
];
if self.arch == X86DBArch::X86_64 || self.arch == X86DBArch::X86_X32 {
flags.push("-Wl,-melf_x86_64".to_string());
} else {
flags.push("-Wl,-melf_i386".to_string());
}
flags
}
}
impl Default for X86Database {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86DBValidationResult {
pub valid: bool,
pub errors: Vec<String>,
pub warnings: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct X86SQLSupport {
pub dialect: X86SQLDialect,
pub lexer: X86SQLLexer,
pub parser: X86SQLParser,
pub type_system: X86SQLTypeSystem,
pub index_support: X86SQLIndexSupport,
pub transaction_support: X86SQLTransactionSupport,
pub subquery_support: bool,
pub window_fn_support: bool,
pub cte_support: bool,
pub recursive_cte_support: bool,
pub statement_cache: HashMap<String, X86SQLCompiledStatement>,
pub stats: X86SQLCompileStats,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLDialect {
Standard,
SQLite,
PostgreSQL,
MySQL,
MariaDB,
MSSQL,
Oracle,
}
impl fmt::Display for X86SQLDialect {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Standard => write!(f, "SQL-Standard"),
Self::SQLite => write!(f, "SQLite"),
Self::PostgreSQL => write!(f, "PostgreSQL"),
Self::MySQL => write!(f, "MySQL"),
Self::MariaDB => write!(f, "MariaDB"),
Self::MSSQL => write!(f, "MSSQL"),
Self::Oracle => write!(f, "Oracle"),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct X86SQLCompileStats {
pub statements_compiled: u64,
pub tokens_lexed: u64,
pub parse_errors: u64,
pub type_errors: u64,
pub avg_compile_time_us: f64,
pub cache_hits: u64,
pub cache_misses: u64,
}
impl X86SQLSupport {
pub fn new() -> Self {
Self {
dialect: X86SQLDialect::Standard,
lexer: X86SQLLexer::new(),
parser: X86SQLParser::new(),
type_system: X86SQLTypeSystem::new(),
index_support: X86SQLIndexSupport::new(),
transaction_support: X86SQLTransactionSupport::new(),
subquery_support: true,
window_fn_support: true,
cte_support: true,
recursive_cte_support: true,
statement_cache: HashMap::new(),
stats: X86SQLCompileStats::default(),
}
}
pub fn validate(&self) -> bool {
self.lexer.validate() && self.parser.validate() && self.type_system.validate()
}
pub fn compile(&mut self, sql: &str) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
if let Some(cached) = self.statement_cache.get(sql) {
self.stats.cache_hits += 1;
return Ok(cached.clone());
}
self.stats.cache_misses += 1;
let tokens = self.lexer.tokenize(sql)?;
self.stats.tokens_lexed += tokens.len() as u64;
let statement = self.parser.parse(&tokens)?;
self.stats.statements_compiled += 1;
self.statement_cache
.insert(sql.to_string(), statement.clone());
Ok(statement)
}
}
#[derive(Debug, Clone)]
pub struct X86SQLCompiledStatement {
pub statement_type: X86SQLStatementType,
pub columns: Vec<X86SQLColumnDef>,
pub table_name: Option<String>,
pub conditions: Vec<X86SQLCondition>,
pub joins: Vec<X86SQLJoin>,
pub group_by: Vec<String>,
pub having: Option<X86SQLExpression>,
pub order_by: Vec<X86SQLOrderBy>,
pub limit: Option<i64>,
pub offset: Option<i64>,
pub subqueries: Vec<X86SQLSubquery>,
pub window_fns: Vec<X86SQLWindowFn>,
pub ctes: Vec<X86SQLCTE>,
pub parameter_count: usize,
pub result_columns: Vec<X86SQLResultColumn>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLStatementType {
Select,
Insert,
Update,
Delete,
CreateTable,
DropTable,
AlterTable,
CreateIndex,
DropIndex,
Begin,
Commit,
Rollback,
Savepoint,
Release,
Explain,
Other,
}
impl fmt::Display for X86SQLStatementType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Select => write!(f, "SELECT"),
Self::Insert => write!(f, "INSERT"),
Self::Update => write!(f, "UPDATE"),
Self::Delete => write!(f, "DELETE"),
Self::CreateTable => write!(f, "CREATE TABLE"),
Self::DropTable => write!(f, "DROP TABLE"),
Self::AlterTable => write!(f, "ALTER TABLE"),
Self::CreateIndex => write!(f, "CREATE INDEX"),
Self::DropIndex => write!(f, "DROP INDEX"),
Self::Begin => write!(f, "BEGIN"),
Self::Commit => write!(f, "COMMIT"),
Self::Rollback => write!(f, "ROLLBACK"),
Self::Savepoint => write!(f, "SAVEPOINT"),
Self::Release => write!(f, "RELEASE"),
Self::Explain => write!(f, "EXPLAIN"),
Self::Other => write!(f, "OTHER"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86SQLColumnDef {
pub name: String,
pub data_type: X86SQLDataType,
pub nullable: bool,
pub primary_key: bool,
pub unique: bool,
pub default_value: Option<X86SQLLiteral>,
pub check_constraint: Option<String>,
pub references: Option<(String, String)>,
pub auto_increment: bool,
}
#[derive(Debug, Clone)]
pub struct X86SQLCondition {
pub column: String,
pub operator: X86SQLOperator,
pub value: X86SQLExpression,
pub logical_op: X86SQLLogicalOp,
}
#[derive(Debug, Clone)]
pub struct X86SQLJoin {
pub join_type: X86SQLJoinType,
pub table: String,
pub alias: Option<String>,
pub on_condition: Option<X86SQLExpression>,
pub using_columns: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct X86SQLOrderBy {
pub column: String,
pub ascending: bool,
pub nulls_first: bool,
}
#[derive(Debug, Clone)]
pub struct X86SQLSubquery {
pub subquery_type: X86SQLSubqueryType,
pub statement: Box<X86SQLCompiledStatement>,
pub correlated: bool,
pub correlation_columns: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct X86SQLWindowFn {
pub function: X86SQLWindowFnType,
pub expression: X86SQLExpression,
pub partition_by: Vec<String>,
pub order_by: Vec<X86SQLOrderBy>,
pub frame: Option<X86SQLWindowFrame>,
}
#[derive(Debug, Clone)]
pub struct X86SQLCTE {
pub name: String,
pub columns: Vec<String>,
pub is_recursive: bool,
pub query: Box<X86SQLCompiledStatement>,
}
#[derive(Debug, Clone)]
pub struct X86SQLResultColumn {
pub name: String,
pub data_type: X86SQLDataType,
pub expression: Option<String>,
pub alias: Option<String>,
pub table_qualifier: Option<String>,
}
#[derive(Debug, Clone)]
pub struct X86SQLLexer {
pub position: usize,
pub buffer: Vec<u8>,
pub tokens: Vec<X86SQLToken>,
pub state: X86SQLLexerState,
pub keywords: HashMap<String, X86SQLKeyword>,
pub case_sensitive: bool,
pub line: u32,
pub column: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLLexerState {
Normal,
InString,
InIdentifier,
InComment,
InBlockComment,
Error,
}
#[derive(Debug, Clone, PartialEq)]
pub enum X86SQLToken {
Keyword(X86SQLKeyword),
Identifier(String),
StringLiteral(String),
NumericLiteral(String),
Operator(X86SQLOperator),
Comma,
Semicolon,
LeftParen,
RightParen,
Dot,
Star,
QuestionMark,
Colon,
Placeholder(usize),
EOF,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86SQLKeyword {
Select,
Insert,
Update,
Delete,
From,
Where,
Set,
Values,
Into,
Returning,
Replace,
Merge,
Create,
Drop,
Alter,
Table,
Index,
View,
Trigger,
Schema,
Database,
If,
Not,
Exists,
Temporary,
Temp,
Primary,
Key,
Foreign,
References,
Unique,
Check,
Default,
Null,
Constraint,
Cascade,
Restrict,
Action,
No,
Join,
Inner,
Left,
Right,
Full,
Outer,
Cross,
Natural,
Using,
As,
On,
Or,
And,
In,
Like,
Between,
Is,
Distinct,
All,
Any,
Some,
Case,
When,
Then,
Else,
End,
Union,
Except,
Intersect,
Limit,
Offset,
Fetch,
Next,
Rows,
Only,
With,
Ties,
Count,
Sum,
Avg,
Min,
Max,
Group,
By,
Having,
Order,
Asc,
Desc,
Nulls,
First,
Last,
Over,
Partition,
Row,
Number,
Rank,
DenseRank,
Ntile,
Lag,
Lead,
FirstValue,
LastValue,
NthValue,
Range,
Unbounded,
Preceding,
Following,
Current,
Recursive,
Begin,
Commit,
Rollback,
Savepoint,
Release,
Transaction,
Read,
Write,
OnlyRead,
ReadWrite,
Serializable,
Repeatable,
Committed,
Uncommitted,
Isolation,
Level,
Deferrable,
Immediate,
Exclusive,
Explain,
Analyze,
Query,
Plan,
Integer,
BigInt,
SmallInt,
TinyInt,
MediumInt,
Real,
Double,
Float,
Decimal,
Numeric,
Boolean,
Bool,
Char,
Varchar,
NChar,
NVarchar,
Text,
Blob,
Binary,
VarBinary,
Date,
Time,
Timestamp,
DateTime,
Interval,
Json,
Jsonb,
Uuid,
Bytea,
Enum,
SetType,
Serial,
BigSerial,
AutoIncrement,
Identity,
Generated,
Always,
Btree,
Hash,
Gin,
Gist,
SpGist,
Brin,
}
impl X86SQLKeyword {
pub fn as_str(&self) -> &'static str {
match self {
Self::Select => "SELECT",
Self::Insert => "INSERT",
Self::Update => "UPDATE",
Self::Delete => "DELETE",
Self::From => "FROM",
Self::Where => "WHERE",
Self::Set => "SET",
Self::Values => "VALUES",
Self::Into => "INTO",
Self::Returning => "RETURNING",
Self::Replace => "REPLACE",
Self::Merge => "MERGE",
Self::Create => "CREATE",
Self::Drop => "DROP",
Self::Alter => "ALTER",
Self::Table => "TABLE",
Self::Index => "INDEX",
Self::View => "VIEW",
Self::Trigger => "TRIGGER",
Self::Schema => "SCHEMA",
Self::Database => "DATABASE",
Self::If => "IF",
Self::Not => "NOT",
Self::Exists => "EXISTS",
Self::Temporary => "TEMPORARY",
Self::Temp => "TEMP",
Self::Primary => "PRIMARY",
Self::Key => "KEY",
Self::Foreign => "FOREIGN",
Self::References => "REFERENCES",
Self::Unique => "UNIQUE",
Self::Check => "CHECK",
Self::Default => "DEFAULT",
Self::Null => "NULL",
Self::Constraint => "CONSTRAINT",
Self::Cascade => "CASCADE",
Self::Restrict => "RESTRICT",
Self::Action => "ACTION",
Self::No => "NO",
Self::Join => "JOIN",
Self::Inner => "INNER",
Self::Left => "LEFT",
Self::Right => "RIGHT",
Self::Full => "FULL",
Self::Outer => "OUTER",
Self::Cross => "CROSS",
Self::Natural => "NATURAL",
Self::Using => "USING",
Self::As => "AS",
Self::On => "ON",
Self::Or => "OR",
Self::And => "AND",
Self::In => "IN",
Self::Like => "LIKE",
Self::Between => "BETWEEN",
Self::Is => "IS",
Self::Distinct => "DISTINCT",
Self::All => "ALL",
Self::Any => "ANY",
Self::Some => "SOME",
Self::Case => "CASE",
Self::When => "WHEN",
Self::Then => "THEN",
Self::Else => "ELSE",
Self::End => "END",
Self::Union => "UNION",
Self::Except => "EXCEPT",
Self::Intersect => "INTERSECT",
Self::Limit => "LIMIT",
Self::Offset => "OFFSET",
Self::Fetch => "FETCH",
Self::Next => "NEXT",
Self::Rows => "ROWS",
Self::Only => "ONLY",
Self::With => "WITH",
Self::Ties => "TIES",
Self::Count => "COUNT",
Self::Sum => "SUM",
Self::Avg => "AVG",
Self::Min => "MIN",
Self::Max => "MAX",
Self::Group => "GROUP",
Self::By => "BY",
Self::Having => "HAVING",
Self::Order => "ORDER",
Self::Asc => "ASC",
Self::Desc => "DESC",
Self::Nulls => "NULLS",
Self::First => "FIRST",
Self::Last => "LAST",
Self::Over => "OVER",
Self::Partition => "PARTITION",
Self::Row => "ROW",
Self::Number => "NUMBER",
Self::Rank => "RANK",
Self::DenseRank => "DENSE_RANK",
Self::Ntile => "NTILE",
Self::Lag => "LAG",
Self::Lead => "LEAD",
Self::FirstValue => "FIRST_VALUE",
Self::LastValue => "LAST_VALUE",
Self::NthValue => "NTH_VALUE",
Self::Range => "RANGE",
Self::Unbounded => "UNBOUNDED",
Self::Preceding => "PRECEDING",
Self::Following => "FOLLOWING",
Self::Current => "CURRENT",
Self::Recursive => "RECURSIVE",
Self::Begin => "BEGIN",
Self::Commit => "COMMIT",
Self::Rollback => "ROLLBACK",
Self::Savepoint => "SAVEPOINT",
Self::Release => "RELEASE",
Self::Transaction => "TRANSACTION",
Self::Read => "READ",
Self::Write => "WRITE",
Self::OnlyRead => "READ ONLY",
Self::ReadWrite => "READ WRITE",
Self::Serializable => "SERIALIZABLE",
Self::Repeatable => "REPEATABLE",
Self::Committed => "COMMITTED",
Self::Uncommitted => "UNCOMMITTED",
Self::Isolation => "ISOLATION",
Self::Level => "LEVEL",
Self::Deferrable => "DEFERRABLE",
Self::Immediate => "IMMEDIATE",
Self::Exclusive => "EXCLUSIVE",
Self::Explain => "EXPLAIN",
Self::Analyze => "ANALYZE",
Self::Query => "QUERY",
Self::Plan => "PLAN",
Self::Integer => "INTEGER",
Self::BigInt => "BIGINT",
Self::SmallInt => "SMALLINT",
Self::TinyInt => "TINYINT",
Self::MediumInt => "MEDIUMINT",
Self::Real => "REAL",
Self::Double => "DOUBLE",
Self::Float => "FLOAT",
Self::Decimal => "DECIMAL",
Self::Numeric => "NUMERIC",
Self::Boolean => "BOOLEAN",
Self::Bool => "BOOL",
Self::Char => "CHAR",
Self::Varchar => "VARCHAR",
Self::NChar => "NCHAR",
Self::NVarchar => "NVARCHAR",
Self::Text => "TEXT",
Self::Blob => "BLOB",
Self::Binary => "BINARY",
Self::VarBinary => "VARBINARY",
Self::Date => "DATE",
Self::Time => "TIME",
Self::Timestamp => "TIMESTAMP",
Self::DateTime => "DATETIME",
Self::Interval => "INTERVAL",
Self::Json => "JSON",
Self::Jsonb => "JSONB",
Self::Uuid => "UUID",
Self::Bytea => "BYTEA",
Self::Enum => "ENUM",
Self::SetType => "SET",
Self::Serial => "SERIAL",
Self::BigSerial => "BIGSERIAL",
Self::AutoIncrement => "AUTO_INCREMENT",
Self::Identity => "IDENTITY",
Self::Generated => "GENERATED",
Self::Always => "ALWAYS",
Self::Btree => "BTREE",
Self::Hash => "HASH",
Self::Gin => "GIN",
Self::Gist => "GIST",
Self::SpGist => "SP-GIST",
Self::Brin => "BRIN",
}
}
}
impl X86SQLLexer {
pub fn new() -> Self {
let mut keywords = HashMap::new();
let all_kws = [
("SELECT", X86SQLKeyword::Select),
("INSERT", X86SQLKeyword::Insert),
("UPDATE", X86SQLKeyword::Update),
("DELETE", X86SQLKeyword::Delete),
("FROM", X86SQLKeyword::From),
("WHERE", X86SQLKeyword::Where),
("SET", X86SQLKeyword::Set),
("VALUES", X86SQLKeyword::Values),
("INTO", X86SQLKeyword::Into),
("RETURNING", X86SQLKeyword::Returning),
("REPLACE", X86SQLKeyword::Replace),
("MERGE", X86SQLKeyword::Merge),
("CREATE", X86SQLKeyword::Create),
("DROP", X86SQLKeyword::Drop),
("ALTER", X86SQLKeyword::Alter),
("TABLE", X86SQLKeyword::Table),
("INDEX", X86SQLKeyword::Index),
("VIEW", X86SQLKeyword::View),
("TRIGGER", X86SQLKeyword::Trigger),
("SCHEMA", X86SQLKeyword::Schema),
("DATABASE", X86SQLKeyword::Database),
("IF", X86SQLKeyword::If),
("NOT", X86SQLKeyword::Not),
("EXISTS", X86SQLKeyword::Exists),
("TEMPORARY", X86SQLKeyword::Temporary),
("TEMP", X86SQLKeyword::Temp),
("PRIMARY", X86SQLKeyword::Primary),
("KEY", X86SQLKeyword::Key),
("FOREIGN", X86SQLKeyword::Foreign),
("REFERENCES", X86SQLKeyword::References),
("UNIQUE", X86SQLKeyword::Unique),
("CHECK", X86SQLKeyword::Check),
("DEFAULT", X86SQLKeyword::Default),
("NULL", X86SQLKeyword::Null),
("CONSTRAINT", X86SQLKeyword::Constraint),
("CASCADE", X86SQLKeyword::Cascade),
("RESTRICT", X86SQLKeyword::Restrict),
("ACTION", X86SQLKeyword::Action),
("NO", X86SQLKeyword::No),
("JOIN", X86SQLKeyword::Join),
("INNER", X86SQLKeyword::Inner),
("LEFT", X86SQLKeyword::Left),
("RIGHT", X86SQLKeyword::Right),
("FULL", X86SQLKeyword::Full),
("OUTER", X86SQLKeyword::Outer),
("CROSS", X86SQLKeyword::Cross),
("NATURAL", X86SQLKeyword::Natural),
("USING", X86SQLKeyword::Using),
("AS", X86SQLKeyword::As),
("ON", X86SQLKeyword::On),
("OR", X86SQLKeyword::Or),
("AND", X86SQLKeyword::And),
("IN", X86SQLKeyword::In),
("LIKE", X86SQLKeyword::Like),
("BETWEEN", X86SQLKeyword::Between),
("IS", X86SQLKeyword::Is),
("DISTINCT", X86SQLKeyword::Distinct),
("ALL", X86SQLKeyword::All),
("ANY", X86SQLKeyword::Any),
("SOME", X86SQLKeyword::Some),
("CASE", X86SQLKeyword::Case),
("WHEN", X86SQLKeyword::When),
("THEN", X86SQLKeyword::Then),
("ELSE", X86SQLKeyword::Else),
("END", X86SQLKeyword::End),
("UNION", X86SQLKeyword::Union),
("EXCEPT", X86SQLKeyword::Except),
("INTERSECT", X86SQLKeyword::Intersect),
("LIMIT", X86SQLKeyword::Limit),
("OFFSET", X86SQLKeyword::Offset),
("FETCH", X86SQLKeyword::Fetch),
("NEXT", X86SQLKeyword::Next),
("ROWS", X86SQLKeyword::Rows),
("ONLY", X86SQLKeyword::Only),
("WITH", X86SQLKeyword::With),
("TIES", X86SQLKeyword::Ties),
("COUNT", X86SQLKeyword::Count),
("SUM", X86SQLKeyword::Sum),
("AVG", X86SQLKeyword::Avg),
("MIN", X86SQLKeyword::Min),
("MAX", X86SQLKeyword::Max),
("GROUP", X86SQLKeyword::Group),
("BY", X86SQLKeyword::By),
("HAVING", X86SQLKeyword::Having),
("ORDER", X86SQLKeyword::Order),
("ASC", X86SQLKeyword::Asc),
("DESC", X86SQLKeyword::Desc),
("NULLS", X86SQLKeyword::Nulls),
("FIRST", X86SQLKeyword::First),
("LAST", X86SQLKeyword::Last),
("OVER", X86SQLKeyword::Over),
("PARTITION", X86SQLKeyword::Partition),
("ROW", X86SQLKeyword::Row),
("NUMBER", X86SQLKeyword::Number),
("RANK", X86SQLKeyword::Rank),
("DENSE_RANK", X86SQLKeyword::DenseRank),
("NTILE", X86SQLKeyword::Ntile),
("LAG", X86SQLKeyword::Lag),
("LEAD", X86SQLKeyword::Lead),
("FIRST_VALUE", X86SQLKeyword::FirstValue),
("LAST_VALUE", X86SQLKeyword::LastValue),
("NTH_VALUE", X86SQLKeyword::NthValue),
("RANGE", X86SQLKeyword::Range),
("UNBOUNDED", X86SQLKeyword::Unbounded),
("PRECEDING", X86SQLKeyword::Preceding),
("FOLLOWING", X86SQLKeyword::Following),
("CURRENT", X86SQLKeyword::Current),
("RECURSIVE", X86SQLKeyword::Recursive),
("BEGIN", X86SQLKeyword::Begin),
("COMMIT", X86SQLKeyword::Commit),
("ROLLBACK", X86SQLKeyword::Rollback),
("SAVEPOINT", X86SQLKeyword::Savepoint),
("RELEASE", X86SQLKeyword::Release),
("TRANSACTION", X86SQLKeyword::Transaction),
("READ", X86SQLKeyword::Read),
("WRITE", X86SQLKeyword::Write),
("SERIALIZABLE", X86SQLKeyword::Serializable),
("REPEATABLE", X86SQLKeyword::Repeatable),
("COMMITTED", X86SQLKeyword::Committed),
("UNCOMMITTED", X86SQLKeyword::Uncommitted),
("ISOLATION", X86SQLKeyword::Isolation),
("LEVEL", X86SQLKeyword::Level),
("DEFERRABLE", X86SQLKeyword::Deferrable),
("IMMEDIATE", X86SQLKeyword::Immediate),
("EXCLUSIVE", X86SQLKeyword::Exclusive),
("EXPLAIN", X86SQLKeyword::Explain),
("ANALYZE", X86SQLKeyword::Analyze),
("QUERY", X86SQLKeyword::Query),
("PLAN", X86SQLKeyword::Plan),
("INTEGER", X86SQLKeyword::Integer),
("INT", X86SQLKeyword::Integer),
("BIGINT", X86SQLKeyword::BigInt),
("SMALLINT", X86SQLKeyword::SmallInt),
("TINYINT", X86SQLKeyword::TinyInt),
("MEDIUMINT", X86SQLKeyword::MediumInt),
("REAL", X86SQLKeyword::Real),
("DOUBLE", X86SQLKeyword::Double),
("FLOAT", X86SQLKeyword::Float),
("DECIMAL", X86SQLKeyword::Decimal),
("NUMERIC", X86SQLKeyword::Numeric),
("BOOLEAN", X86SQLKeyword::Boolean),
("BOOL", X86SQLKeyword::Bool),
("CHAR", X86SQLKeyword::Char),
("VARCHAR", X86SQLKeyword::Varchar),
("NCHAR", X86SQLKeyword::NChar),
("NVARCHAR", X86SQLKeyword::NVarchar),
("TEXT", X86SQLKeyword::Text),
("BLOB", X86SQLKeyword::Blob),
("BINARY", X86SQLKeyword::Binary),
("VARBINARY", X86SQLKeyword::VarBinary),
("DATE", X86SQLKeyword::Date),
("TIME", X86SQLKeyword::Time),
("TIMESTAMP", X86SQLKeyword::Timestamp),
("DATETIME", X86SQLKeyword::DateTime),
("INTERVAL", X86SQLKeyword::Interval),
("JSON", X86SQLKeyword::Json),
("JSONB", X86SQLKeyword::Jsonb),
("UUID", X86SQLKeyword::Uuid),
("BYTEA", X86SQLKeyword::Bytea),
("ENUM", X86SQLKeyword::Enum),
("SERIAL", X86SQLKeyword::Serial),
("BIGSERIAL", X86SQLKeyword::BigSerial),
("AUTO_INCREMENT", X86SQLKeyword::AutoIncrement),
("IDENTITY", X86SQLKeyword::Identity),
("GENERATED", X86SQLKeyword::Generated),
("ALWAYS", X86SQLKeyword::Always),
("BTREE", X86SQLKeyword::Btree),
("HASH", X86SQLKeyword::Hash),
("GIN", X86SQLKeyword::Gin),
("GIST", X86SQLKeyword::Gist),
("SP-GIST", X86SQLKeyword::SpGist),
("BRIN", X86SQLKeyword::Brin),
];
for (s, kw) in &all_kws {
keywords.insert(s.to_string(), *kw);
}
Self {
position: 0,
buffer: Vec::new(),
tokens: Vec::new(),
state: X86SQLLexerState::Normal,
keywords,
case_sensitive: false,
line: 1,
column: 1,
}
}
pub fn validate(&self) -> bool {
!self.keywords.is_empty()
}
pub fn tokenize(&mut self, input: &str) -> Result<Vec<X86SQLToken>, X86SQLCompileError> {
self.position = 0;
self.line = 1;
self.column = 1;
self.tokens.clear();
self.buffer = input.as_bytes().to_vec();
let len = self.buffer.len();
while self.position < len {
let ch = self.buffer[self.position] as char;
if ch.is_whitespace() {
if ch == '\n' {
self.line += 1;
self.column = 1;
} else {
self.column += 1;
}
self.position += 1;
continue;
}
if ch == '-' && self.position + 1 < len && self.buffer[self.position + 1] == b'-' {
self.skip_line_comment();
continue;
}
if ch == '/' && self.position + 1 < len && self.buffer[self.position + 1] == b'*' {
self.skip_block_comment()?;
continue;
}
if ch == '\'' {
let lit = self.lex_string_literal()?;
self.tokens.push(X86SQLToken::StringLiteral(lit));
continue;
}
if ch == '"' {
let id = self.lex_quoted_identifier()?;
self.tokens.push(X86SQLToken::Identifier(id));
continue;
}
if ch.is_ascii_digit() {
let num = self.lex_number()?;
self.tokens.push(X86SQLToken::NumericLiteral(num));
continue;
}
if ch == '.' && self.position + 1 < len {
let next = self.buffer[self.position + 1] as char;
if next.is_ascii_digit() {
let num = self.lex_number()?;
self.tokens.push(X86SQLToken::NumericLiteral(num));
continue;
}
}
if ch.is_alphabetic() || ch == '_' {
let word = self.lex_word();
let upper = word.to_uppercase();
if let Some(kw) = self.keywords.get(&upper) {
self.tokens.push(X86SQLToken::Keyword(*kw));
} else {
self.tokens.push(X86SQLToken::Identifier(word));
}
continue;
}
match ch {
',' => {
self.tokens.push(X86SQLToken::Comma);
self.position += 1;
self.column += 1;
}
';' => {
self.tokens.push(X86SQLToken::Semicolon);
self.position += 1;
self.column += 1;
}
'(' => {
self.tokens.push(X86SQLToken::LeftParen);
self.position += 1;
self.column += 1;
}
')' => {
self.tokens.push(X86SQLToken::RightParen);
self.position += 1;
self.column += 1;
}
'.' => {
self.tokens.push(X86SQLToken::Dot);
self.position += 1;
self.column += 1;
}
'*' => {
self.tokens.push(X86SQLToken::Star);
self.position += 1;
self.column += 1;
}
'?' => {
self.tokens.push(X86SQLToken::QuestionMark);
self.position += 1;
self.column += 1;
}
':' => {
self.tokens.push(X86SQLToken::Colon);
self.position += 1;
self.column += 1;
}
'=' | '!' | '<' | '>' | '+' | '-' | '/' | '%' | '&' | '|' | '^' | '~' => {
let op = self.lex_operator()?;
self.tokens.push(op);
}
_ => {
return Err(X86SQLCompileError::LexerError(format!(
"Unexpected character '{}' at line {}, column {}",
ch, self.line, self.column
)));
}
}
}
self.tokens.push(X86SQLToken::EOF);
Ok(self.tokens.clone())
}
fn skip_line_comment(&mut self) {
while self.position < self.buffer.len() && self.buffer[self.position] != b'\n' {
self.position += 1;
}
}
fn skip_block_comment(&mut self) -> Result<(), X86SQLCompileError> {
self.position += 2; let len = self.buffer.len();
while self.position + 1 < len {
if self.buffer[self.position] == b'\n' {
self.line += 1;
self.column = 1;
}
if self.buffer[self.position] == b'*' && self.buffer[self.position + 1] == b'/' {
self.position += 2;
return Ok(());
}
self.position += 1;
}
Err(X86SQLCompileError::LexerError(
"Unterminated block comment".to_string(),
))
}
fn lex_string_literal(&mut self) -> Result<String, X86SQLCompileError> {
self.position += 1; let mut s = String::new();
let len = self.buffer.len();
while self.position < len {
let ch = self.buffer[self.position] as char;
if ch == '\'' {
if self.position + 1 < len && self.buffer[self.position + 1] == b'\'' {
s.push('\'');
self.position += 2;
} else {
self.position += 1;
return Ok(s);
}
} else {
s.push(ch);
self.position += 1;
}
}
Err(X86SQLCompileError::LexerError(
"Unterminated string literal".to_string(),
))
}
fn lex_quoted_identifier(&mut self) -> Result<String, X86SQLCompileError> {
self.position += 1;
let mut s = String::new();
let len = self.buffer.len();
while self.position < len {
let ch = self.buffer[self.position] as char;
if ch == '"' {
if self.position + 1 < len && self.buffer[self.position + 1] == b'"' {
s.push('"');
self.position += 2;
} else {
self.position += 1;
return Ok(s);
}
} else {
s.push(ch);
self.position += 1;
}
}
Err(X86SQLCompileError::LexerError(
"Unterminated quoted identifier".to_string(),
))
}
fn lex_number(&mut self) -> Result<String, X86SQLCompileError> {
let start = self.position;
let len = self.buffer.len();
if self.position < len && self.buffer[self.position] == b'.' {
self.position += 1;
}
while self.position < len && (self.buffer[self.position] as char).is_ascii_digit() {
self.position += 1;
}
if self.position < len && self.buffer[self.position] == b'.' {
self.position += 1;
while self.position < len && (self.buffer[self.position] as char).is_ascii_digit() {
self.position += 1;
}
}
if self.position < len
&& (self.buffer[self.position] == b'e' || self.buffer[self.position] == b'E')
{
self.position += 1;
if self.position < len
&& (self.buffer[self.position] == b'+' || self.buffer[self.position] == b'-')
{
self.position += 1;
}
while self.position < len && (self.buffer[self.position] as char).is_ascii_digit() {
self.position += 1;
}
}
let s = std::str::from_utf8(&self.buffer[start..self.position])
.map_err(|e| X86SQLCompileError::LexerError(e.to_string()))?;
self.column += (self.position - start) as u32;
Ok(s.to_string())
}
fn lex_word(&mut self) -> String {
let start = self.position;
let len = self.buffer.len();
while self.position < len {
let ch = self.buffer[self.position] as char;
if ch.is_alphanumeric() || ch == '_' || ch == '$' {
self.position += 1;
} else {
break;
}
}
let word = std::str::from_utf8(&self.buffer[start..self.position]).unwrap_or("");
self.column += (self.position - start) as u32;
word.to_string()
}
fn lex_operator(&mut self) -> Result<X86SQLToken, X86SQLCompileError> {
let ch = self.buffer[self.position] as char;
let len = self.buffer.len();
if self.position + 1 < len {
let next = self.buffer[self.position + 1] as char;
let two_char = match (ch, next) {
('<', '=') => Some(X86SQLOperator::LessEqual),
('>', '=') => Some(X86SQLOperator::GreaterEqual),
('<', '>') => Some(X86SQLOperator::NotEqual),
('!', '=') => Some(X86SQLOperator::NotEqual),
('|', '|') => Some(X86SQLOperator::Concat),
_ => None,
};
if let Some(op) = two_char {
self.position += 2;
self.column += 2;
return Ok(X86SQLToken::Operator(op));
}
}
let op = match ch {
'=' => X86SQLOperator::Equal,
'<' => X86SQLOperator::LessThan,
'>' => X86SQLOperator::GreaterThan,
'+' => X86SQLOperator::Plus,
'-' => X86SQLOperator::Minus,
'*' => X86SQLOperator::Multiply,
'/' => X86SQLOperator::Divide,
'%' => X86SQLOperator::Modulo,
'!' => X86SQLOperator::Not,
_ => {
return Err(X86SQLCompileError::LexerError(format!(
"Unknown operator '{}'",
ch
)));
}
};
self.position += 1;
self.column += 1;
Ok(X86SQLToken::Operator(op))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLOperator {
Equal,
NotEqual,
LessThan,
GreaterThan,
LessEqual,
GreaterEqual,
Plus,
Minus,
Multiply,
Divide,
Modulo,
Not,
And,
Or,
Concat,
Like,
ILike,
Glob,
Match,
Regexp,
In,
NotIn,
Between,
NotBetween,
IsNull,
IsNotNull,
BitAnd,
BitOr,
BitXor,
BitNot,
ShiftLeft,
ShiftRight,
}
impl fmt::Display for X86SQLOperator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Equal => write!(f, "="),
Self::NotEqual => write!(f, "!="),
Self::LessThan => write!(f, "<"),
Self::GreaterThan => write!(f, ">"),
Self::LessEqual => write!(f, "<="),
Self::GreaterEqual => write!(f, ">="),
Self::Plus => write!(f, "+"),
Self::Minus => write!(f, "-"),
Self::Multiply => write!(f, "*"),
Self::Divide => write!(f, "/"),
Self::Modulo => write!(f, "%"),
Self::Not => write!(f, "NOT"),
Self::And => write!(f, "AND"),
Self::Or => write!(f, "OR"),
Self::Concat => write!(f, "||"),
Self::Like => write!(f, "LIKE"),
Self::ILike => write!(f, "ILIKE"),
Self::Glob => write!(f, "GLOB"),
Self::Match => write!(f, "MATCH"),
Self::Regexp => write!(f, "REGEXP"),
Self::In => write!(f, "IN"),
Self::NotIn => write!(f, "NOT IN"),
Self::Between => write!(f, "BETWEEN"),
Self::NotBetween => write!(f, "NOT BETWEEN"),
Self::IsNull => write!(f, "IS NULL"),
Self::IsNotNull => write!(f, "IS NOT NULL"),
Self::BitAnd => write!(f, "&"),
Self::BitOr => write!(f, "|"),
Self::BitXor => write!(f, "^"),
Self::BitNot => write!(f, "~"),
Self::ShiftLeft => write!(f, "<<"),
Self::ShiftRight => write!(f, ">>"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLLogicalOp {
And,
Or,
None,
}
impl fmt::Display for X86SQLLogicalOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::And => write!(f, "AND"),
Self::Or => write!(f, "OR"),
Self::None => write!(f, ""),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLJoinType {
Inner,
Left,
Right,
FullOuter,
Cross,
Natural,
LeftOuter,
RightOuter,
CrossJoin,
NaturalInner,
NaturalLeft,
NaturalRight,
NaturalFull,
}
impl fmt::Display for X86SQLJoinType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Inner => write!(f, "INNER JOIN"),
Self::Left => write!(f, "LEFT JOIN"),
Self::Right => write!(f, "RIGHT JOIN"),
Self::FullOuter => write!(f, "FULL OUTER JOIN"),
Self::Cross => write!(f, "CROSS JOIN"),
Self::Natural => write!(f, "NATURAL JOIN"),
Self::LeftOuter => write!(f, "LEFT OUTER JOIN"),
Self::RightOuter => write!(f, "RIGHT OUTER JOIN"),
Self::CrossJoin => write!(f, "CROSS JOIN"),
Self::NaturalInner => write!(f, "NATURAL INNER JOIN"),
Self::NaturalLeft => write!(f, "NATURAL LEFT JOIN"),
Self::NaturalRight => write!(f, "NATURAL RIGHT JOIN"),
Self::NaturalFull => write!(f, "NATURAL FULL JOIN"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLSubqueryType {
Scalar,
Exists,
NotExists,
InSubquery,
NotInSubquery,
Any,
All,
Correlated,
DerivedTable,
}
impl fmt::Display for X86SQLSubqueryType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Scalar => write!(f, "SCALAR"),
Self::Exists => write!(f, "EXISTS"),
Self::NotExists => write!(f, "NOT EXISTS"),
Self::InSubquery => write!(f, "IN"),
Self::NotInSubquery => write!(f, "NOT IN"),
Self::Any => write!(f, "ANY"),
Self::All => write!(f, "ALL"),
Self::Correlated => write!(f, "CORRELATED"),
Self::DerivedTable => write!(f, "DERIVED TABLE"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLWindowFnType {
RowNumber,
Rank,
DenseRank,
Ntile,
Lag,
Lead,
FirstValue,
LastValue,
NthValue,
PercentRank,
CumeDist,
RowNumberWindow,
}
impl fmt::Display for X86SQLWindowFnType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RowNumber => write!(f, "ROW_NUMBER"),
Self::Rank => write!(f, "RANK"),
Self::DenseRank => write!(f, "DENSE_RANK"),
Self::Ntile => write!(f, "NTILE"),
Self::Lag => write!(f, "LAG"),
Self::Lead => write!(f, "LEAD"),
Self::FirstValue => write!(f, "FIRST_VALUE"),
Self::LastValue => write!(f, "LAST_VALUE"),
Self::NthValue => write!(f, "NTH_VALUE"),
Self::PercentRank => write!(f, "PERCENT_RANK"),
Self::CumeDist => write!(f, "CUME_DIST"),
Self::RowNumberWindow => write!(f, "ROW_NUMBER"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86SQLWindowFrame {
pub frame_type: X86SQLWindowFrameType,
pub start_bound: X86SQLWindowBound,
pub end_bound: Option<X86SQLWindowBound>,
pub exclude: Option<X86SQLWindowExclude>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLWindowFrameType {
Rows,
Range,
Groups,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLWindowBound {
CurrentRow,
UnboundedPreceding,
UnboundedFollowing,
Preceding(i64),
Following(i64),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLWindowExclude {
NoOthers,
CurrentRow,
Group,
Ties,
}
#[derive(Debug, Clone)]
pub enum X86SQLExpression {
Column(String),
Literal(X86SQLLiteral),
BinaryOp {
left: Box<X86SQLExpression>,
op: X86SQLOperator,
right: Box<X86SQLExpression>,
},
UnaryOp {
op: X86SQLOperator,
expr: Box<X86SQLExpression>,
},
Function {
name: String,
args: Vec<X86SQLExpression>,
},
Subquery(Box<X86SQLCompiledStatement>),
Case {
when_clauses: Vec<(X86SQLExpression, X86SQLExpression)>,
else_clause: Option<Box<X86SQLExpression>>,
},
Cast {
expr: Box<X86SQLExpression>,
target_type: X86SQLDataType,
},
Between {
expr: Box<X86SQLExpression>,
low: Box<X86SQLExpression>,
high: Box<X86SQLExpression>,
},
InList {
expr: Box<X86SQLExpression>,
list: Vec<X86SQLExpression>,
},
IsNull(Box<X86SQLExpression>),
IsNotNull(Box<X86SQLExpression>),
Exists(Box<X86SQLCompiledStatement>),
Null,
Parameter(usize),
}
#[derive(Debug, Clone)]
pub struct X86SQLParser {
pub tokens: Vec<X86SQLToken>,
pub position: usize,
pub dialect: X86SQLDialect,
pub error_count: usize,
pub max_recursion_depth: usize,
}
impl X86SQLParser {
pub fn new() -> Self {
Self {
tokens: Vec::new(),
position: 0,
dialect: X86SQLDialect::Standard,
error_count: 0,
max_recursion_depth: 32,
}
}
pub fn validate(&self) -> bool {
self.max_recursion_depth > 0
}
pub fn parse(
&mut self,
tokens: &[X86SQLToken],
) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.tokens = tokens.to_vec();
self.position = 0;
self.error_count = 0;
if self.tokens.is_empty() || self.peek().is_none() {
return Err(X86SQLCompileError::ParseError("Empty input".to_string()));
}
let stmt = self.parse_statement()?;
Ok(stmt)
}
fn peek(&self) -> Option<&X86SQLToken> {
self.tokens.get(self.position)
}
fn advance(&mut self) -> Option<&X86SQLToken> {
let tok = self.tokens.get(self.position);
self.position += 1;
tok
}
fn expect_keyword(&mut self, kw: X86SQLKeyword) -> Result<(), X86SQLCompileError> {
match self.advance() {
Some(X86SQLToken::Keyword(k)) if *k == kw => Ok(()),
Some(other) => Err(X86SQLCompileError::ParseError(format!(
"Expected keyword {:?}, got {:?}",
kw.as_str(),
other
))),
None => Err(X86SQLCompileError::ParseError(format!(
"Expected keyword {:?}, got EOF",
kw.as_str()
))),
}
}
fn expect_identifier(&mut self) -> Result<String, X86SQLCompileError> {
match self.advance() {
Some(X86SQLToken::Identifier(s)) => Ok(s.clone()),
Some(other) => Err(X86SQLCompileError::ParseError(format!(
"Expected identifier, got {:?}",
other
))),
None => Err(X86SQLCompileError::ParseError(
"Expected identifier, got EOF".to_string(),
)),
}
}
fn parse_statement(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
match self.peek() {
Some(X86SQLToken::Keyword(kw)) => match kw {
X86SQLKeyword::Select => self.parse_select(),
X86SQLKeyword::Insert => self.parse_insert(),
X86SQLKeyword::Update => self.parse_update(),
X86SQLKeyword::Delete => self.parse_delete(),
X86SQLKeyword::Create => self.parse_create(),
X86SQLKeyword::Drop => self.parse_drop(),
X86SQLKeyword::Alter => self.parse_alter(),
X86SQLKeyword::Begin => self.parse_begin(),
X86SQLKeyword::Commit => self.parse_commit(),
X86SQLKeyword::Rollback => self.parse_rollback(),
X86SQLKeyword::Savepoint => self.parse_savepoint(),
X86SQLKeyword::Explain => self.parse_explain(),
X86SQLKeyword::With => self.parse_cte_statement(),
_ => Err(X86SQLCompileError::ParseError(format!(
"Unexpected keyword: {}",
kw.as_str()
))),
},
_ => Err(X86SQLCompileError::ParseError(
"Expected SQL statement keyword".to_string(),
)),
}
}
fn parse_select(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::Select)?;
let columns = self.parse_select_list()?;
let table_name = self.parse_from_clause()?;
let joins = self.parse_join_clauses()?;
let conditions = self.parse_where_clause()?;
let group_by = self.parse_group_by_clause()?;
let having = self.parse_having_clause()?;
let order_by = self.parse_order_by_clause()?;
let (limit, offset) = self.parse_limit_clause()?;
Ok(X86SQLCompiledStatement {
statement_type: X86SQLStatementType::Select,
columns: Vec::new(),
table_name,
conditions,
joins,
group_by,
having,
order_by,
limit,
offset,
subqueries: Vec::new(),
window_fns: Vec::new(),
ctes: Vec::new(),
parameter_count: 0,
result_columns: columns,
})
}
fn parse_select_list(&mut self) -> Result<Vec<X86SQLResultColumn>, X86SQLCompileError> {
let mut columns = Vec::new();
if let Some(X86SQLToken::Keyword(X86SQLKeyword::Distinct)) = self.peek() {
self.advance();
}
if let Some(X86SQLToken::Star) = self.peek() {
self.advance();
columns.push(X86SQLResultColumn {
name: "*".to_string(),
data_type: X86SQLDataType::Unknown,
expression: Some("*".to_string()),
alias: None,
table_qualifier: None,
});
return Ok(columns);
}
loop {
let col = self.parse_result_column()?;
columns.push(col);
if let Some(X86SQLToken::Comma) = self.peek() {
self.advance();
continue;
}
break;
}
Ok(columns)
}
fn parse_result_column(&mut self) -> Result<X86SQLResultColumn, X86SQLCompileError> {
let expr = self.parse_expression()?;
let alias = if let Some(X86SQLToken::Keyword(X86SQLKeyword::As)) = self.peek() {
self.advance();
Some(self.expect_identifier()?)
} else if let Some(X86SQLToken::Identifier(_)) = self.peek() {
Some(self.expect_identifier()?)
} else {
None
};
let name = alias.clone().unwrap_or_else(|| match &expr {
X86SQLExpression::Column(s) => s.clone(),
_ => "expr".to_string(),
});
Ok(X86SQLResultColumn {
name,
data_type: X86SQLDataType::Unknown,
expression: Some(format!("{:?}", expr)),
alias,
table_qualifier: None,
})
}
fn parse_from_clause(&mut self) -> Result<Option<String>, X86SQLCompileError> {
if let Some(X86SQLToken::Keyword(X86SQLKeyword::From)) = self.peek() {
self.advance();
let table = self.expect_identifier()?;
if let Some(X86SQLToken::Keyword(X86SQLKeyword::As)) = self.peek() {
self.advance();
let _ = self.expect_identifier()?;
} else if let Some(X86SQLToken::Identifier(_)) = self.peek() {
let _ = self.advance();
}
Ok(Some(table))
} else {
Ok(None)
}
}
fn parse_join_clauses(&mut self) -> Result<Vec<X86SQLJoin>, X86SQLCompileError> {
let mut joins = Vec::new();
while let Some(X86SQLToken::Keyword(kw)) = self.peek() {
let join_type = match kw {
X86SQLKeyword::Join => Some(X86SQLJoinType::Inner),
X86SQLKeyword::Inner => Some(X86SQLJoinType::Inner),
X86SQLKeyword::Left => Some(X86SQLJoinType::Left),
X86SQLKeyword::Right => Some(X86SQLJoinType::Right),
X86SQLKeyword::Full => Some(X86SQLJoinType::FullOuter),
X86SQLKeyword::Cross => Some(X86SQLJoinType::Cross),
X86SQLKeyword::Natural => Some(X86SQLJoinType::Natural),
_ => None,
};
if join_type.is_none() {
break;
}
self.advance();
let jt = join_type.unwrap();
if jt == X86SQLJoinType::Left
|| jt == X86SQLJoinType::Right
|| jt == X86SQLJoinType::FullOuter
{
if let Some(X86SQLToken::Keyword(X86SQLKeyword::Outer)) = self.peek() {
self.advance();
}
}
if let Some(X86SQLToken::Keyword(X86SQLKeyword::Join)) = self.peek() {
self.advance();
}
let table = self.expect_identifier()?;
let alias = if let Some(X86SQLToken::Keyword(X86SQLKeyword::As)) = self.peek() {
self.advance();
Some(self.expect_identifier()?)
} else if let Some(X86SQLToken::Identifier(_)) = self.peek() {
Some(self.expect_identifier()?)
} else {
None
};
let on_condition = if let Some(X86SQLToken::Keyword(X86SQLKeyword::On)) = self.peek() {
self.advance();
Some(self.parse_expression()?)
} else {
None
};
let using_columns =
if let Some(X86SQLToken::Keyword(X86SQLKeyword::Using)) = self.peek() {
self.advance();
self.expect_token(&X86SQLToken::LeftParen)?;
let mut cols = Vec::new();
cols.push(self.expect_identifier()?);
while let Some(X86SQLToken::Comma) = self.peek() {
self.advance();
cols.push(self.expect_identifier()?);
}
self.expect_token(&X86SQLToken::RightParen)?;
cols
} else {
Vec::new()
};
joins.push(X86SQLJoin {
join_type: jt,
table,
alias,
on_condition,
using_columns,
});
}
Ok(joins)
}
fn expect_token(&mut self, expected: &X86SQLToken) -> Result<(), X86SQLCompileError> {
match self.advance() {
Some(tok) if *tok == *expected => Ok(()),
Some(tok) => Err(X86SQLCompileError::ParseError(format!(
"Expected {:?}, got {:?}",
expected, tok
))),
None => Err(X86SQLCompileError::ParseError(
"Expected token, got EOF".to_string(),
)),
}
}
fn parse_where_clause(&mut self) -> Result<Vec<X86SQLCondition>, X86SQLCompileError> {
if let Some(X86SQLToken::Keyword(X86SQLKeyword::Where)) = self.peek() {
self.advance();
self.parse_condition_list()
} else {
Ok(Vec::new())
}
}
fn parse_condition_list(&mut self) -> Result<Vec<X86SQLCondition>, X86SQLCompileError> {
let mut conditions = Vec::new();
loop {
let column = self.expect_identifier()?;
let operator = self.parse_comparison_operator()?;
let value = self.parse_expression()?;
let logical_op = match self.peek() {
Some(X86SQLToken::Keyword(X86SQLKeyword::And)) => {
self.advance();
X86SQLLogicalOp::And
}
Some(X86SQLToken::Keyword(X86SQLKeyword::Or)) => {
self.advance();
X86SQLLogicalOp::Or
}
_ => X86SQLLogicalOp::None,
};
conditions.push(X86SQLCondition {
column,
operator,
value,
logical_op,
});
if logical_op == X86SQLLogicalOp::None {
break;
}
}
Ok(conditions)
}
fn parse_comparison_operator(&mut self) -> Result<X86SQLOperator, X86SQLCompileError> {
match self.peek() {
Some(X86SQLToken::Operator(op)) => {
let op = *op;
self.advance();
Ok(op)
}
Some(X86SQLToken::Keyword(X86SQLKeyword::Is)) => {
self.advance();
if let Some(X86SQLToken::Keyword(X86SQLKeyword::Not)) = self.peek() {
self.advance();
self.expect_keyword(X86SQLKeyword::Null)?;
Ok(X86SQLOperator::IsNotNull)
} else {
self.expect_keyword(X86SQLKeyword::Null)?;
Ok(X86SQLOperator::IsNull)
}
}
Some(X86SQLToken::Keyword(X86SQLKeyword::Like)) => {
self.advance();
Ok(X86SQLOperator::Like)
}
Some(X86SQLToken::Keyword(X86SQLKeyword::Between)) => {
self.advance();
Ok(X86SQLOperator::Between)
}
Some(X86SQLToken::Keyword(X86SQLKeyword::In)) => {
self.advance();
Ok(X86SQLOperator::In)
}
_ => Err(X86SQLCompileError::ParseError(
"Expected comparison operator".to_string(),
)),
}
}
fn parse_expression(&mut self) -> Result<X86SQLExpression, X86SQLCompileError> {
match self.peek().cloned() {
Some(X86SQLToken::Identifier(s)) => {
self.advance();
let col = s.clone();
if let Some(X86SQLToken::LeftParen) = self.peek() {
self.advance();
let mut args = Vec::new();
if !matches!(self.peek(), Some(X86SQLToken::RightParen)) {
args.push(self.parse_expression()?);
while let Some(X86SQLToken::Comma) = self.peek() {
self.advance();
args.push(self.parse_expression()?);
}
}
self.expect_token(&X86SQLToken::RightParen)?;
Ok(X86SQLExpression::Function { name: col, args })
} else {
Ok(X86SQLExpression::Column(col))
}
}
Some(X86SQLToken::StringLiteral(s)) => {
self.advance();
Ok(X86SQLExpression::Literal(X86SQLLiteral::String(s)))
}
Some(X86SQLToken::NumericLiteral(s)) => {
self.advance();
if s.contains('.') || s.contains('e') || s.contains('E') {
Ok(X86SQLExpression::Literal(X86SQLLiteral::Float(
s.parse().unwrap_or(0.0),
)))
} else {
Ok(X86SQLExpression::Literal(X86SQLLiteral::Integer(
s.parse().unwrap_or(0),
)))
}
}
Some(X86SQLToken::Keyword(X86SQLKeyword::Null)) => {
self.advance();
Ok(X86SQLExpression::Null)
}
Some(X86SQLToken::LeftParen) => {
self.advance();
if let Some(X86SQLToken::Keyword(X86SQLKeyword::Select)) = self.peek() {
let subquery = self.parse_select()?;
self.expect_token(&X86SQLToken::RightParen)?;
Ok(X86SQLExpression::Subquery(Box::new(subquery)))
} else {
let expr = self.parse_expression()?;
self.expect_token(&X86SQLToken::RightParen)?;
Ok(expr)
}
}
Some(X86SQLToken::QuestionMark) => {
self.advance();
Ok(X86SQLExpression::Parameter(0))
}
_ => Err(X86SQLCompileError::ParseError(
"Expected expression".to_string(),
)),
}
}
fn parse_group_by_clause(&mut self) -> Result<Vec<String>, X86SQLCompileError> {
if let Some(X86SQLToken::Keyword(X86SQLKeyword::Group)) = self.peek() {
self.advance();
self.expect_keyword(X86SQLKeyword::By)?;
let mut cols = Vec::new();
cols.push(self.expect_identifier()?);
while let Some(X86SQLToken::Comma) = self.peek() {
self.advance();
cols.push(self.expect_identifier()?);
}
Ok(cols)
} else {
Ok(Vec::new())
}
}
fn parse_having_clause(&mut self) -> Result<Option<X86SQLExpression>, X86SQLCompileError> {
if let Some(X86SQLToken::Keyword(X86SQLKeyword::Having)) = self.peek() {
self.advance();
Ok(Some(self.parse_expression()?))
} else {
Ok(None)
}
}
fn parse_order_by_clause(&mut self) -> Result<Vec<X86SQLOrderBy>, X86SQLCompileError> {
if let Some(X86SQLToken::Keyword(X86SQLKeyword::Order)) = self.peek() {
self.advance();
self.expect_keyword(X86SQLKeyword::By)?;
let mut cols = Vec::new();
loop {
let column = self.expect_identifier()?;
let ascending = match self.peek() {
Some(X86SQLToken::Keyword(X86SQLKeyword::Desc)) => {
self.advance();
false
}
Some(X86SQLToken::Keyword(X86SQLKeyword::Asc)) => {
self.advance();
true
}
_ => true,
};
cols.push(X86SQLOrderBy {
column,
ascending,
nulls_first: false,
});
if let Some(X86SQLToken::Comma) = self.peek() {
self.advance();
continue;
}
break;
}
Ok(cols)
} else {
Ok(Vec::new())
}
}
fn parse_limit_clause(&mut self) -> Result<(Option<i64>, Option<i64>), X86SQLCompileError> {
let mut limit = None;
let mut offset = None;
if let Some(X86SQLToken::Keyword(X86SQLKeyword::Limit)) = self.peek() {
self.advance();
if let Some(X86SQLToken::NumericLiteral(s)) = self.advance().cloned() {
limit = Some(s.parse().unwrap_or(0));
}
}
if let Some(X86SQLToken::Keyword(X86SQLKeyword::Offset)) = self.peek() {
self.advance();
if let Some(X86SQLToken::NumericLiteral(s)) = self.advance().cloned() {
offset = Some(s.parse().unwrap_or(0));
}
}
Ok((limit, offset))
}
fn parse_insert(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::Insert)?;
let _ = self.advance(); let table_name = self.expect_identifier()?;
let mut columns = Vec::new();
if let Some(X86SQLToken::LeftParen) = self.peek() {
self.advance();
loop {
columns.push(X86SQLColumnDef {
name: self.expect_identifier()?,
data_type: X86SQLDataType::Unknown,
nullable: true,
primary_key: false,
unique: false,
default_value: None,
check_constraint: None,
references: None,
auto_increment: false,
});
if let Some(X86SQLToken::Comma) = self.peek() {
self.advance();
continue;
}
break;
}
self.expect_token(&X86SQLToken::RightParen)?;
}
self.expect_keyword(X86SQLKeyword::Values)?;
self.expect_token(&X86SQLToken::LeftParen)?;
let mut result_columns = Vec::new();
loop {
let val = self.parse_expression()?;
result_columns.push(X86SQLResultColumn {
name: String::new(),
data_type: X86SQLDataType::Unknown,
expression: Some(format!("{:?}", val)),
alias: None,
table_qualifier: None,
});
if let Some(X86SQLToken::Comma) = self.peek() {
self.advance();
continue;
}
break;
}
self.expect_token(&X86SQLToken::RightParen)?;
Ok(X86SQLCompiledStatement {
statement_type: X86SQLStatementType::Insert,
columns,
table_name: Some(table_name),
conditions: Vec::new(),
joins: Vec::new(),
group_by: Vec::new(),
having: None,
order_by: Vec::new(),
limit: None,
offset: None,
subqueries: Vec::new(),
window_fns: Vec::new(),
ctes: Vec::new(),
parameter_count: 0,
result_columns,
})
}
fn parse_update(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::Update)?;
let table_name = self.expect_identifier()?;
self.expect_keyword(X86SQLKeyword::Set)?;
let mut result_columns = Vec::new();
loop {
let col = self.expect_identifier()?;
match self.peek() {
Some(X86SQLToken::Operator(X86SQLOperator::Equal)) => {
self.advance();
}
_ => {}
}
let val = self.parse_expression()?;
result_columns.push(X86SQLResultColumn {
name: col,
data_type: X86SQLDataType::Unknown,
expression: Some(format!("{:?}", val)),
alias: None,
table_qualifier: None,
});
if let Some(X86SQLToken::Comma) = self.peek() {
self.advance();
continue;
}
break;
}
let conditions = self.parse_where_clause()?;
Ok(X86SQLCompiledStatement {
statement_type: X86SQLStatementType::Update,
columns: Vec::new(),
table_name: Some(table_name),
conditions,
joins: Vec::new(),
group_by: Vec::new(),
having: None,
order_by: Vec::new(),
limit: None,
offset: None,
subqueries: Vec::new(),
window_fns: Vec::new(),
ctes: Vec::new(),
parameter_count: 0,
result_columns,
})
}
fn parse_delete(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::Delete)?;
self.expect_keyword(X86SQLKeyword::From)?;
let table_name = self.expect_identifier()?;
let conditions = self.parse_where_clause()?;
Ok(X86SQLCompiledStatement {
statement_type: X86SQLStatementType::Delete,
columns: Vec::new(),
table_name: Some(table_name),
conditions,
joins: Vec::new(),
group_by: Vec::new(),
having: None,
order_by: Vec::new(),
limit: None,
offset: None,
subqueries: Vec::new(),
window_fns: Vec::new(),
ctes: Vec::new(),
parameter_count: 0,
result_columns: Vec::new(),
})
}
fn parse_create(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::Create)?;
if matches!(
self.peek(),
Some(X86SQLToken::Keyword(X86SQLKeyword::Temporary))
| Some(X86SQLToken::Keyword(X86SQLKeyword::Temp))
) {
self.advance();
}
match self.peek() {
Some(X86SQLToken::Keyword(X86SQLKeyword::Table)) => self.parse_create_table(),
Some(X86SQLToken::Keyword(X86SQLKeyword::Index)) => self.parse_create_index(),
_ => Err(X86SQLCompileError::ParseError(
"Expected TABLE or INDEX after CREATE".to_string(),
)),
}
}
fn parse_create_table(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::Table)?;
if let Some(X86SQLToken::Keyword(X86SQLKeyword::If)) = self.peek() {
self.advance();
self.expect_keyword(X86SQLKeyword::Not)?;
self.expect_keyword(X86SQLKeyword::Exists)?;
}
let table_name = self.expect_identifier()?;
self.expect_token(&X86SQLToken::LeftParen)?;
let mut columns = Vec::new();
loop {
let col_name = self.expect_identifier()?;
let data_type = self.parse_data_type()?;
let mut col = X86SQLColumnDef {
name: col_name,
data_type,
nullable: true,
primary_key: false,
unique: false,
default_value: None,
check_constraint: None,
references: None,
auto_increment: false,
};
loop {
match self.peek() {
Some(X86SQLToken::Keyword(X86SQLKeyword::Primary)) => {
self.advance();
self.expect_keyword(X86SQLKeyword::Key)?;
col.primary_key = true;
}
Some(X86SQLToken::Keyword(X86SQLKeyword::Not)) => {
self.advance();
self.expect_keyword(X86SQLKeyword::Null)?;
col.nullable = false;
}
Some(X86SQLToken::Keyword(X86SQLKeyword::Null)) => {
self.advance();
col.nullable = true;
}
Some(X86SQLToken::Keyword(X86SQLKeyword::Unique)) => {
self.advance();
col.unique = true;
}
Some(X86SQLToken::Keyword(X86SQLKeyword::Default)) => {
self.advance();
col.default_value = Some(self.parse_literal()?);
}
Some(X86SQLToken::Keyword(X86SQLKeyword::Check)) => {
self.advance();
self.expect_token(&X86SQLToken::LeftParen)?;
let mut depth = 1;
while depth > 0 {
match self.advance() {
Some(X86SQLToken::LeftParen) => depth += 1,
Some(X86SQLToken::RightParen) => depth -= 1,
Some(X86SQLToken::EOF) | None => break,
_ => {}
}
}
}
Some(X86SQLToken::Keyword(X86SQLKeyword::References)) => {
self.advance();
let ref_table = self.expect_identifier()?;
self.expect_token(&X86SQLToken::LeftParen)?;
let ref_col = self.expect_identifier()?;
self.expect_token(&X86SQLToken::RightParen)?;
col.references = Some((ref_table, ref_col));
}
_ => break,
}
}
columns.push(col);
if let Some(X86SQLToken::Comma) = self.peek() {
self.advance();
continue;
}
break;
}
self.expect_token(&X86SQLToken::RightParen)?;
Ok(X86SQLCompiledStatement {
statement_type: X86SQLStatementType::CreateTable,
columns,
table_name: Some(table_name),
conditions: Vec::new(),
joins: Vec::new(),
group_by: Vec::new(),
having: None,
order_by: Vec::new(),
limit: None,
offset: None,
subqueries: Vec::new(),
window_fns: Vec::new(),
ctes: Vec::new(),
parameter_count: 0,
result_columns: Vec::new(),
})
}
fn parse_create_index(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::Index)?;
let _index_name = self.expect_identifier()?;
self.expect_keyword(X86SQLKeyword::On)?;
let table_name = self.expect_identifier()?;
self.expect_token(&X86SQLToken::LeftParen)?;
let mut columns = Vec::new();
columns.push(X86SQLColumnDef {
name: self.expect_identifier()?,
data_type: X86SQLDataType::Unknown,
nullable: true,
primary_key: false,
unique: false,
default_value: None,
check_constraint: None,
references: None,
auto_increment: false,
});
while let Some(X86SQLToken::Comma) = self.peek() {
self.advance();
columns.push(X86SQLColumnDef {
name: self.expect_identifier()?,
data_type: X86SQLDataType::Unknown,
nullable: true,
primary_key: false,
unique: false,
default_value: None,
check_constraint: None,
references: None,
auto_increment: false,
});
}
self.expect_token(&X86SQLToken::RightParen)?;
Ok(X86SQLCompiledStatement {
statement_type: X86SQLStatementType::CreateIndex,
columns,
table_name: Some(table_name),
conditions: Vec::new(),
joins: Vec::new(),
group_by: Vec::new(),
having: None,
order_by: Vec::new(),
limit: None,
offset: None,
subqueries: Vec::new(),
window_fns: Vec::new(),
ctes: Vec::new(),
parameter_count: 0,
result_columns: Vec::new(),
})
}
fn parse_drop(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::Drop)?;
match self.peek() {
Some(X86SQLToken::Keyword(X86SQLKeyword::Table)) => {
self.advance();
if let Some(X86SQLToken::Keyword(X86SQLKeyword::If)) = self.peek() {
self.advance();
self.expect_keyword(X86SQLKeyword::Exists)?;
}
let table_name = self.expect_identifier()?;
Ok(X86SQLCompiledStatement {
statement_type: X86SQLStatementType::DropTable,
columns: Vec::new(),
table_name: Some(table_name),
conditions: Vec::new(),
joins: Vec::new(),
group_by: Vec::new(),
having: None,
order_by: Vec::new(),
limit: None,
offset: None,
subqueries: Vec::new(),
window_fns: Vec::new(),
ctes: Vec::new(),
parameter_count: 0,
result_columns: Vec::new(),
})
}
Some(X86SQLToken::Keyword(X86SQLKeyword::Index)) => {
self.advance();
let _index_name = self.expect_identifier()?;
Ok(X86SQLCompiledStatement {
statement_type: X86SQLStatementType::DropIndex,
columns: Vec::new(),
table_name: None,
conditions: Vec::new(),
joins: Vec::new(),
group_by: Vec::new(),
having: None,
order_by: Vec::new(),
limit: None,
offset: None,
subqueries: Vec::new(),
window_fns: Vec::new(),
ctes: Vec::new(),
parameter_count: 0,
result_columns: Vec::new(),
})
}
_ => Err(X86SQLCompileError::ParseError(
"Expected TABLE or INDEX after DROP".to_string(),
)),
}
}
fn parse_alter(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::Alter)?;
self.expect_keyword(X86SQLKeyword::Table)?;
let table_name = self.expect_identifier()?;
Ok(X86SQLCompiledStatement {
statement_type: X86SQLStatementType::AlterTable,
columns: Vec::new(),
table_name: Some(table_name),
conditions: Vec::new(),
joins: Vec::new(),
group_by: Vec::new(),
having: None,
order_by: Vec::new(),
limit: None,
offset: None,
subqueries: Vec::new(),
window_fns: Vec::new(),
ctes: Vec::new(),
parameter_count: 0,
result_columns: Vec::new(),
})
}
fn parse_begin(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::Begin)?;
if let Some(X86SQLToken::Keyword(X86SQLKeyword::Transaction)) = self.peek() {
self.advance();
}
Ok(X86SQLCompiledStatement {
statement_type: X86SQLStatementType::Begin,
columns: Vec::new(),
table_name: None,
conditions: Vec::new(),
joins: Vec::new(),
group_by: Vec::new(),
having: None,
order_by: Vec::new(),
limit: None,
offset: None,
subqueries: Vec::new(),
window_fns: Vec::new(),
ctes: Vec::new(),
parameter_count: 0,
result_columns: Vec::new(),
})
}
fn parse_commit(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::Commit)?;
Ok(X86SQLCompiledStatement {
statement_type: X86SQLStatementType::Commit,
columns: Vec::new(),
table_name: None,
conditions: Vec::new(),
joins: Vec::new(),
group_by: Vec::new(),
having: None,
order_by: Vec::new(),
limit: None,
offset: None,
subqueries: Vec::new(),
window_fns: Vec::new(),
ctes: Vec::new(),
parameter_count: 0,
result_columns: Vec::new(),
})
}
fn parse_rollback(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::Rollback)?;
Ok(X86SQLCompiledStatement {
statement_type: X86SQLStatementType::Rollback,
columns: Vec::new(),
table_name: None,
conditions: Vec::new(),
joins: Vec::new(),
group_by: Vec::new(),
having: None,
order_by: Vec::new(),
limit: None,
offset: None,
subqueries: Vec::new(),
window_fns: Vec::new(),
ctes: Vec::new(),
parameter_count: 0,
result_columns: Vec::new(),
})
}
fn parse_savepoint(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::Savepoint)?;
let _name = self.expect_identifier()?;
Ok(X86SQLCompiledStatement {
statement_type: X86SQLStatementType::Savepoint,
columns: Vec::new(),
table_name: None,
conditions: Vec::new(),
joins: Vec::new(),
group_by: Vec::new(),
having: None,
order_by: Vec::new(),
limit: None,
offset: None,
subqueries: Vec::new(),
window_fns: Vec::new(),
ctes: Vec::new(),
parameter_count: 0,
result_columns: Vec::new(),
})
}
fn parse_explain(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::Explain)?;
if let Some(X86SQLToken::Keyword(X86SQLKeyword::Query)) = self.peek() {
self.advance();
self.expect_keyword(X86SQLKeyword::Plan)?;
}
Ok(X86SQLCompiledStatement {
statement_type: X86SQLStatementType::Explain,
columns: Vec::new(),
table_name: None,
conditions: Vec::new(),
joins: Vec::new(),
group_by: Vec::new(),
having: None,
order_by: Vec::new(),
limit: None,
offset: None,
subqueries: Vec::new(),
window_fns: Vec::new(),
ctes: Vec::new(),
parameter_count: 0,
result_columns: Vec::new(),
})
}
fn parse_cte_statement(&mut self) -> Result<X86SQLCompiledStatement, X86SQLCompileError> {
self.expect_keyword(X86SQLKeyword::With)?;
let is_recursive = if let Some(X86SQLToken::Keyword(X86SQLKeyword::Recursive)) = self.peek()
{
self.advance();
true
} else {
false
};
let mut ctes = Vec::new();
loop {
let cte_name = self.expect_identifier()?;
let mut cte_columns = Vec::new();
if let Some(X86SQLToken::LeftParen) = self.peek() {
self.advance();
cte_columns.push(self.expect_identifier()?);
while let Some(X86SQLToken::Comma) = self.peek() {
self.advance();
cte_columns.push(self.expect_identifier()?);
}
self.expect_token(&X86SQLToken::RightParen)?;
}
self.expect_keyword(X86SQLKeyword::As)?;
self.expect_token(&X86SQLToken::LeftParen)?;
let query = self.parse_select()?;
self.expect_token(&X86SQLToken::RightParen)?;
ctes.push(X86SQLCTE {
name: cte_name,
columns: cte_columns,
is_recursive,
query: Box::new(query),
});
if let Some(X86SQLToken::Comma) = self.peek() {
self.advance();
continue;
}
break;
}
let mut stmt = self.parse_statement()?;
stmt.ctes = ctes;
Ok(stmt)
}
fn parse_data_type(&mut self) -> Result<X86SQLDataType, X86SQLCompileError> {
match self.peek() {
Some(X86SQLToken::Keyword(kw)) => {
let dt = X86SQLDataType::from_keyword(*kw);
self.advance();
if let Some(X86SQLToken::LeftParen) = self.peek() {
self.advance();
let mut depth = 1;
while depth > 0 {
match self.advance() {
Some(X86SQLToken::LeftParen) => depth += 1,
Some(X86SQLToken::RightParen) => depth -= 1,
Some(X86SQLToken::EOF) | None => break,
_ => {}
}
}
}
Ok(dt)
}
_ => Ok(X86SQLDataType::Unknown),
}
}
fn parse_literal(&mut self) -> Result<X86SQLLiteral, X86SQLCompileError> {
match self.peek().cloned() {
Some(X86SQLToken::StringLiteral(s)) => {
self.advance();
Ok(X86SQLLiteral::String(s))
}
Some(X86SQLToken::NumericLiteral(s)) => {
self.advance();
if s.contains('.') || s.contains('e') || s.contains('E') {
Ok(X86SQLLiteral::Float(s.parse().unwrap_or(0.0)))
} else {
Ok(X86SQLLiteral::Integer(s.parse().unwrap_or(0)))
}
}
Some(X86SQLToken::Keyword(X86SQLKeyword::Null)) => {
self.advance();
Ok(X86SQLLiteral::Null)
}
_ => Err(X86SQLCompileError::ParseError(
"Expected literal value".to_string(),
)),
}
}
}
#[derive(Debug, Clone)]
pub struct X86SQLTypeSystem {
pub types: HashMap<String, X86SQLDataType>,
pub type_aliases: HashMap<String, String>,
pub dialect_overrides: HashMap<X86SQLDialect, HashMap<String, X86SQLDataType>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86SQLDataType {
Unknown,
Integer,
BigInt,
SmallInt,
TinyInt,
MediumInt,
Int2,
Int4,
Int8,
Real,
Double,
Float4,
Float8,
Numeric,
Decimal,
Boolean,
Bool,
Char,
Varchar,
NChar,
NVarchar,
Text,
Blob,
Binary,
VarBinary,
Date,
Time,
Timestamp,
DateTime,
Interval,
Json,
Jsonb,
Uuid,
Bytea,
Enum,
}
impl X86SQLDataType {
pub fn from_keyword(kw: X86SQLKeyword) -> Self {
match kw {
X86SQLKeyword::Integer => Self::Integer,
X86SQLKeyword::BigInt => Self::BigInt,
X86SQLKeyword::SmallInt => Self::SmallInt,
X86SQLKeyword::TinyInt => Self::TinyInt,
X86SQLKeyword::MediumInt => Self::MediumInt,
X86SQLKeyword::Real => Self::Real,
X86SQLKeyword::Double => Self::Double,
X86SQLKeyword::Float => Self::Real,
X86SQLKeyword::Decimal => Self::Decimal,
X86SQLKeyword::Numeric => Self::Numeric,
X86SQLKeyword::Boolean | X86SQLKeyword::Bool => Self::Boolean,
X86SQLKeyword::Char => Self::Char,
X86SQLKeyword::Varchar => Self::Varchar,
X86SQLKeyword::NChar => Self::NChar,
X86SQLKeyword::NVarchar => Self::NVarchar,
X86SQLKeyword::Text => Self::Text,
X86SQLKeyword::Blob => Self::Blob,
X86SQLKeyword::Binary => Self::Binary,
X86SQLKeyword::VarBinary => Self::VarBinary,
X86SQLKeyword::Date => Self::Date,
X86SQLKeyword::Time => Self::Time,
X86SQLKeyword::Timestamp => Self::Timestamp,
X86SQLKeyword::DateTime => Self::DateTime,
X86SQLKeyword::Interval => Self::Interval,
X86SQLKeyword::Json => Self::Json,
X86SQLKeyword::Jsonb => Self::Jsonb,
X86SQLKeyword::Uuid => Self::Uuid,
X86SQLKeyword::Bytea => Self::Bytea,
_ => Self::Unknown,
}
}
pub fn x86_size(&self) -> usize {
match self {
Self::Integer | Self::Int4 => 4,
Self::BigInt | Self::Int8 => 8,
Self::SmallInt | Self::Int2 => 2,
Self::TinyInt => 1,
Self::MediumInt => 3,
Self::Real | Self::Float4 => 4,
Self::Double | Self::Float8 => 8,
Self::Boolean | Self::Bool => 1,
Self::Date => 4,
Self::Time => 8,
Self::Timestamp | Self::DateTime => 8,
Self::Interval => 16,
Self::Uuid => 16,
_ => 0, }
}
pub fn x86_alignment(&self) -> usize {
match self.x86_size() {
1 => 1,
2 => 2,
3 | 4 => 4,
8 => 8,
16 => 16,
_ => 8,
}
}
}
impl fmt::Display for X86SQLDataType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Unknown => write!(f, "UNKNOWN"),
Self::Integer => write!(f, "INTEGER"),
Self::BigInt => write!(f, "BIGINT"),
Self::SmallInt => write!(f, "SMALLINT"),
Self::TinyInt => write!(f, "TINYINT"),
Self::MediumInt => write!(f, "MEDIUMINT"),
Self::Int2 => write!(f, "INT2"),
Self::Int4 => write!(f, "INT4"),
Self::Int8 => write!(f, "INT8"),
Self::Real => write!(f, "REAL"),
Self::Double => write!(f, "DOUBLE"),
Self::Float4 => write!(f, "FLOAT4"),
Self::Float8 => write!(f, "FLOAT8"),
Self::Numeric => write!(f, "NUMERIC"),
Self::Decimal => write!(f, "DECIMAL"),
Self::Boolean => write!(f, "BOOLEAN"),
Self::Bool => write!(f, "BOOL"),
Self::Char => write!(f, "CHAR"),
Self::Varchar => write!(f, "VARCHAR"),
Self::NChar => write!(f, "NCHAR"),
Self::NVarchar => write!(f, "NVARCHAR"),
Self::Text => write!(f, "TEXT"),
Self::Blob => write!(f, "BLOB"),
Self::Binary => write!(f, "BINARY"),
Self::VarBinary => write!(f, "VARBINARY"),
Self::Date => write!(f, "DATE"),
Self::Time => write!(f, "TIME"),
Self::Timestamp => write!(f, "TIMESTAMP"),
Self::DateTime => write!(f, "DATETIME"),
Self::Interval => write!(f, "INTERVAL"),
Self::Json => write!(f, "JSON"),
Self::Jsonb => write!(f, "JSONB"),
Self::Uuid => write!(f, "UUID"),
Self::Bytea => write!(f, "BYTEA"),
Self::Enum => write!(f, "ENUM"),
}
}
}
impl X86SQLTypeSystem {
pub fn new() -> Self {
let mut types = HashMap::new();
let type_names = [
"INTEGER",
"INT",
"BIGINT",
"SMALLINT",
"TINYINT",
"MEDIUMINT",
"REAL",
"DOUBLE",
"FLOAT",
"DECIMAL",
"NUMERIC",
"BOOLEAN",
"BOOL",
"CHAR",
"VARCHAR",
"NCHAR",
"NVARCHAR",
"TEXT",
"BLOB",
"BINARY",
"VARBINARY",
"DATE",
"TIME",
"TIMESTAMP",
"DATETIME",
"INTERVAL",
"JSON",
"JSONB",
"UUID",
"BYTEA",
];
for name in &type_names {
types.insert(name.to_string(), X86SQLDataType::Unknown);
}
Self {
types,
type_aliases: HashMap::new(),
dialect_overrides: HashMap::new(),
}
}
pub fn validate(&self) -> bool {
!self.types.is_empty()
}
pub fn resolve(&self, name: &str) -> X86SQLDataType {
let upper = name.to_uppercase();
match upper.as_str() {
"INTEGER" | "INT" => X86SQLDataType::Integer,
"BIGINT" | "INT8" => X86SQLDataType::BigInt,
"SMALLINT" | "INT2" => X86SQLDataType::SmallInt,
"TINYINT" => X86SQLDataType::TinyInt,
"MEDIUMINT" => X86SQLDataType::MediumInt,
"REAL" | "FLOAT" | "FLOAT4" => X86SQLDataType::Real,
"DOUBLE" | "DOUBLE PRECISION" | "FLOAT8" => X86SQLDataType::Double,
"DECIMAL" => X86SQLDataType::Decimal,
"NUMERIC" => X86SQLDataType::Numeric,
"BOOLEAN" | "BOOL" => X86SQLDataType::Boolean,
"CHAR" | "CHARACTER" => X86SQLDataType::Char,
"VARCHAR" | "CHARACTER VARYING" => X86SQLDataType::Varchar,
"NCHAR" => X86SQLDataType::NChar,
"NVARCHAR" => X86SQLDataType::NVarchar,
"TEXT" => X86SQLDataType::Text,
"BLOB" | "BYTEA" => X86SQLDataType::Blob,
"BINARY" => X86SQLDataType::Binary,
"VARBINARY" => X86SQLDataType::VarBinary,
"DATE" => X86SQLDataType::Date,
"TIME" => X86SQLDataType::Time,
"TIMESTAMP" => X86SQLDataType::Timestamp,
"DATETIME" => X86SQLDataType::DateTime,
"INTERVAL" => X86SQLDataType::Interval,
"JSON" => X86SQLDataType::Json,
"JSONB" => X86SQLDataType::Jsonb,
"UUID" => X86SQLDataType::Uuid,
"BYTEA" => X86SQLDataType::Bytea,
_ => X86SQLDataType::Unknown,
}
}
}
#[derive(Debug, Clone)]
pub enum X86SQLLiteral {
Null,
Integer(i64),
Float(f64),
String(String),
Blob(Vec<u8>),
Boolean(bool),
Date(String),
Time(String),
Timestamp(String),
}
#[derive(Debug, Clone)]
pub struct X86SQLIndexSupport {
pub index_types: HashMap<String, X86SQLIndexType>,
pub default_index_type: X86SQLIndexType,
pub concurrent_index_build: bool,
pub partial_index_support: bool,
pub expression_index_support: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86SQLIndexType {
Btree,
Hash,
Gin,
Gist,
SpGist,
Brin,
RTree,
FullText,
Bitmap,
Clustered,
NonClustered,
Unique,
Spatial,
}
impl fmt::Display for X86SQLIndexType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Btree => write!(f, "BTREE"),
Self::Hash => write!(f, "HASH"),
Self::Gin => write!(f, "GIN"),
Self::Gist => write!(f, "GIST"),
Self::SpGist => write!(f, "SP-GIST"),
Self::Brin => write!(f, "BRIN"),
Self::RTree => write!(f, "RTREE"),
Self::FullText => write!(f, "FULLTEXT"),
Self::Bitmap => write!(f, "BITMAP"),
Self::Clustered => write!(f, "CLUSTERED"),
Self::NonClustered => write!(f, "NONCLUSTERED"),
Self::Unique => write!(f, "UNIQUE"),
Self::Spatial => write!(f, "SPATIAL"),
}
}
}
impl X86SQLIndexSupport {
pub fn new() -> Self {
let mut index_types = HashMap::new();
let idx_kinds = [
("BTREE", X86SQLIndexType::Btree),
("HASH", X86SQLIndexType::Hash),
("GIN", X86SQLIndexType::Gin),
("GIST", X86SQLIndexType::Gist),
("SP-GIST", X86SQLIndexType::SpGist),
("BRIN", X86SQLIndexType::Brin),
("RTREE", X86SQLIndexType::RTree),
("FULLTEXT", X86SQLIndexType::FullText),
];
for (name, it) in &idx_kinds {
index_types.insert(name.to_string(), *it);
}
Self {
index_types,
default_index_type: X86SQLIndexType::Btree,
concurrent_index_build: true,
partial_index_support: true,
expression_index_support: true,
}
}
pub fn x86_page_utilization(&self, index_type: X86SQLIndexType) -> f64 {
match index_type {
X86SQLIndexType::Btree => 0.75,
X86SQLIndexType::Hash => 0.60,
X86SQLIndexType::Gin => 0.80,
X86SQLIndexType::Gist => 0.70,
X86SQLIndexType::SpGist => 0.65,
X86SQLIndexType::Brin => 0.95,
X86SQLIndexType::RTree => 0.70,
_ => 0.75,
}
}
}
#[derive(Debug, Clone)]
pub struct X86SQLTransactionSupport {
pub isolation_levels: Vec<X86SQLIsolationLevel>,
pub default_isolation: X86SQLIsolationLevel,
pub autocommit: bool,
pub savepoints_enabled: bool,
pub two_phase_commit: bool,
pub transaction_timeout_ms: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLIsolationLevel {
ReadUncommitted,
ReadCommitted,
RepeatableRead,
Serializable,
}
impl fmt::Display for X86SQLIsolationLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ReadUncommitted => write!(f, "READ UNCOMMITTED"),
Self::ReadCommitted => write!(f, "READ COMMITTED"),
Self::RepeatableRead => write!(f, "REPEATABLE READ"),
Self::Serializable => write!(f, "SERIALIZABLE"),
}
}
}
impl X86SQLTransactionSupport {
pub fn new() -> Self {
Self {
isolation_levels: vec![
X86SQLIsolationLevel::ReadUncommitted,
X86SQLIsolationLevel::ReadCommitted,
X86SQLIsolationLevel::RepeatableRead,
X86SQLIsolationLevel::Serializable,
],
default_isolation: X86SQLIsolationLevel::ReadCommitted,
autocommit: true,
savepoints_enabled: true,
two_phase_commit: false,
transaction_timeout_ms: 30000,
}
}
}
#[derive(Debug, Clone)]
pub enum X86SQLCompileError {
LexerError(String),
ParseError(String),
TypeError(String),
SemanticError(String),
Unsupported(String),
}
impl fmt::Display for X86SQLCompileError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::LexerError(s) => write!(f, "Lexer error: {}", s),
Self::ParseError(s) => write!(f, "Parse error: {}", s),
Self::TypeError(s) => write!(f, "Type error: {}", s),
Self::SemanticError(s) => write!(f, "Semantic error: {}", s),
Self::Unsupported(s) => write!(f, "Unsupported: {}", s),
}
}
}
#[derive(Debug, Clone)]
pub struct X86SQLiteSupport {
pub version: String,
pub api: X86SQLiteAPI,
pub data_types: X86SQLiteDataTypeMap,
pub storage: X86SQLiteStorage,
pub wal: X86SQLiteWAL,
pub optimizer: X86SQLiteOptimizer,
pub fts5_enabled: bool,
pub rtree_enabled: bool,
pub json1_enabled: bool,
pub geopoly_enabled: bool,
pub rstar_enabled: bool,
pub compile_flags: Vec<String>,
pub connections: HashMap<u64, X86SQLiteConnection>,
next_conn_id: u64,
}
#[derive(Debug, Clone)]
pub struct X86SQLiteAPI {
pub open_available: bool,
pub close_available: bool,
pub exec_available: bool,
pub prepare_available: bool,
pub bind_available: bool,
pub column_available: bool,
pub api_version: u32,
pub thread_safe: bool,
pub mutex_enabled: bool,
}
#[derive(Debug, Clone)]
pub struct X86SQLiteConnection {
pub id: u64,
pub filename: String,
pub open: bool,
pub read_only: bool,
pub last_error: Option<String>,
pub last_error_code: i32,
pub changes: i64,
pub last_insert_rowid: i64,
pub total_changes: i64,
pub interrupted: bool,
}
#[derive(Debug, Clone)]
pub struct X86SQLiteDataTypeMap {
pub type_affinity: HashMap<String, X86SQLiteAffinity>,
pub declared_to_affinity: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLiteAffinity {
Integer,
Real,
Text,
Blob,
Numeric,
None,
}
impl fmt::Display for X86SQLiteAffinity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Integer => write!(f, "INTEGER"),
Self::Real => write!(f, "REAL"),
Self::Text => write!(f, "TEXT"),
Self::Blob => write!(f, "BLOB"),
Self::Numeric => write!(f, "NUMERIC"),
Self::None => write!(f, "NONE"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86SQLiteStorage {
pub page_size: u32,
pub cache_size: i32,
pub max_page_count: u32,
pub mmap_enabled: bool,
pub mmap_size: i64,
pub journal_mode: String,
pub synchronous: String,
pub auto_vacuum: bool,
pub page_format: u32,
pub reserved_bytes: u32,
pub freelist_pages: u32,
pub schema_cookie: u32,
pub file_format: u32,
}
#[derive(Debug, Clone)]
pub struct X86SQLitePage {
pub page_number: u32,
pub page_type: X86SQLitePageType,
pub free_block_offset: u16,
pub cell_count: u16,
pub cell_content_offset: u16,
pub fragmented_free_bytes: u8,
pub right_child: Option<u32>,
pub cells: Vec<X86SQLiteCell>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLitePageType {
InteriorIndex = 0x02,
InteriorTable = 0x05,
LeafIndex = 0x0A,
LeafTable = 0x0D,
}
#[derive(Debug, Clone)]
pub struct X86SQLiteCell {
pub cell_type: X86SQLiteCellType,
pub left_child: Option<u32>,
pub row_id: Option<i64>,
pub payload: Vec<u8>,
pub overflow_pages: Vec<u32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLiteCellType {
TableLeaf,
TableInterior,
IndexLeaf,
IndexInterior,
}
#[derive(Debug, Clone)]
pub struct X86SQLiteFileHeader {
pub header_string: [u8; 16],
pub page_size: u32,
pub file_format_write_version: u8,
pub file_format_read_version: u8,
pub reserved_bytes: u8,
pub max_embedded_payload_fraction: u8,
pub min_embedded_payload_fraction: u8,
pub leaf_payload_fraction: u8,
pub file_change_counter: u32,
pub database_size_pages: u32,
pub first_freelist_trunk_page: u32,
pub freelist_page_count: u32,
pub schema_cookie: u32,
pub schema_format: u32,
pub default_cache_size: u32,
pub auto_vacuum: u32,
pub text_encoding: u32,
pub user_version: u32,
pub incremental_vacuum: u32,
pub application_id: u32,
pub version_valid_for: u32,
pub software_version: u32,
}
#[derive(Debug, Clone)]
pub struct X86SQLiteWAL {
pub enabled: bool,
pub checkpoint_mode: X86SQLiteCheckpointMode,
pub auto_checkpoint: u32,
pub wal_size_pages: u32,
pub last_checkpoint_frame: u64,
pub checkpoint_in_progress: bool,
pub frame_buffer: Vec<X86SQLiteWALFrame>,
pub max_wal_size: u32,
pub checksum: (u32, u32),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SQLiteCheckpointMode {
Passive,
Full,
Restart,
Truncate,
}
#[derive(Debug, Clone)]
pub struct X86SQLiteWALFrame {
pub frame_number: u32,
pub page_number: u32,
pub database_size: u32,
pub salt1: u32,
pub salt2: u32,
pub checksum1: u32,
pub checksum2: u32,
pub page_data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct X86SQLiteWALHeader {
pub magic: [u8; 4],
pub file_format: u32,
pub page_size: u32,
pub checkpoint_seq: u32,
pub salt1: u32,
pub salt2: u32,
pub checksum1: u32,
pub checksum2: u32,
}
#[derive(Debug, Clone)]
pub struct X86SQLiteOptimizer {
pub enabled: bool,
pub automatic_index: bool,
pub stat_tables: Vec<X86SQLiteStatTable>,
pub plan_cache: HashMap<String, X86SQLiteQueryPlan>,
pub cost_model: X86SQLiteCostModel,
pub covering_indexes: bool,
pub join_order_optimization: bool,
pub subquery_flattening: bool,
pub push_down_optimization: bool,
}
#[derive(Debug, Clone)]
pub struct X86SQLiteStatTable {
pub table_name: String,
pub index_name: Option<String>,
pub stat: String,
}
#[derive(Debug, Clone)]
pub struct X86SQLiteQueryPlan {
pub plan_id: u64,
pub sql: String,
pub plan_text: String,
pub estimated_cost: f64,
pub estimated_rows: i64,
pub uses_index: bool,
pub uses_temp_table: bool,
pub uses_filesort: bool,
pub scan_type: String,
}
#[derive(Debug, Clone)]
pub struct X86SQLiteCostModel {
pub sequential_page_cost: f64,
pub random_page_cost: f64,
pub cpu_tuple_cost: f64,
pub cpu_index_tuple_cost: f64,
pub cpu_operator_cost: f64,
pub cache_line_penalty: f64,
pub branch_mispredict_penalty: f64,
}
impl Default for X86SQLiteCostModel {
fn default() -> Self {
Self {
sequential_page_cost: 1.0,
random_page_cost: 4.0,
cpu_tuple_cost: 0.01,
cpu_index_tuple_cost: 0.005,
cpu_operator_cost: 0.0025,
cache_line_penalty: 0.05,
branch_mispredict_penalty: 0.1,
}
}
}
impl X86SQLiteSupport {
pub fn new() -> Self {
Self {
version: "3.44.0".to_string(),
api: X86SQLiteAPI {
open_available: true,
close_available: true,
exec_available: true,
prepare_available: true,
bind_available: true,
column_available: true,
api_version: 3004000,
thread_safe: true,
mutex_enabled: true,
},
data_types: X86SQLiteDataTypeMap::new(),
storage: X86SQLiteStorage {
page_size: X86_SQLITE_DEFAULT_PAGE_SIZE,
cache_size: X86_SQLITE_DEFAULT_CACHE_SIZE,
max_page_count: 1073741823,
mmap_enabled: true,
mmap_size: 0x7FFF0000,
journal_mode: X86_SQLITE_JOURNAL_MODE.to_string(),
synchronous: X86_SQLITE_SYNCHRONOUS.to_string(),
auto_vacuum: false,
page_format: 4,
reserved_bytes: 0,
freelist_pages: 0,
schema_cookie: 0,
file_format: 4,
},
wal: X86SQLiteWAL {
enabled: true,
checkpoint_mode: X86SQLiteCheckpointMode::Passive,
auto_checkpoint: X86_SQLITE_WAL_AUTOCHECKPOINT,
wal_size_pages: 0,
last_checkpoint_frame: 0,
checkpoint_in_progress: false,
frame_buffer: Vec::new(),
max_wal_size: 100000,
checksum: (0, 0),
},
optimizer: X86SQLiteOptimizer {
enabled: true,
automatic_index: true,
stat_tables: Vec::new(),
plan_cache: HashMap::new(),
cost_model: X86SQLiteCostModel::default(),
covering_indexes: true,
join_order_optimization: true,
subquery_flattening: true,
push_down_optimization: true,
},
fts5_enabled: true,
rtree_enabled: true,
json1_enabled: true,
geopoly_enabled: false,
rstar_enabled: false,
compile_flags: vec![
"SQLITE_ENABLE_FTS5".to_string(),
"SQLITE_ENABLE_RTREE".to_string(),
"SQLITE_ENABLE_JSON1".to_string(),
"SQLITE_ENABLE_COLUMN_METADATA".to_string(),
"SQLITE_ENABLE_DBSTAT_VTAB".to_string(),
"SQLITE_ENABLE_STMTVTAB".to_string(),
"SQLITE_ENABLE_STAT4".to_string(),
"SQLITE_THREADSAFE=1".to_string(),
"SQLITE_DEFAULT_CACHE_SIZE=-2000".to_string(),
"SQLITE_DEFAULT_WAL_SYNCHRONOUS=1".to_string(),
"SQLITE_USE_URI=1".to_string(),
"SQLITE_MAX_EXPR_DEPTH=1000".to_string(),
"SQLITE_MAX_COLUMN=2000".to_string(),
"SQLITE_MAX_COMPOUND_SELECT=500".to_string(),
"SQLITE_DQS=0".to_string(),
],
connections: HashMap::new(),
next_conn_id: 0,
}
}
pub fn validate(&self) -> bool {
self.api.open_available && self.storage.page_size >= 512 && self.storage.page_size <= 65536
}
pub fn open(&mut self, filename: &str) -> Result<u64, String> {
let id = self.next_conn_id;
self.next_conn_id += 1;
let conn = X86SQLiteConnection {
id,
filename: filename.to_string(),
open: true,
read_only: false,
last_error: None,
last_error_code: 0,
changes: 0,
last_insert_rowid: 0,
total_changes: 0,
interrupted: false,
};
self.connections.insert(id, conn);
Ok(id)
}
pub fn close(&mut self, conn_id: u64) -> Result<(), String> {
if let Some(conn) = self.connections.get_mut(&conn_id) {
conn.open = false;
}
self.connections.remove(&conn_id);
Ok(())
}
pub fn exec(&mut self, conn_id: u64, sql: &str) -> Result<Vec<Vec<String>>, String> {
let conn = self
.connections
.get(&conn_id)
.ok_or("Connection not found")?;
if !conn.open {
return Err("Connection is closed".to_string());
}
Ok(Vec::new())
}
pub fn prepare(&mut self, conn_id: u64, sql: &str) -> Result<X86SQLitePreparedStmt, String> {
let _conn = self
.connections
.get(&conn_id)
.ok_or("Connection not found")?;
let param_count = sql.matches('?').count();
Ok(X86SQLitePreparedStmt {
conn_id,
sql: sql.to_string(),
param_count,
is_explain: sql.to_uppercase().starts_with("EXPLAIN"),
})
}
pub fn explain_query_plan(&self, sql: &str) -> X86SQLiteQueryPlan {
X86SQLiteQueryPlan {
plan_id: 0,
sql: sql.to_string(),
plan_text: format!("SCAN TABLE (virtual) for: {}", sql),
estimated_cost: 1.0,
estimated_rows: 0,
uses_index: false,
uses_temp_table: false,
uses_filesort: false,
scan_type: "SCAN TABLE".to_string(),
}
}
pub fn wal_checkpoint(&mut self) -> Result<X86SQLiteCheckpointResult, String> {
if !self.wal.enabled {
return Err("WAL mode not enabled".to_string());
}
Ok(X86SQLiteCheckpointResult {
pages_written: self.wal.wal_size_pages,
pages_wal: self.wal.wal_size_pages,
pages_checkpointed: self.wal.wal_size_pages,
mode: self.wal.checkpoint_mode,
})
}
pub fn wal_rollback(&mut self) -> Result<(), String> {
if !self.wal.enabled {
return Err("WAL mode not enabled".to_string());
}
self.wal.wal_size_pages = 0;
self.wal.frame_buffer.clear();
Ok(())
}
pub fn parse_file_header(&self, data: &[u8]) -> Option<X86SQLiteFileHeader> {
if data.len() < 100 {
return None;
}
let mut header_string = [0u8; 16];
header_string.copy_from_slice(&data[0..16]);
let header = X86SQLiteFileHeader {
header_string,
page_size: u32::from_be_bytes([data[16], data[17], data[18], data[19]]),
file_format_write_version: data[18],
file_format_read_version: data[19],
reserved_bytes: data[20],
max_embedded_payload_fraction: data[21],
min_embedded_payload_fraction: data[22],
leaf_payload_fraction: data[23],
file_change_counter: u32::from_be_bytes([data[24], data[25], data[26], data[27]]),
database_size_pages: u32::from_be_bytes([data[28], data[29], data[30], data[31]]),
first_freelist_trunk_page: u32::from_be_bytes([data[32], data[33], data[34], data[35]]),
freelist_page_count: u32::from_be_bytes([data[36], data[37], data[38], data[39]]),
schema_cookie: u32::from_be_bytes([data[40], data[41], data[42], data[43]]),
schema_format: u32::from_be_bytes([data[44], data[45], data[46], data[47]]),
default_cache_size: u32::from_be_bytes([data[48], data[49], data[50], data[51]]),
auto_vacuum: u32::from_be_bytes([data[52], data[53], data[54], data[55]]),
text_encoding: u32::from_be_bytes([data[56], data[57], data[58], data[59]]),
user_version: u32::from_be_bytes([data[60], data[61], data[62], data[63]]),
incremental_vacuum: u32::from_be_bytes([data[64], data[65], data[66], data[67]]),
application_id: u32::from_be_bytes([data[68], data[69], data[70], data[71]]),
version_valid_for: u32::from_be_bytes([data[92], data[93], data[94], data[95]]),
software_version: u32::from_be_bytes([data[96], data[97], data[98], data[99]]),
};
Some(header)
}
}
#[derive(Debug, Clone)]
pub struct X86SQLitePreparedStmt {
pub conn_id: u64,
pub sql: String,
pub param_count: usize,
pub is_explain: bool,
}
#[derive(Debug, Clone)]
pub struct X86SQLiteCheckpointResult {
pub pages_written: u32,
pub pages_wal: u32,
pub pages_checkpointed: u32,
pub mode: X86SQLiteCheckpointMode,
}
impl X86SQLiteDataTypeMap {
pub fn new() -> Self {
let mut type_affinity = HashMap::new();
for t in &[
"INT",
"INTEGER",
"TINYINT",
"SMALLINT",
"MEDIUMINT",
"BIGINT",
"UNSIGNED BIG INT",
"INT2",
"INT8",
] {
type_affinity.insert(t.to_string(), X86SQLiteAffinity::Integer);
}
for t in &[
"CHARACTER",
"VARCHAR",
"VARYING CHARACTER",
"NCHAR",
"NATIVE CHARACTER",
"NVARCHAR",
"TEXT",
"CLOB",
] {
type_affinity.insert(t.to_string(), X86SQLiteAffinity::Text);
}
type_affinity.insert("".to_string(), X86SQLiteAffinity::None);
for t in &["REAL", "DOUBLE", "DOUBLE PRECISION", "FLOAT"] {
type_affinity.insert(t.to_string(), X86SQLiteAffinity::Real);
}
for t in &["NUMERIC", "DECIMAL", "BOOLEAN", "DATE", "DATETIME"] {
type_affinity.insert(t.to_string(), X86SQLiteAffinity::Numeric);
}
type_affinity.insert("BLOB".to_string(), X86SQLiteAffinity::Blob);
Self {
type_affinity,
declared_to_affinity: true,
}
}
pub fn affinity(&self, declared_type: &str) -> X86SQLiteAffinity {
let upper = declared_type.to_uppercase().trim().to_string();
if let Some(aff) = self.type_affinity.get(&upper) {
return *aff;
}
if upper.contains("INT") {
return X86SQLiteAffinity::Integer;
}
if upper.contains("CHAR") || upper.contains("CLOB") || upper.contains("TEXT") {
return X86SQLiteAffinity::Text;
}
if upper.contains("BLOB") {
return X86SQLiteAffinity::Blob;
}
if upper.contains("REAL") || upper.contains("FLOA") || upper.contains("DOUB") {
return X86SQLiteAffinity::Real;
}
X86SQLiteAffinity::Numeric
}
}
#[derive(Debug, Clone)]
pub struct X86PostgreSQLSupport {
pub version: String,
pub api: X86PostgreSQLAPI,
pub types: X86PostgreSQLTypes,
pub protocol: X86PostgreSQLProtocol,
pub connections: HashMap<String, X86PostgreSQLConnection>,
pub scram_auth: bool,
pub ssl_supported: bool,
pub default_port: u16,
pub compile_flags: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct X86PostgreSQLAPI {
pub connect_available: bool,
pub finish_available: bool,
pub exec_available: bool,
pub prepare_available: bool,
pub exec_prepared_available: bool,
pub copy_available: bool,
pub async_available: bool,
pub ssl_available: bool,
pub pq_version: u32,
}
#[derive(Debug, Clone)]
pub struct X86PostgreSQLConnection {
pub conn_string: String,
pub host: String,
pub port: u16,
pub dbname: String,
pub user: String,
pub password: Option<String>,
pub status: X86PostgreSQLConnStatus,
pub transaction_status: X86PostgreSQLTransStatus,
pub backend_pid: u32,
pub server_version: String,
pub protocol_version: u32,
pub ssl_in_use: bool,
pub last_error: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86PostgreSQLConnStatus {
Ok,
Bad,
Started,
Made,
AwaitingResponse,
AuthOk,
Setup,
SslStartup,
Negotiating,
CheckReady,
CheckDone,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86PostgreSQLTransStatus {
Idle,
Active,
InTransaction,
InError,
Unknown,
}
#[derive(Debug, Clone)]
pub struct X86PostgreSQLTypes {
pub type_oids: HashMap<String, u32>,
pub type_sizes: HashMap<u32, i16>,
pub type_names: HashMap<u32, String>,
}
#[allow(non_upper_case_globals)]
pub mod pg_oids {
pub const BOOL_OID: u32 = 16;
pub const BYTEA_OID: u32 = 17;
pub const CHAR_OID: u32 = 18;
pub const NAME_OID: u32 = 19;
pub const INT8_OID: u32 = 20;
pub const INT2_OID: u32 = 21;
pub const INT4_OID: u32 = 23;
pub const TEXT_OID: u32 = 25;
pub const OID_OID: u32 = 26;
pub const FLOAT4_OID: u32 = 700;
pub const FLOAT8_OID: u32 = 701;
pub const VARCHAR_OID: u32 = 1043;
pub const DATE_OID: u32 = 1082;
pub const TIME_OID: u32 = 1083;
pub const TIMESTAMP_OID: u32 = 1114;
pub const TIMESTAMPTZ_OID: u32 = 1184;
pub const INTERVAL_OID: u32 = 1186;
pub const NUMERIC_OID: u32 = 1700;
pub const UUID_OID: u32 = 2950;
pub const JSON_OID: u32 = 114;
pub const JSONB_OID: u32 = 3802;
pub const INT2VECTOR_OID: u32 = 22;
pub const OIDVECTOR_OID: u32 = 30;
}
#[derive(Debug, Clone)]
pub struct X86PostgreSQLProtocol {
pub major_version: u32,
pub minor_version: u32,
pub message_buffer: VecDeque<X86PGMessage>,
pub state: X86PGProtocolState,
pub startup_parameters: HashMap<String, String>,
pub backend_key_data: Option<(u32, u32)>,
pub parameter_status: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub enum X86PGMessage {
Startup {
protocol_version: u32,
parameters: HashMap<String, String>,
},
Query {
query: String,
},
Parse {
name: String,
query: String,
param_types: Vec<u32>,
},
Bind {
portal: String,
statement: String,
param_formats: Vec<i16>,
param_values: Vec<Option<Vec<u8>>>,
result_formats: Vec<i16>,
},
Execute {
portal: String,
max_rows: u32,
},
Describe {
variant: X86PGDescribeVariant,
name: String,
},
Close {
variant: X86PGCloseVariant,
name: String,
},
Flush,
Sync,
Terminate,
CopyData(Vec<u8>),
CopyDone,
CopyFail(String),
Authentication(u32),
ParameterStatus {
name: String,
value: String,
},
BackendKeyData {
pid: u32,
secret: u32,
},
ReadyForQuery(u8),
RowDescription(Vec<X86PGColumnDesc>),
DataRow(Vec<Option<String>>),
CommandComplete(String),
EmptyQueryResponse,
ErrorResponse(Vec<(u8, String)>),
NoticeResponse(Vec<(u8, String)>),
ParseComplete,
BindComplete,
CloseComplete,
NotificationResponse {
pid: u32,
channel: String,
payload: String,
},
CopyInResponse(u8, Vec<i16>),
CopyOutResponse(u8, Vec<i16>),
CopyBothResponse(u8, Vec<i16>),
}
#[derive(Debug, Clone)]
pub struct X86PGColumnDesc {
pub name: String,
pub table_oid: u32,
pub col_attr: i16,
pub type_oid: u32,
pub type_size: i16,
pub type_modifier: i32,
pub format_code: i16,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86PGDescribeVariant {
Statement,
Portal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86PGCloseVariant {
Statement,
Portal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86PGProtocolState {
Startup,
Authentication,
Ready,
Idle,
Busy,
CopyIn,
CopyOut,
CopyBoth,
Error,
}
#[derive(Debug, Clone)]
pub struct X86PostgreSQLCopyFormat {
pub binary_mode: bool,
pub header: X86PGCopyHeader,
pub tuples: Vec<X86PGCopyTuple>,
pub trailer: Option<X86PGCopyTrailer>,
}
#[derive(Debug, Clone)]
pub struct X86PGCopyHeader {
pub signature: [u8; 11],
pub flags: u32,
pub header_extension_length: u32,
pub header_extension: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct X86PGCopyTuple {
pub field_count: i16,
pub fields: Vec<X86PGCopyField>,
}
#[derive(Debug, Clone)]
pub struct X86PGCopyField {
pub length: i32,
pub data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct X86PGCopyTrailer {
pub marker: i16,
}
impl X86PostgreSQLSupport {
pub fn new() -> Self {
let mut types = X86PostgreSQLTypes {
type_oids: HashMap::new(),
type_sizes: HashMap::new(),
type_names: HashMap::new(),
};
let oid_map = [
("bool", pg_oids::BOOL_OID, 1i16),
("bytea", pg_oids::BYTEA_OID, -1),
("char", pg_oids::CHAR_OID, 1),
("int8", pg_oids::INT8_OID, 8),
("int2", pg_oids::INT2_OID, 2),
("int4", pg_oids::INT4_OID, 4),
("text", pg_oids::TEXT_OID, -1),
("oid", pg_oids::OID_OID, 4),
("float4", pg_oids::FLOAT4_OID, 4),
("float8", pg_oids::FLOAT8_OID, 8),
("varchar", pg_oids::VARCHAR_OID, -1),
("date", pg_oids::DATE_OID, 4),
("time", pg_oids::TIME_OID, 8),
("timestamp", pg_oids::TIMESTAMP_OID, 8),
("timestamptz", pg_oids::TIMESTAMPTZ_OID, 8),
("interval", pg_oids::INTERVAL_OID, 16),
("numeric", pg_oids::NUMERIC_OID, -1),
("uuid", pg_oids::UUID_OID, 16),
("json", pg_oids::JSON_OID, -1),
("jsonb", pg_oids::JSONB_OID, -1),
];
for (name, oid, size) in &oid_map {
types.type_oids.insert(name.to_string(), *oid);
types.type_sizes.insert(*oid, *size);
types.type_names.insert(*oid, name.to_string());
}
Self {
version: "16.0".to_string(),
api: X86PostgreSQLAPI {
connect_available: true,
finish_available: true,
exec_available: true,
prepare_available: true,
exec_prepared_available: true,
copy_available: true,
async_available: true,
ssl_available: true,
pq_version: 160000,
},
types,
protocol: X86PostgreSQLProtocol {
major_version: 3,
minor_version: 0,
message_buffer: VecDeque::new(),
state: X86PGProtocolState::Startup,
startup_parameters: HashMap::new(),
backend_key_data: None,
parameter_status: HashMap::new(),
},
connections: HashMap::new(),
scram_auth: true,
ssl_supported: true,
default_port: X86_PG_DEFAULT_PORT,
compile_flags: vec!["-I/usr/include/postgresql".to_string(), "-lpq".to_string()],
}
}
pub fn validate(&self) -> bool {
self.api.connect_available && self.protocol.major_version == 3
}
pub fn build_conn_string(
&self,
host: &str,
port: u16,
dbname: &str,
user: &str,
password: Option<&str>,
) -> String {
let mut params = vec![
format!("host={}", host),
format!("port={}", port),
format!("dbname={}", dbname),
format!("user={}", user),
];
if let Some(pw) = password {
params.push(format!("password={}", pw));
}
params.join(" ")
}
pub fn connect(&mut self, conn_string: &str) -> Result<String, String> {
let conn = X86PostgreSQLConnection {
conn_string: conn_string.to_string(),
host: "localhost".to_string(),
port: self.default_port,
dbname: "postgres".to_string(),
user: "postgres".to_string(),
password: None,
status: X86PostgreSQLConnStatus::Ok,
transaction_status: X86PostgreSQLTransStatus::Idle,
backend_pid: 0,
server_version: self.version.clone(),
protocol_version: 3 << 16,
ssl_in_use: false,
last_error: None,
};
let key = conn_string.to_string();
self.connections.insert(key.clone(), conn);
Ok(key)
}
pub fn disconnect(&mut self, conn_key: &str) {
if let Some(conn) = self.connections.get_mut(conn_key) {
conn.status = X86PostgreSQLConnStatus::Bad;
}
self.connections.remove(conn_key);
}
pub fn query(&self, conn_key: &str, query: &str) -> Result<Vec<Vec<String>>, String> {
let _conn = self
.connections
.get(conn_key)
.ok_or("Connection not found")?;
let _msg = X86PGMessage::Query {
query: query.to_string(),
};
Ok(Vec::new())
}
pub fn prepare(
&self,
conn_key: &str,
stmt_name: &str,
query: &str,
param_types: &[u32],
) -> Result<(), String> {
let _conn = self
.connections
.get(conn_key)
.ok_or("Connection not found")?;
let _msg = X86PGMessage::Parse {
name: stmt_name.to_string(),
query: query.to_string(),
param_types: param_types.to_vec(),
};
Ok(())
}
pub fn exec_prepared(
&self,
conn_key: &str,
stmt_name: &str,
param_values: &[Option<Vec<u8>>],
) -> Result<Vec<Vec<String>>, String> {
let _conn = self
.connections
.get(conn_key)
.ok_or("Connection not found")?;
let _msg = X86PGMessage::Bind {
portal: String::new(),
statement: stmt_name.to_string(),
param_formats: vec![0; param_values.len()],
param_values: param_values.to_vec(),
result_formats: vec![0],
};
Ok(Vec::new())
}
pub fn copy_binary_format(&self, tuples: Vec<Vec<Option<Vec<u8>>>>) -> X86PostgreSQLCopyFormat {
let header = X86PGCopyHeader {
signature: *b"PGCOPY\n\xff\r\n\0",
flags: 0,
header_extension_length: 0,
header_extension: Vec::new(),
};
let copy_tuples: Vec<X86PGCopyTuple> = tuples
.into_iter()
.map(|t| {
let fields: Vec<X86PGCopyField> = t
.into_iter()
.map(|f| match f {
Some(data) => X86PGCopyField {
length: data.len() as i32,
data,
},
None => X86PGCopyField {
length: -1,
data: Vec::new(),
},
})
.collect();
X86PGCopyTuple {
field_count: fields.len() as i16,
fields,
}
})
.collect();
X86PostgreSQLCopyFormat {
binary_mode: true,
header,
tuples: copy_tuples,
trailer: Some(X86PGCopyTrailer { marker: -1 }),
}
}
}
#[derive(Debug, Clone)]
pub struct X86MySQLSupport {
pub version: String,
pub api: X86MySQLAPI,
pub types: X86MySQLTypes,
pub prepared: X86MySQLPreparedStmtSupport,
pub connections: HashMap<String, X86MySQLConnection>,
pub ssl_supported: bool,
pub compression_supported: bool,
pub default_port: u16,
pub compile_flags: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct X86MySQLAPI {
pub init_available: bool,
pub connect_available: bool,
pub query_available: bool,
pub store_result_available: bool,
pub fetch_row_available: bool,
pub free_result_available: bool,
pub stmt_init_available: bool,
pub stmt_prepare_available: bool,
pub stmt_bind_param_available: bool,
pub stmt_execute_available: bool,
pub stmt_bind_result_available: bool,
pub stmt_fetch_available: bool,
pub client_version: u32,
}
#[derive(Debug, Clone)]
pub struct X86MySQLConnection {
pub host: String,
pub port: u16,
pub user: String,
pub password: Option<String>,
pub database: Option<String>,
pub unix_socket: Option<String>,
pub client_flag: u64,
pub status: X86MySQLConnStatus,
pub server_version: String,
pub last_error: Option<String>,
pub last_errno: u32,
pub affected_rows: u64,
pub insert_id: u64,
pub warning_count: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86MySQLConnStatus {
Connected,
Disconnected,
Error,
Connecting,
}
#[derive(Debug, Clone)]
pub struct X86MySQLTypes {
pub type_codes: HashMap<String, u8>,
pub type_names: HashMap<u8, String>,
pub type_sizes: HashMap<u8, usize>,
}
#[allow(non_upper_case_globals)]
pub mod mysql_field_types {
pub const MYSQL_TYPE_DECIMAL: u8 = 0;
pub const MYSQL_TYPE_TINY: u8 = 1;
pub const MYSQL_TYPE_SHORT: u8 = 2;
pub const MYSQL_TYPE_LONG: u8 = 3;
pub const MYSQL_TYPE_FLOAT: u8 = 4;
pub const MYSQL_TYPE_DOUBLE: u8 = 5;
pub const MYSQL_TYPE_NULL: u8 = 6;
pub const MYSQL_TYPE_TIMESTAMP: u8 = 7;
pub const MYSQL_TYPE_LONGLONG: u8 = 8;
pub const MYSQL_TYPE_INT24: u8 = 9;
pub const MYSQL_TYPE_DATE: u8 = 10;
pub const MYSQL_TYPE_TIME: u8 = 11;
pub const MYSQL_TYPE_DATETIME: u8 = 12;
pub const MYSQL_TYPE_YEAR: u8 = 13;
pub const MYSQL_TYPE_NEWDATE: u8 = 14;
pub const MYSQL_TYPE_VARCHAR: u8 = 15;
pub const MYSQL_TYPE_BIT: u8 = 16;
pub const MYSQL_TYPE_JSON: u8 = 245;
pub const MYSQL_TYPE_NEWDECIMAL: u8 = 246;
pub const MYSQL_TYPE_ENUM: u8 = 247;
pub const MYSQL_TYPE_SET: u8 = 248;
pub const MYSQL_TYPE_TINY_BLOB: u8 = 249;
pub const MYSQL_TYPE_MEDIUM_BLOB: u8 = 250;
pub const MYSQL_TYPE_LONG_BLOB: u8 = 251;
pub const MYSQL_TYPE_BLOB: u8 = 252;
pub const MYSQL_TYPE_VAR_STRING: u8 = 253;
pub const MYSQL_TYPE_STRING: u8 = 254;
pub const MYSQL_TYPE_GEOMETRY: u8 = 255;
}
#[derive(Debug, Clone)]
pub struct X86MySQLPreparedStmt {
pub stmt_id: u32,
pub sql: String,
pub param_count: usize,
pub param_bindings: Vec<X86MySQLBind>,
pub result_bindings: Vec<X86MySQLBind>,
pub field_count: usize,
pub warning_count: u32,
}
#[derive(Debug, Clone)]
pub struct X86MySQLBind {
pub buffer_type: u8,
pub buffer: Vec<u8>,
pub buffer_length: usize,
pub is_null: bool,
pub is_unsigned: bool,
pub error: bool,
}
#[derive(Debug, Clone)]
pub struct X86MySQLPreparedStmtSupport {
pub max_prepared_stmt_count: u32,
pub server_prepare: bool,
pub cursor_type: u32,
pub update_max_length: bool,
}
impl X86MySQLSupport {
pub fn new() -> Self {
let mut types = X86MySQLTypes {
type_codes: HashMap::new(),
type_names: HashMap::new(),
type_sizes: HashMap::new(),
};
let type_map = [
("TINYINT", mysql_field_types::MYSQL_TYPE_TINY, 1usize),
("SMALLINT", mysql_field_types::MYSQL_TYPE_SHORT, 2),
("MEDIUMINT", mysql_field_types::MYSQL_TYPE_INT24, 3),
("INT", mysql_field_types::MYSQL_TYPE_LONG, 4),
("INTEGER", mysql_field_types::MYSQL_TYPE_LONG, 4),
("BIGINT", mysql_field_types::MYSQL_TYPE_LONGLONG, 8),
("FLOAT", mysql_field_types::MYSQL_TYPE_FLOAT, 4),
("DOUBLE", mysql_field_types::MYSQL_TYPE_DOUBLE, 8),
("DECIMAL", mysql_field_types::MYSQL_TYPE_NEWDECIMAL, 0),
("DATE", mysql_field_types::MYSQL_TYPE_DATE, 3),
("TIME", mysql_field_types::MYSQL_TYPE_TIME, 3),
("DATETIME", mysql_field_types::MYSQL_TYPE_DATETIME, 8),
("TIMESTAMP", mysql_field_types::MYSQL_TYPE_TIMESTAMP, 4),
("YEAR", mysql_field_types::MYSQL_TYPE_YEAR, 1),
("VARCHAR", mysql_field_types::MYSQL_TYPE_VARCHAR, 0),
("CHAR", mysql_field_types::MYSQL_TYPE_STRING, 0),
("TEXT", mysql_field_types::MYSQL_TYPE_BLOB, 0),
("TINYTEXT", mysql_field_types::MYSQL_TYPE_TINY_BLOB, 0),
("MEDIUMTEXT", mysql_field_types::MYSQL_TYPE_MEDIUM_BLOB, 0),
("LONGTEXT", mysql_field_types::MYSQL_TYPE_LONG_BLOB, 0),
("BLOB", mysql_field_types::MYSQL_TYPE_BLOB, 0),
("TINYBLOB", mysql_field_types::MYSQL_TYPE_TINY_BLOB, 0),
("MEDIUMBLOB", mysql_field_types::MYSQL_TYPE_MEDIUM_BLOB, 0),
("LONGBLOB", mysql_field_types::MYSQL_TYPE_LONG_BLOB, 0),
("ENUM", mysql_field_types::MYSQL_TYPE_ENUM, 0),
("SET", mysql_field_types::MYSQL_TYPE_SET, 0),
("JSON", mysql_field_types::MYSQL_TYPE_JSON, 0),
("GEOMETRY", mysql_field_types::MYSQL_TYPE_GEOMETRY, 0),
("BIT", mysql_field_types::MYSQL_TYPE_BIT, 0),
];
for (name, code, size) in &type_map {
types.type_codes.insert(name.to_string(), *code);
types.type_names.insert(*code, name.to_string());
if *size > 0 {
types.type_sizes.insert(*code, *size);
}
}
Self {
version: "8.0.35".to_string(),
api: X86MySQLAPI {
init_available: true,
connect_available: true,
query_available: true,
store_result_available: true,
fetch_row_available: true,
free_result_available: true,
stmt_init_available: true,
stmt_prepare_available: true,
stmt_bind_param_available: true,
stmt_execute_available: true,
stmt_bind_result_available: true,
stmt_fetch_available: true,
client_version: 80035,
},
types,
prepared: X86MySQLPreparedStmtSupport {
max_prepared_stmt_count: 16382,
server_prepare: true,
cursor_type: 0,
update_max_length: false,
},
connections: HashMap::new(),
ssl_supported: true,
compression_supported: true,
default_port: X86_MYSQL_DEFAULT_PORT,
compile_flags: vec![
"-I/usr/include/mysql".to_string(),
"-lmysqlclient".to_string(),
],
}
}
pub fn validate(&self) -> bool {
self.api.connect_available && self.api.query_available
}
pub fn connect(
&mut self,
host: &str,
port: u16,
user: &str,
password: Option<&str>,
database: Option<&str>,
) -> Result<String, String> {
let key = format!("{}:{}:{}", host, port, user);
let conn = X86MySQLConnection {
host: host.to_string(),
port,
user: user.to_string(),
password: password.map(|s| s.to_string()),
database: database.map(|s| s.to_string()),
unix_socket: None,
client_flag: 0,
status: X86MySQLConnStatus::Connected,
server_version: self.version.clone(),
last_error: None,
last_errno: 0,
affected_rows: 0,
insert_id: 0,
warning_count: 0,
};
self.connections.insert(key.clone(), conn);
Ok(key)
}
pub fn query(&self, conn_key: &str, query: &str) -> Result<Vec<Vec<String>>, String> {
let _conn = self
.connections
.get(conn_key)
.ok_or("Connection not found")?;
Ok(Vec::new())
}
pub fn stmt_prepare(&self, conn_key: &str, sql: &str) -> Result<X86MySQLPreparedStmt, String> {
let _conn = self
.connections
.get(conn_key)
.ok_or("Connection not found")?;
let param_count = sql.matches('?').count();
Ok(X86MySQLPreparedStmt {
stmt_id: 0,
sql: sql.to_string(),
param_count,
param_bindings: Vec::new(),
result_bindings: Vec::new(),
field_count: 0,
warning_count: 0,
})
}
pub fn stmt_bind_param(
stmt: &mut X86MySQLPreparedStmt,
param_index: usize,
buffer_type: u8,
value: &[u8],
is_unsigned: bool,
) -> Result<(), String> {
if param_index >= stmt.param_count {
return Err(format!(
"Parameter index {} out of range (max {})",
param_index, stmt.param_count
));
}
while stmt.param_bindings.len() <= param_index {
stmt.param_bindings.push(X86MySQLBind {
buffer_type: 0,
buffer: Vec::new(),
buffer_length: 0,
is_null: false,
is_unsigned: false,
error: false,
});
}
stmt.param_bindings[param_index] = X86MySQLBind {
buffer_type,
buffer: value.to_vec(),
buffer_length: value.len(),
is_null: false,
is_unsigned,
error: false,
};
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct X86RedisSupport {
pub resp_version: X86RedisRespVersion,
pub protocol: X86RedisProtocol,
pub commands: HashMap<String, X86RedisCommand>,
pub store: HashMap<String, X86RedisValue>,
pub channels: HashMap<String, Vec<String>>,
pub streams: HashMap<String, VecDeque<X86RedisStreamEntry>>,
pub sorted_sets: HashMap<String, Vec<(f64, Vec<String>)>>,
pub connections: HashMap<String, X86RedisConnection>,
pub default_port: u16,
pub compile_flags: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86RedisRespVersion {
RESP2,
RESP3,
}
#[derive(Debug, Clone)]
pub struct X86RedisProtocol {
pub version: X86RedisRespVersion,
pub buffer: Vec<u8>,
pub position: usize,
pub parsing_state: X86RedisParsingState,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86RedisParsingState {
Idle,
ReadingType,
ReadingLength,
ReadingData,
Error,
}
#[derive(Debug, Clone)]
pub enum X86RedisValue {
SimpleString(String),
Error(String),
Integer(i64),
BulkString(Option<Vec<u8>>),
Array(Vec<X86RedisValue>),
Null,
Boolean(bool),
Double(f64),
BigNumber(String),
VerbatimString {
encoding: String,
data: String,
},
Map(Vec<(X86RedisValue, X86RedisValue)>),
Set(Vec<X86RedisValue>),
Attribute {
attributes: HashMap<String, X86RedisValue>,
value: Box<X86RedisValue>,
},
Push(Vec<X86RedisValue>),
}
#[derive(Debug, Clone)]
pub struct X86RedisConnection {
pub id: String,
pub host: String,
pub port: u16,
pub database: u32,
pub authenticated: bool,
pub subscribed_channels: Vec<String>,
pub in_transaction: bool,
pub transaction_queue: Vec<X86RedisCommand>,
pub last_error: Option<String>,
}
#[derive(Debug, Clone)]
pub struct X86RedisCommand {
pub name: String,
pub arity: i32,
pub flags: Vec<String>,
pub first_key: i32,
pub last_key: i32,
pub key_step: i32,
pub handler: Option<String>,
}
#[derive(Debug, Clone)]
pub struct X86RedisStreamEntry {
pub id: String,
pub fields: Vec<(String, String)>,
}
#[derive(Debug, Clone)]
pub struct X86RedisExpireEntry {
pub key: String,
pub expires_at_ms: u64,
}
impl X86RedisSupport {
pub fn new() -> Self {
let mut commands = HashMap::new();
let cmd_defs = [
("GET", 2, vec!["readonly", "fast"]),
("SET", -3, vec!["write", "denyoom"]),
("DEL", -2, vec!["write"]),
("EXISTS", -2, vec!["readonly", "fast"]),
("EXPIRE", 3, vec!["write", "fast"]),
("TTL", 2, vec!["readonly", "fast"]),
("INCR", 2, vec!["write", "denyoom", "fast"]),
("DECR", 2, vec!["write", "denyoom", "fast"]),
("LPUSH", -3, vec!["write", "denyoom", "fast"]),
("RPUSH", -3, vec!["write", "denyoom", "fast"]),
("LPOP", -2, vec!["write", "fast"]),
("RPOP", -2, vec!["write", "fast"]),
("LLEN", 2, vec!["readonly", "fast"]),
("LRANGE", 4, vec!["readonly"]),
("SADD", -3, vec!["write", "denyoom", "fast"]),
("SMEMBERS", 2, vec!["readonly"]),
("SISMEMBER", 3, vec!["readonly", "fast"]),
("HSET", -4, vec!["write", "denyoom", "fast"]),
("HGET", 3, vec!["readonly", "fast"]),
("HGETALL", 2, vec!["readonly"]),
("ZADD", -4, vec!["write", "denyoom", "fast"]),
("ZRANGE", -4, vec!["readonly"]),
("PUBLISH", 3, vec!["pubsub", "loading", "stale", "fast"]),
(
"SUBSCRIBE",
-2,
vec!["pubsub", "noscript", "loading", "stale"],
),
];
for (name, arity, flags) in &cmd_defs {
commands.insert(
name.to_string(),
X86RedisCommand {
name: name.to_string(),
arity: *arity,
flags: flags.iter().map(|s| s.to_string()).collect(),
first_key: 1,
last_key: if *arity < 0 { -1 } else { 1 },
key_step: 1,
handler: None,
},
);
}
Self {
resp_version: X86RedisRespVersion::RESP2,
protocol: X86RedisProtocol {
version: X86RedisRespVersion::RESP2,
buffer: Vec::new(),
position: 0,
parsing_state: X86RedisParsingState::Idle,
},
commands,
store: HashMap::new(),
channels: HashMap::new(),
streams: HashMap::new(),
sorted_sets: HashMap::new(),
connections: HashMap::new(),
default_port: X86_REDIS_DEFAULT_PORT,
compile_flags: vec!["-lhiredis".to_string()],
}
}
pub fn validate(&self) -> bool {
!self.commands.is_empty()
}
pub fn serialize_resp2(&self, value: &X86RedisValue) -> Vec<u8> {
let mut buf = Vec::new();
match value {
X86RedisValue::SimpleString(s) => {
buf.push(b'+');
buf.extend_from_slice(s.as_bytes());
buf.extend_from_slice(b"\r\n");
}
X86RedisValue::Error(s) => {
buf.push(b'-');
buf.extend_from_slice(s.as_bytes());
buf.extend_from_slice(b"\r\n");
}
X86RedisValue::Integer(i) => {
buf.push(b':');
buf.extend_from_slice(i.to_string().as_bytes());
buf.extend_from_slice(b"\r\n");
}
X86RedisValue::BulkString(Some(data)) => {
buf.push(b'$');
buf.extend_from_slice(data.len().to_string().as_bytes());
buf.extend_from_slice(b"\r\n");
buf.extend_from_slice(data);
buf.extend_from_slice(b"\r\n");
}
X86RedisValue::BulkString(None) => {
buf.extend_from_slice(b"$-1\r\n");
}
X86RedisValue::Array(items) => {
buf.push(b'*');
buf.extend_from_slice(items.len().to_string().as_bytes());
buf.extend_from_slice(b"\r\n");
for item in items {
buf.extend_from_slice(&self.serialize_resp2(item));
}
}
X86RedisValue::Null => {
buf.extend_from_slice(b"_\r\n");
}
X86RedisValue::Boolean(b) => {
buf.push(b'#');
buf.push(if *b { b't' } else { b'f' });
buf.extend_from_slice(b"\r\n");
}
X86RedisValue::Double(d) => {
buf.push(b',');
buf.extend_from_slice(format!("{:.17}", d).as_bytes());
buf.extend_from_slice(b"\r\n");
}
X86RedisValue::BigNumber(s) => {
buf.push(b'(');
buf.extend_from_slice(s.as_bytes());
buf.extend_from_slice(b"\r\n");
}
_ => {
buf.extend_from_slice(b"$-1\r\n");
}
}
buf
}
pub fn parse_resp2(&self, data: &[u8]) -> Result<X86RedisValue, String> {
if data.is_empty() {
return Err("Empty data".to_string());
}
match data[0] {
b'+' => {
let s = std::str::from_utf8(&data[1..data.len() - 2]).map_err(|e| e.to_string())?;
Ok(X86RedisValue::SimpleString(s.to_string()))
}
b'-' => {
let s = std::str::from_utf8(&data[1..data.len() - 2]).map_err(|e| e.to_string())?;
Ok(X86RedisValue::Error(s.to_string()))
}
b':' => {
let s = std::str::from_utf8(&data[1..data.len() - 2]).map_err(|e| e.to_string())?;
Ok(X86RedisValue::Integer(
s.parse::<i64>().map_err(|e| e.to_string())?,
))
}
b'$' => {
let crlf1 = data
.iter()
.position(|&b| b == b'\r')
.ok_or("Malformed bulk string")?;
let len_str = std::str::from_utf8(&data[1..crlf1]).map_err(|e| e.to_string())?;
let len: i32 = len_str.parse::<i32>().map_err(|e| e.to_string())?;
if len < 0 {
Ok(X86RedisValue::BulkString(None))
} else {
let start = crlf1 + 2;
let end = start + len as usize;
Ok(X86RedisValue::BulkString(Some(data[start..end].to_vec())))
}
}
b'*' => {
let crlf1 = data
.iter()
.position(|&b| b == b'\r')
.ok_or("Malformed array")?;
let count_str = std::str::from_utf8(&data[1..crlf1]).map_err(|e| e.to_string())?;
let count: i32 = count_str.parse::<i32>().map_err(|e| e.to_string())?;
let mut items = Vec::new();
let mut pos = crlf1 + 2;
for _ in 0..count {
if pos >= data.len() {
break;
}
let mut depth = 1;
let mut el_pos = pos;
while depth > 0 && el_pos < data.len() {
match data[el_pos] {
b'+' | b'-' | b':' => {
while el_pos < data.len() && data[el_pos] != b'\r' {
el_pos += 1;
}
el_pos += 2;
depth -= 1;
}
b'$' => {
let crlf = data[el_pos..]
.iter()
.position(|&b| b == b'\r')
.map(|p| el_pos + p)
.unwrap_or(data.len());
let s =
std::str::from_utf8(&data[el_pos + 1..crlf]).unwrap_or("-1");
let l: i32 = s.parse().unwrap_or(-1);
if l < 0 {
el_pos = crlf + 2;
} else {
el_pos = crlf + 2 + l as usize + 2;
}
depth -= 1;
}
b'*' => {
depth += 1;
el_pos += 1;
}
_ => {
el_pos += 1;
}
}
}
let element = self.parse_resp2(&data[pos..el_pos])?;
items.push(element);
pos = el_pos;
}
Ok(X86RedisValue::Array(items))
}
_ => Err(format!("Unknown RESP type byte: {}", data[0] as char)),
}
}
pub fn execute(
&mut self,
conn_key: &str,
command: &str,
args: &[String],
) -> Result<X86RedisValue, String> {
let cmd_upper = command.to_uppercase();
let _conn = self
.connections
.get(conn_key)
.ok_or("Connection not found")?;
match cmd_upper.as_str() {
"GET" => self.cmd_get(args),
"SET" => self.cmd_set(args),
"DEL" => self.cmd_del(args),
"EXISTS" => self.cmd_exists(args),
"EXPIRE" => self.cmd_expire(args),
"TTL" => self.cmd_ttl(args),
"INCR" => self.cmd_incr(args),
"DECR" => self.cmd_decr(args),
"LPUSH" => self.cmd_lpush(args),
"RPUSH" => self.cmd_rpush(args),
"LPOP" => self.cmd_lpop(args),
"RPOP" => self.cmd_rpop(args),
"LLEN" => self.cmd_llen(args),
"LRANGE" => self.cmd_lrange(args),
"SADD" => self.cmd_sadd(args),
"SMEMBERS" => self.cmd_smembers(args),
"SISMEMBER" => self.cmd_sismember(args),
"HSET" => self.cmd_hset(args),
"HGET" => self.cmd_hget(args),
"HGETALL" => self.cmd_hgetall(args),
"ZADD" => self.cmd_zadd(args),
"ZRANGE" => self.cmd_zrange(args),
"PUBLISH" => self.cmd_publish(args),
"SUBSCRIBE" => self.cmd_subscribe(conn_key, args),
_ => Err(format!("Unknown command: {}", command)),
}
}
fn cmd_get(&self, args: &[String]) -> Result<X86RedisValue, String> {
if args.is_empty() {
return Err("GET requires 1 argument".to_string());
}
match self.store.get(&args[0]) {
Some(val) => Ok(val.clone()),
None => Ok(X86RedisValue::Null),
}
}
fn cmd_set(&mut self, args: &[String]) -> Result<X86RedisValue, String> {
if args.len() < 2 {
return Err("SET requires at least 2 arguments".to_string());
}
self.store.insert(
args[0].clone(),
X86RedisValue::BulkString(Some(args[1].as_bytes().to_vec())),
);
Ok(X86RedisValue::SimpleString("OK".to_string()))
}
fn cmd_del(&mut self, args: &[String]) -> Result<X86RedisValue, String> {
let mut count = 0i64;
for key in args {
if self.store.remove(key).is_some() {
count += 1;
}
}
Ok(X86RedisValue::Integer(count))
}
fn cmd_exists(&self, args: &[String]) -> Result<X86RedisValue, String> {
let mut count = 0i64;
for key in args {
if self.store.contains_key(key) {
count += 1;
}
}
Ok(X86RedisValue::Integer(count))
}
fn cmd_expire(&self, _args: &[String]) -> Result<X86RedisValue, String> {
Ok(X86RedisValue::Integer(1))
}
fn cmd_ttl(&self, args: &[String]) -> Result<X86RedisValue, String> {
if args.is_empty() {
return Err("TTL requires 1 argument".to_string());
}
if self.store.contains_key(&args[0]) {
Ok(X86RedisValue::Integer(-1)) } else {
Ok(X86RedisValue::Integer(-2))
}
}
fn cmd_incr(&mut self, args: &[String]) -> Result<X86RedisValue, String> {
if args.is_empty() {
return Err("INCR requires 1 argument".to_string());
}
let entry = self.store.entry(args[0].clone());
let val = entry.or_insert_with(|| X86RedisValue::BulkString(Some(b"0".to_vec())));
match val {
X86RedisValue::BulkString(Some(data)) => {
let s = std::str::from_utf8(data).unwrap_or("0");
let n: i64 = s.parse().unwrap_or(0);
let new_n = n + 1;
*data = new_n.to_string().into_bytes();
Ok(X86RedisValue::Integer(new_n))
}
_ => Err("Value is not an integer".to_string()),
}
}
fn cmd_decr(&mut self, args: &[String]) -> Result<X86RedisValue, String> {
if args.is_empty() {
return Err("DECR requires 1 argument".to_string());
}
let entry = self.store.entry(args[0].clone());
let val = entry.or_insert_with(|| X86RedisValue::BulkString(Some(b"0".to_vec())));
match val {
X86RedisValue::BulkString(Some(data)) => {
let s = std::str::from_utf8(data).unwrap_or("0");
let n: i64 = s.parse().unwrap_or(0);
let new_n = n - 1;
*data = new_n.to_string().into_bytes();
Ok(X86RedisValue::Integer(new_n))
}
_ => Err("Value is not an integer".to_string()),
}
}
fn cmd_lpush(&mut self, args: &[String]) -> Result<X86RedisValue, String> {
if args.len() < 2 {
return Err("LPUSH requires at least 2 arguments".to_string());
}
let entry = self.store.entry(args[0].clone());
let list = entry.or_insert_with(|| X86RedisValue::Array(Vec::new()));
match list {
X86RedisValue::Array(items) => {
for v in args[1..].iter().rev() {
items.insert(0, X86RedisValue::BulkString(Some(v.as_bytes().to_vec())));
}
Ok(X86RedisValue::Integer(items.len() as i64))
}
_ => Err("Key holds wrong type".to_string()),
}
}
fn cmd_rpush(&mut self, args: &[String]) -> Result<X86RedisValue, String> {
if args.len() < 2 {
return Err("RPUSH requires at least 2 arguments".to_string());
}
let entry = self.store.entry(args[0].clone());
let list = entry.or_insert_with(|| X86RedisValue::Array(Vec::new()));
match list {
X86RedisValue::Array(items) => {
for v in &args[1..] {
items.push(X86RedisValue::BulkString(Some(v.as_bytes().to_vec())));
}
Ok(X86RedisValue::Integer(items.len() as i64))
}
_ => Err("Key holds wrong type".to_string()),
}
}
fn cmd_lpop(&mut self, args: &[String]) -> Result<X86RedisValue, String> {
if args.is_empty() {
return Err("LPOP requires at least 1 argument".to_string());
}
match self.store.get_mut(&args[0]) {
Some(X86RedisValue::Array(items)) => {
if items.is_empty() {
Ok(X86RedisValue::Null)
} else {
Ok(items.remove(0))
}
}
Some(_) => Err("Key holds wrong type".to_string()),
None => Ok(X86RedisValue::Null),
}
}
fn cmd_rpop(&mut self, args: &[String]) -> Result<X86RedisValue, String> {
if args.is_empty() {
return Err("RPOP requires at least 1 argument".to_string());
}
match self.store.get_mut(&args[0]) {
Some(X86RedisValue::Array(items)) => {
if items.is_empty() {
Ok(X86RedisValue::Null)
} else {
Ok(items.pop().unwrap())
}
}
Some(_) => Err("Key holds wrong type".to_string()),
None => Ok(X86RedisValue::Null),
}
}
fn cmd_llen(&self, args: &[String]) -> Result<X86RedisValue, String> {
if args.is_empty() {
return Err("LLEN requires 1 argument".to_string());
}
match self.store.get(&args[0]) {
Some(X86RedisValue::Array(items)) => Ok(X86RedisValue::Integer(items.len() as i64)),
Some(_) => Err("Key holds wrong type".to_string()),
None => Ok(X86RedisValue::Integer(0)),
}
}
fn cmd_lrange(&self, args: &[String]) -> Result<X86RedisValue, String> {
if args.len() < 3 {
return Err("LRANGE requires 3 arguments".to_string());
}
let start: i64 = args[1].parse::<i64>().map_err(|e| e.to_string())?;
let stop: i64 = args[2].parse::<i64>().map_err(|e| e.to_string())?;
match self.store.get(&args[0]) {
Some(X86RedisValue::Array(items)) => {
let len = items.len() as i64;
let start_idx = if start < 0 { len + start } else { start };
let stop_idx = if stop < 0 { len + stop } else { stop };
let start_idx = start_idx.max(0) as usize;
let stop_idx = (stop_idx.min(len - 1).max(-1)) as usize;
if start_idx > stop_idx || start_idx >= items.len() {
return Ok(X86RedisValue::Array(Vec::new()));
}
let end = (stop_idx + 1).min(items.len());
Ok(X86RedisValue::Array(items[start_idx..end].to_vec()))
}
Some(_) => Err("Key holds wrong type".to_string()),
None => Ok(X86RedisValue::Array(Vec::new())),
}
}
fn cmd_sadd(&mut self, args: &[String]) -> Result<X86RedisValue, String> {
if args.len() < 2 {
return Err("SADD requires at least 2 arguments".to_string());
}
let entry = self.store.entry(args[0].clone());
let set = entry.or_insert_with(|| X86RedisValue::Set(Vec::new()));
let mut added = 0i64;
match set {
X86RedisValue::Set(members) => {
for v in &args[1..] {
let val = X86RedisValue::BulkString(Some(v.as_bytes().to_vec()));
let val_str = format!("{:?}", val);
if !members.iter().any(|m| format!("{:?}", m) == val_str) {
members.push(val);
added += 1;
}
}
}
_ => return Err("Key holds wrong type".to_string()),
}
Ok(X86RedisValue::Integer(added))
}
fn cmd_smembers(&self, args: &[String]) -> Result<X86RedisValue, String> {
if args.is_empty() {
return Err("SMEMBERS requires 1 argument".to_string());
}
match self.store.get(&args[0]) {
Some(X86RedisValue::Set(members)) => Ok(X86RedisValue::Array(members.clone())),
Some(_) => Err("Key holds wrong type".to_string()),
None => Ok(X86RedisValue::Array(Vec::new())),
}
}
fn cmd_sismember(&self, args: &[String]) -> Result<X86RedisValue, String> {
if args.len() < 2 {
return Err("SISMEMBER requires 2 arguments".to_string());
}
match self.store.get(&args[0]) {
Some(X86RedisValue::Set(members)) => {
let target = format!(
"{:?}",
X86RedisValue::BulkString(Some(args[1].as_bytes().to_vec()))
);
let found = members.iter().any(|m| format!("{:?}", m) == target);
Ok(X86RedisValue::Integer(if found { 1 } else { 0 }))
}
Some(_) => Err("Key holds wrong type".to_string()),
None => Ok(X86RedisValue::Integer(0)),
}
}
fn cmd_hset(&mut self, args: &[String]) -> Result<X86RedisValue, String> {
if args.len() < 3 || args.len() % 2 != 1 {
return Err("HSET requires key and field-value pairs".to_string());
}
let entry = self.store.entry(args[0].clone());
let hash = entry.or_insert_with(|| X86RedisValue::Map(Vec::new()));
let mut created = 0i64;
match hash {
X86RedisValue::Map(pairs) => {
let mut i = 1;
while i < args.len() {
let field = X86RedisValue::BulkString(Some(args[i].as_bytes().to_vec()));
let value = X86RedisValue::BulkString(Some(args[i + 1].as_bytes().to_vec()));
let field_str = format!("{:?}", field);
if let Some(pos) = pairs
.iter()
.position(|(k, _)| format!("{:?}", k) == field_str)
{
pairs[pos] = (field, value);
} else {
pairs.push((field, value));
created += 1;
}
i += 2;
}
}
_ => return Err("Key holds wrong type".to_string()),
}
Ok(X86RedisValue::Integer(created))
}
fn cmd_hget(&self, args: &[String]) -> Result<X86RedisValue, String> {
if args.len() < 2 {
return Err("HGET requires 2 arguments".to_string());
}
match self.store.get(&args[0]) {
Some(X86RedisValue::Map(pairs)) => {
let field_str = format!(
"{:?}",
X86RedisValue::BulkString(Some(args[1].as_bytes().to_vec()))
);
for (k, v) in pairs {
if format!("{:?}", k) == field_str {
return Ok(v.clone());
}
}
Ok(X86RedisValue::Null)
}
Some(_) => Err("Key holds wrong type".to_string()),
None => Ok(X86RedisValue::Null),
}
}
fn cmd_hgetall(&self, args: &[String]) -> Result<X86RedisValue, String> {
if args.is_empty() {
return Err("HGETALL requires 1 argument".to_string());
}
match self.store.get(&args[0]) {
Some(X86RedisValue::Map(pairs)) => {
let mut result = Vec::new();
for (k, v) in pairs {
result.push(k.clone());
result.push(v.clone());
}
Ok(X86RedisValue::Array(result))
}
Some(_) => Err("Key holds wrong type".to_string()),
None => Ok(X86RedisValue::Array(Vec::new())),
}
}
fn cmd_zadd(&mut self, args: &[String]) -> Result<X86RedisValue, String> {
if args.len() < 3 || args.len() % 2 != 1 {
return Err("ZADD requires key and score-member pairs".to_string());
}
let mut added = 0i64;
let set = self
.sorted_sets
.entry(args[0].clone())
.or_insert_with(Vec::new);
let mut i = 1;
while i < args.len() {
let score: f64 = args[i].parse::<f64>().map_err(|e| e.to_string())?;
let member = args[i + 1].clone();
if let Some(entry) = set
.iter_mut()
.find(|(s, _)| s.total_cmp(&score) == std::cmp::Ordering::Equal)
{
entry.1.push(member);
} else {
set.push((score, vec![member]));
}
added += 1;
i += 2;
}
Ok(X86RedisValue::Integer(added))
}
fn cmd_zrange(&self, args: &[String]) -> Result<X86RedisValue, String> {
if args.len() < 3 {
return Err("ZRANGE requires at least 3 arguments".to_string());
}
let start: i64 = args[1].parse::<i64>().map_err(|e| e.to_string())?;
let stop: i64 = args[2].parse::<i64>().map_err(|e| e.to_string())?;
match self.sorted_sets.get(&args[0]) {
Some(set) => {
let mut sorted: Vec<_> = set.iter().collect();
sorted.sort_by(|a, b| a.0.total_cmp(&b.0));
let all: Vec<_> = sorted
.iter()
.flat_map(|(_, members)| members.iter())
.collect();
let len = all.len() as i64;
let start_idx = if start < 0 { len + start } else { start };
let stop_idx = if stop < 0 { len + stop } else { stop };
let start_idx = start_idx.max(0) as usize;
let stop_idx = stop_idx.min(len - 1).max(-1) as usize;
if start_idx > stop_idx || start_idx >= all.len() {
return Ok(X86RedisValue::Array(Vec::new()));
}
let result: Vec<X86RedisValue> = all[start_idx..=(stop_idx.min(all.len() - 1))]
.iter()
.map(|s| X86RedisValue::BulkString(Some(s.as_bytes().to_vec())))
.collect();
Ok(X86RedisValue::Array(result))
}
None => Ok(X86RedisValue::Array(Vec::new())),
}
}
fn cmd_publish(&mut self, args: &[String]) -> Result<X86RedisValue, String> {
if args.len() < 2 {
return Err("PUBLISH requires 2 arguments".to_string());
}
let channel = &args[0];
let message = &args[1];
let mut count = 0i64;
if let Some(subscribers) = self.channels.get(channel) {
count = subscribers.len() as i64;
}
let _ = message;
Ok(X86RedisValue::Integer(count))
}
fn cmd_subscribe(&mut self, conn_key: &str, args: &[String]) -> Result<X86RedisValue, String> {
if args.is_empty() {
return Err("SUBSCRIBE requires at least 1 channel".to_string());
}
if let Some(conn) = self.connections.get_mut(conn_key) {
for channel in args {
conn.subscribed_channels.push(channel.clone());
self.channels
.entry(channel.clone())
.or_insert_with(Vec::new)
.push(conn_key.to_string());
}
}
Ok(X86RedisValue::SimpleString("OK".to_string()))
}
}
#[derive(Debug, Clone)]
pub struct X86ODBCSupport {
pub version: String,
pub api: X86ODBCAPI,
pub types: X86ODBCTypes,
pub environments: HashMap<u64, X86ODBCEnvironment>,
pub connections: HashMap<u64, X86ODBCConnection>,
pub statements: HashMap<u64, X86ODBCStatement>,
pub driver_manager: X86ODBCDriverManager,
next_handle_id: u64,
pub compile_flags: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct X86ODBCAPI {
pub alloc_handle_available: bool,
pub free_handle_available: bool,
pub connect_available: bool,
pub disconnect_available: bool,
pub exec_direct_available: bool,
pub prepare_available: bool,
pub execute_available: bool,
pub fetch_available: bool,
pub get_data_available: bool,
pub bind_col_available: bool,
pub bind_param_available: bool,
pub num_result_cols_available: bool,
pub describe_col_available: bool,
pub api_version: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ODBCHandleType {
Environment,
Connection,
Statement,
Descriptor,
}
#[derive(Debug, Clone)]
pub struct X86ODBCEnvironment {
pub handle: u64,
pub odbc_version: u32,
pub connection_pooling: bool,
pub output_nts: bool,
}
#[derive(Debug, Clone)]
pub struct X86ODBCConnection {
pub handle: u64,
pub env_handle: u64,
pub connection_string: String,
pub server_name: String,
pub user_name: String,
pub database_name: String,
pub connected: bool,
pub autocommit: bool,
pub isolation_level: u32,
pub last_error: Option<String>,
pub last_sqlstate: Option<String>,
pub last_native_error: i32,
}
#[derive(Debug, Clone)]
pub struct X86ODBCStatement {
pub handle: u64,
pub conn_handle: u64,
pub sql: String,
pub prepared: bool,
pub executed: bool,
pub row_count: i64,
pub column_count: i16,
pub current_row: usize,
pub result_set: Vec<Vec<X86ODBCData>>,
pub column_descs: Vec<X86ODBCColumnDesc>,
pub param_bindings: Vec<X86ODBCParamBinding>,
pub cursor_type: u32,
pub concurrency: u32,
pub cursor_scrollable: bool,
}
#[derive(Debug, Clone)]
pub struct X86ODBCColumnDesc {
pub name: String,
pub data_type: i16,
pub column_size: u32,
pub decimal_digits: i16,
pub nullable: i16,
pub octet_length: u32,
pub display_size: u32,
}
#[derive(Debug, Clone)]
pub enum X86ODBCData {
Null,
TinyInt(i8),
SmallInt(i16),
Integer(i32),
BigInt(i64),
Real(f32),
Double(f64),
Decimal(String),
Char(String),
Varchar(String),
LongVarchar(String),
WChar(String),
WVarChar(String),
Binary(Vec<u8>),
VarBinary(Vec<u8>),
Date(String),
Time(String),
Timestamp(String),
}
#[derive(Debug, Clone)]
pub struct X86ODBCParamBinding {
pub param_index: u16,
pub value_type: i16,
pub param_type: i16,
pub column_size: u32,
pub decimal_digits: i16,
pub value: Option<Vec<u8>>,
pub null_indicator: i32,
}
#[derive(Debug, Clone)]
pub struct X86ODBCTypes {
pub type_codes: HashMap<String, i16>,
pub type_names: HashMap<i16, String>,
pub type_sizes: HashMap<i16, usize>,
}
#[allow(non_upper_case_globals)]
pub mod odbc_sql_types {
pub const SQL_CHAR: i16 = 1;
pub const SQL_NUMERIC: i16 = 2;
pub const SQL_DECIMAL: i16 = 3;
pub const SQL_INTEGER: i16 = 4;
pub const SQL_SMALLINT: i16 = 5;
pub const SQL_FLOAT: i16 = 6;
pub const SQL_REAL: i16 = 7;
pub const SQL_DOUBLE: i16 = 8;
pub const SQL_DATETIME: i16 = 9;
pub const SQL_DATE: i16 = 9;
pub const SQL_TIME: i16 = 10;
pub const SQL_TIMESTAMP: i16 = 11;
pub const SQL_VARCHAR: i16 = 12;
pub const SQL_BIGINT: i16 = -5;
pub const SQL_TINYINT: i16 = -6;
pub const SQL_BIT: i16 = -7;
pub const SQL_WCHAR: i16 = -8;
pub const SQL_WVARCHAR: i16 = -9;
pub const SQL_WLONGVARCHAR: i16 = -10;
pub const SQL_BINARY: i16 = -2;
pub const SQL_VARBINARY: i16 = -3;
pub const SQL_LONGVARBINARY: i16 = -4;
pub const SQL_LONGVARCHAR: i16 = -1;
pub const SQL_GUID: i16 = -11;
pub const SQL_TYPE_DATE: i16 = 91;
pub const SQL_TYPE_TIME: i16 = 92;
pub const SQL_TYPE_TIMESTAMP: i16 = 93;
}
#[derive(Debug, Clone)]
pub struct X86ODBCDriverManager {
pub drivers: Vec<X86ODBCDriver>,
pub data_sources: Vec<X86ODBCDataSource>,
pub tracing_enabled: bool,
pub trace_file: Option<String>,
}
#[derive(Debug, Clone)]
pub struct X86ODBCDriver {
pub name: String,
pub attributes: HashMap<String, String>,
pub file_usage: u32,
pub driver_lib: Option<String>,
pub setup_lib: Option<String>,
}
#[derive(Debug, Clone)]
pub struct X86ODBCDataSource {
pub name: String,
pub driver: String,
pub attributes: HashMap<String, String>,
pub system_dsn: bool,
}
impl X86ODBCSupport {
pub fn new() -> Self {
let mut types = X86ODBCTypes {
type_codes: HashMap::new(),
type_names: HashMap::new(),
type_sizes: HashMap::new(),
};
let type_map = [
("CHAR", odbc_sql_types::SQL_CHAR),
("NUMERIC", odbc_sql_types::SQL_NUMERIC),
("DECIMAL", odbc_sql_types::SQL_DECIMAL),
("INTEGER", odbc_sql_types::SQL_INTEGER),
("SMALLINT", odbc_sql_types::SQL_SMALLINT),
("FLOAT", odbc_sql_types::SQL_FLOAT),
("REAL", odbc_sql_types::SQL_REAL),
("DOUBLE", odbc_sql_types::SQL_DOUBLE),
("VARCHAR", odbc_sql_types::SQL_VARCHAR),
("BIGINT", odbc_sql_types::SQL_BIGINT),
("TINYINT", odbc_sql_types::SQL_TINYINT),
("BIT", odbc_sql_types::SQL_BIT),
("BINARY", odbc_sql_types::SQL_BINARY),
("VARBINARY", odbc_sql_types::SQL_VARBINARY),
("LONGVARCHAR", odbc_sql_types::SQL_LONGVARCHAR),
("DATE", odbc_sql_types::SQL_TYPE_DATE),
("TIME", odbc_sql_types::SQL_TYPE_TIME),
("TIMESTAMP", odbc_sql_types::SQL_TYPE_TIMESTAMP),
];
for (name, code) in &type_map {
types.type_codes.insert(name.to_string(), *code);
types.type_names.insert(*code, name.to_string());
}
types.type_sizes.insert(odbc_sql_types::SQL_CHAR, 1);
types.type_sizes.insert(odbc_sql_types::SQL_INTEGER, 4);
types.type_sizes.insert(odbc_sql_types::SQL_SMALLINT, 2);
types.type_sizes.insert(odbc_sql_types::SQL_REAL, 4);
types.type_sizes.insert(odbc_sql_types::SQL_DOUBLE, 8);
types.type_sizes.insert(odbc_sql_types::SQL_BIGINT, 8);
types.type_sizes.insert(odbc_sql_types::SQL_TINYINT, 1);
Self {
version: "4.0".to_string(),
api: X86ODBCAPI {
alloc_handle_available: true,
free_handle_available: true,
connect_available: true,
disconnect_available: true,
exec_direct_available: true,
prepare_available: true,
execute_available: true,
fetch_available: true,
get_data_available: true,
bind_col_available: true,
bind_param_available: true,
num_result_cols_available: true,
describe_col_available: true,
api_version: 4,
},
types,
environments: HashMap::new(),
connections: HashMap::new(),
statements: HashMap::new(),
driver_manager: X86ODBCDriverManager {
drivers: Vec::new(),
data_sources: Vec::new(),
tracing_enabled: false,
trace_file: None,
},
next_handle_id: 1,
compile_flags: vec!["-lodbc".to_string(), "-lodbcinst".to_string()],
}
}
pub fn validate(&self) -> bool {
self.api.alloc_handle_available && self.api.connect_available
}
pub fn allocate_handle(&mut self, handle_type: X86ODBCHandleType) -> Result<u64, String> {
let handle = self.next_handle_id;
self.next_handle_id += 1;
match handle_type {
X86ODBCHandleType::Environment => {
self.environments.insert(
handle,
X86ODBCEnvironment {
handle,
odbc_version: 4,
connection_pooling: false,
output_nts: true,
},
);
}
X86ODBCHandleType::Connection => {
self.connections.insert(
handle,
X86ODBCConnection {
handle,
env_handle: 0,
connection_string: String::new(),
server_name: String::new(),
user_name: String::new(),
database_name: String::new(),
connected: false,
autocommit: true,
isolation_level: 0,
last_error: None,
last_sqlstate: None,
last_native_error: 0,
},
);
}
X86ODBCHandleType::Statement => {
self.statements.insert(
handle,
X86ODBCStatement {
handle,
conn_handle: 0,
sql: String::new(),
prepared: false,
executed: false,
row_count: 0,
column_count: 0,
current_row: 0,
result_set: Vec::new(),
column_descs: Vec::new(),
param_bindings: Vec::new(),
cursor_type: 0,
concurrency: 0,
cursor_scrollable: false,
},
);
}
X86ODBCHandleType::Descriptor => {
}
}
Ok(handle)
}
pub fn free_handle(
&mut self,
handle_type: X86ODBCHandleType,
handle: u64,
) -> Result<(), String> {
match handle_type {
X86ODBCHandleType::Environment => {
self.environments.remove(&handle);
}
X86ODBCHandleType::Connection => {
self.connections.remove(&handle);
}
X86ODBCHandleType::Statement => {
self.statements.remove(&handle);
}
X86ODBCHandleType::Descriptor => {}
}
Ok(())
}
pub fn connect(
&mut self,
conn_handle: u64,
server_name: &str,
user_name: &str,
password: &str,
) -> Result<(), String> {
let conn = self
.connections
.get_mut(&conn_handle)
.ok_or("Invalid connection handle")?;
conn.server_name = server_name.to_string();
conn.user_name = user_name.to_string();
conn.connection_string =
format!("SERVER={};UID={};PWD={}", server_name, user_name, password);
conn.connected = true;
conn.last_error = None;
Ok(())
}
pub fn disconnect(&mut self, conn_handle: u64) -> Result<(), String> {
let conn = self
.connections
.get_mut(&conn_handle)
.ok_or("Invalid connection handle")?;
conn.connected = false;
Ok(())
}
pub fn exec_direct(&mut self, stmt_handle: u64, sql: &str) -> Result<(), String> {
let stmt = self
.statements
.get_mut(&stmt_handle)
.ok_or("Invalid statement handle")?;
stmt.sql = sql.to_string();
stmt.executed = true;
stmt.prepared = false;
stmt.row_count = 0;
stmt.current_row = 0;
stmt.result_set = Vec::new();
Ok(())
}
pub fn prepare(&mut self, stmt_handle: u64, sql: &str) -> Result<(), String> {
let stmt = self
.statements
.get_mut(&stmt_handle)
.ok_or("Invalid statement handle")?;
stmt.sql = sql.to_string();
stmt.prepared = true;
stmt.executed = false;
Ok(())
}
pub fn execute(&mut self, stmt_handle: u64) -> Result<(), String> {
let stmt = self
.statements
.get_mut(&stmt_handle)
.ok_or("Invalid statement handle")?;
if !stmt.prepared {
return Err("Statement not prepared".to_string());
}
stmt.executed = true;
stmt.row_count = 0;
Ok(())
}
pub fn fetch(&mut self, stmt_handle: u64) -> Result<Option<Vec<X86ODBCData>>, String> {
let stmt = self
.statements
.get_mut(&stmt_handle)
.ok_or("Invalid statement handle")?;
if !stmt.executed {
return Err("Statement not executed".to_string());
}
if stmt.current_row >= stmt.result_set.len() {
return Ok(None);
}
let row = stmt.result_set[stmt.current_row].clone();
stmt.current_row += 1;
Ok(Some(row))
}
pub fn get_data(
&self,
stmt_handle: u64,
column_index: u16,
) -> Result<Option<X86ODBCData>, String> {
let stmt = self
.statements
.get(&stmt_handle)
.ok_or("Invalid statement handle")?;
if !stmt.executed {
return Err("Statement not executed".to_string());
}
if stmt.current_row == 0 || stmt.current_row > stmt.result_set.len() {
return Err("No current row".to_string());
}
let row = &stmt.result_set[stmt.current_row - 1];
let idx = (column_index - 1) as usize;
if idx >= row.len() {
return Ok(None);
}
Ok(Some(row[idx].clone()))
}
pub fn num_result_cols(&self, stmt_handle: u64) -> Result<i16, String> {
let stmt = self
.statements
.get(&stmt_handle)
.ok_or("Invalid statement handle")?;
Ok(stmt.column_count)
}
pub fn describe_col(
&self,
stmt_handle: u64,
column_index: u16,
) -> Result<X86ODBCColumnDesc, String> {
let stmt = self
.statements
.get(&stmt_handle)
.ok_or("Invalid statement handle")?;
let idx = (column_index - 1) as usize;
if idx >= stmt.column_descs.len() {
return Err("Column index out of range".to_string());
}
Ok(stmt.column_descs[idx].clone())
}
pub fn odbc_type_size(&self, sql_type: i16) -> usize {
self.types.type_sizes.get(&sql_type).copied().unwrap_or(0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_x86_database_default() {
let db = X86Database::new();
assert_eq!(db.arch, X86DBArch::X86_64);
assert_eq!(db.endianness, "little");
assert!(db.has_sse2);
assert!(db.has_avx2);
}
#[test]
fn test_x86_database_32bit() {
let db = X86Database::new_x86_32();
assert_eq!(db.arch, X86DBArch::X86_32);
assert!(!db.has_avx2);
assert!(!db.has_aesni);
}
#[test]
fn test_x86_database_x32() {
let db = X86Database::new_x86_x32();
assert_eq!(db.arch, X86DBArch::X86_X32);
assert!(db.has_sse2);
}
#[test]
fn test_x86_database_validate() {
let db = X86Database::new();
let result = db.validate();
assert!(result.valid);
assert!(result.errors.is_empty());
}
#[test]
fn test_x86_database_compiler_flags() {
let db = X86Database::new();
let flags = db.compiler_flags();
assert!(flags.contains(&"-std=c11".to_string()));
assert!(flags.contains(&"-fPIC".to_string()));
assert!(flags.contains(&"-m64".to_string()));
assert!(flags.contains(&"-msse2".to_string()));
assert!(flags.contains(&"-mavx2".to_string()));
}
#[test]
fn test_x86_database_linker_flags() {
let db = X86Database::new();
let flags = db.linker_flags();
assert!(flags.contains(&"-lpthread".to_string()));
assert!(flags.contains(&"-ldl".to_string()));
}
#[test]
fn test_x86_db_arch_display() {
assert_eq!(X86DBArch::X86_32.to_string(), "x86-32");
assert_eq!(X86DBArch::X86_64.to_string(), "x86-64");
assert_eq!(X86DBArch::X86_X32.to_string(), "x86-x32");
}
#[test]
fn test_sql_support_creation() {
let sql = X86SQLSupport::new();
assert!(sql.validate());
assert!(sql.subquery_support);
assert!(sql.window_fn_support);
assert!(sql.cte_support);
assert!(sql.recursive_cte_support);
}
#[test]
fn test_sql_lexer_creation() {
let lexer = X86SQLLexer::new();
assert!(lexer.validate());
assert!(!lexer.keywords.is_empty());
assert!(lexer.keywords.contains_key("SELECT"));
assert!(lexer.keywords.contains_key("INSERT"));
assert!(lexer.keywords.contains_key("UPDATE"));
assert!(lexer.keywords.contains_key("DELETE"));
}
#[test]
fn test_sql_lexer_tokenize_select() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer.tokenize("SELECT * FROM users").unwrap();
assert_eq!(tokens.len(), 5); assert_eq!(tokens[0], X86SQLToken::Keyword(X86SQLKeyword::Select));
assert_eq!(tokens[1], X86SQLToken::Star);
assert_eq!(tokens[2], X86SQLToken::Keyword(X86SQLKeyword::From));
assert_eq!(tokens[3], X86SQLToken::Identifier("users".to_string()));
assert_eq!(tokens[4], X86SQLToken::EOF);
}
#[test]
fn test_sql_lexer_tokenize_insert() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer
.tokenize("INSERT INTO users (name) VALUES ('Alice')")
.unwrap();
assert_eq!(tokens[0], X86SQLToken::Keyword(X86SQLKeyword::Insert));
assert_eq!(tokens[tokens.len() - 1], X86SQLToken::EOF);
}
#[test]
fn test_sql_lexer_tokenize_with_string() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer.tokenize("SELECT 'hello world'").unwrap();
assert_eq!(
tokens[1],
X86SQLToken::StringLiteral("hello world".to_string())
);
}
#[test]
fn test_sql_lexer_tokenize_with_number() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer.tokenize("SELECT 42").unwrap();
assert_eq!(tokens[1], X86SQLToken::NumericLiteral("42".to_string()));
}
#[test]
fn test_sql_lexer_tokenize_keywords() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer
.tokenize("CREATE TABLE DROP ALTER INDEX VIEW TRIGGER BEGIN COMMIT ROLLBACK")
.unwrap();
assert_eq!(tokens[0], X86SQLToken::Keyword(X86SQLKeyword::Create));
assert_eq!(tokens[1], X86SQLToken::Keyword(X86SQLKeyword::Table));
assert_eq!(tokens[2], X86SQLToken::Keyword(X86SQLKeyword::Drop));
assert_eq!(tokens[3], X86SQLToken::Keyword(X86SQLKeyword::Alter));
assert_eq!(tokens[4], X86SQLToken::Keyword(X86SQLKeyword::Index));
assert_eq!(tokens[5], X86SQLToken::Keyword(X86SQLKeyword::View));
assert_eq!(tokens[6], X86SQLToken::Keyword(X86SQLKeyword::Trigger));
assert_eq!(tokens[7], X86SQLToken::Keyword(X86SQLKeyword::Begin));
assert_eq!(tokens[8], X86SQLToken::Keyword(X86SQLKeyword::Commit));
assert_eq!(tokens[9], X86SQLToken::Keyword(X86SQLKeyword::Rollback));
}
#[test]
fn test_sql_lexer_line_comment() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer
.tokenize("SELECT 1 -- this is a comment\nSELECT 2")
.unwrap();
assert_eq!(tokens[0], X86SQLToken::Keyword(X86SQLKeyword::Select));
}
#[test]
fn test_sql_lexer_block_comment() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer.tokenize("SELECT /* comment */ 1").unwrap();
assert_eq!(tokens[0], X86SQLToken::Keyword(X86SQLKeyword::Select));
assert_eq!(tokens[1], X86SQLToken::NumericLiteral("1".to_string()));
assert_eq!(tokens[2], X86SQLToken::EOF);
}
#[test]
fn test_sql_parser_select() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Select),
X86SQLToken::Star,
X86SQLToken::Keyword(X86SQLKeyword::From),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::Select);
assert_eq!(stmt.table_name, Some("users".to_string()));
}
#[test]
fn test_sql_parser_select_with_where() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Select),
X86SQLToken::Star,
X86SQLToken::Keyword(X86SQLKeyword::From),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Where),
X86SQLToken::Identifier("id".to_string()),
X86SQLToken::Operator(X86SQLOperator::Equal),
X86SQLToken::NumericLiteral("1".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.conditions.len(), 1);
assert_eq!(stmt.conditions[0].column, "id");
}
#[test]
fn test_sql_parser_insert() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Insert),
X86SQLToken::Keyword(X86SQLKeyword::Into),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::LeftParen,
X86SQLToken::Identifier("name".to_string()),
X86SQLToken::RightParen,
X86SQLToken::Keyword(X86SQLKeyword::Values),
X86SQLToken::LeftParen,
X86SQLToken::StringLiteral("Alice".to_string()),
X86SQLToken::RightParen,
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::Insert);
assert_eq!(stmt.table_name, Some("users".to_string()));
}
#[test]
fn test_sql_parser_update() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Update),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Set),
X86SQLToken::Identifier("name".to_string()),
X86SQLToken::Operator(X86SQLOperator::Equal),
X86SQLToken::StringLiteral("Bob".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Where),
X86SQLToken::Identifier("id".to_string()),
X86SQLToken::Operator(X86SQLOperator::Equal),
X86SQLToken::NumericLiteral("1".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::Update);
}
#[test]
fn test_sql_parser_delete() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Delete),
X86SQLToken::Keyword(X86SQLKeyword::From),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Where),
X86SQLToken::Identifier("id".to_string()),
X86SQLToken::Operator(X86SQLOperator::Equal),
X86SQLToken::NumericLiteral("1".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::Delete);
}
#[test]
fn test_sql_parser_create_table() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Create),
X86SQLToken::Keyword(X86SQLKeyword::Table),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::LeftParen,
X86SQLToken::Identifier("id".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Integer),
X86SQLToken::Keyword(X86SQLKeyword::Primary),
X86SQLToken::Keyword(X86SQLKeyword::Key),
X86SQLToken::Comma,
X86SQLToken::Identifier("name".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Text),
X86SQLToken::RightParen,
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::CreateTable);
assert_eq!(stmt.columns.len(), 2);
assert!(stmt.columns[0].primary_key);
}
#[test]
fn test_sql_parser_drop_table() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Drop),
X86SQLToken::Keyword(X86SQLKeyword::Table),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::DropTable);
}
#[test]
fn test_sql_parser_begin() {
let mut parser = X86SQLParser::new();
let tokens = vec![X86SQLToken::Keyword(X86SQLKeyword::Begin), X86SQLToken::EOF];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::Begin);
}
#[test]
fn test_sql_parser_commit() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Commit),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::Commit);
}
#[test]
fn test_sql_parser_rollback() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Rollback),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::Rollback);
}
#[test]
fn test_sql_parser_savepoint() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Savepoint),
X86SQLToken::Identifier("sp1".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::Savepoint);
}
#[test]
fn test_sql_keyword_as_str() {
assert_eq!(X86SQLKeyword::Select.as_str(), "SELECT");
assert_eq!(X86SQLKeyword::Inner.as_str(), "INNER");
assert_eq!(X86SQLKeyword::Left.as_str(), "LEFT");
assert_eq!(X86SQLKeyword::Right.as_str(), "RIGHT");
assert_eq!(X86SQLKeyword::Full.as_str(), "FULL");
assert_eq!(X86SQLKeyword::Outer.as_str(), "OUTER");
assert_eq!(X86SQLKeyword::Cross.as_str(), "CROSS");
assert_eq!(X86SQLKeyword::Natural.as_str(), "NATURAL");
assert_eq!(X86SQLKeyword::RowNumber.as_str(), "ROW_NUMBER");
assert_eq!(X86SQLKeyword::Btree.as_str(), "BTREE");
assert_eq!(X86SQLKeyword::Gin.as_str(), "GIN");
assert_eq!(X86SQLKeyword::Brin.as_str(), "BRIN");
}
#[test]
fn test_sql_type_system() {
let ts = X86SQLTypeSystem::new();
assert!(ts.validate());
assert_eq!(ts.resolve("INTEGER"), X86SQLDataType::Integer);
assert_eq!(ts.resolve("BIGINT"), X86SQLDataType::BigInt);
assert_eq!(ts.resolve("VARCHAR"), X86SQLDataType::Varchar);
}
#[test]
fn test_sql_data_type_x86_size() {
assert_eq!(X86SQLDataType::Integer.x86_size(), 4);
assert_eq!(X86SQLDataType::BigInt.x86_size(), 8);
assert_eq!(X86SQLDataType::SmallInt.x86_size(), 2);
assert_eq!(X86SQLDataType::TinyInt.x86_size(), 1);
assert_eq!(X86SQLDataType::Real.x86_size(), 4);
assert_eq!(X86SQLDataType::Double.x86_size(), 8);
assert_eq!(X86SQLDataType::Boolean.x86_size(), 1);
assert_eq!(X86SQLDataType::Timestamp.x86_size(), 8);
assert_eq!(X86SQLDataType::Uuid.x86_size(), 16);
}
#[test]
fn test_sql_index_support() {
let idx = X86SQLIndexSupport::new();
assert_eq!(idx.default_index_type, X86SQLIndexType::Btree);
assert!(idx.concurrent_index_build);
assert!(idx.partial_index_support);
let util = idx.x86_page_utilization(X86SQLIndexType::Btree);
assert!(util > 0.0 && util < 1.0);
}
#[test]
fn test_sql_transaction_support() {
let tx = X86SQLTransactionSupport::new();
assert_eq!(tx.default_isolation, X86SQLIsolationLevel::ReadCommitted);
assert!(tx.savepoints_enabled);
assert_eq!(tx.isolation_levels.len(), 4);
}
#[test]
fn test_sqlite_support_creation() {
let sqlite = X86SQLiteSupport::new();
assert!(sqlite.validate());
assert_eq!(sqlite.version, "3.44.0");
assert!(sqlite.api.open_available);
assert!(sqlite.fts5_enabled);
assert!(sqlite.rtree_enabled);
assert!(sqlite.json1_enabled);
}
#[test]
fn test_sqlite_open_close() {
let mut sqlite = X86SQLiteSupport::new();
let conn_id = sqlite.open(":memory:").unwrap();
assert!(sqlite.connections.contains_key(&conn_id));
sqlite.close(conn_id).unwrap();
assert!(!sqlite.connections.contains_key(&conn_id));
}
#[test]
fn test_sqlite_exec() {
let mut sqlite = X86SQLiteSupport::new();
let conn_id = sqlite.open(":memory:").unwrap();
let result = sqlite.exec(conn_id, "SELECT 1");
assert!(result.is_ok());
}
#[test]
fn test_sqlite_prepare() {
let mut sqlite = X86SQLiteSupport::new();
let conn_id = sqlite.open(":memory:").unwrap();
let stmt = sqlite
.prepare(conn_id, "SELECT * FROM t WHERE id = ?")
.unwrap();
assert_eq!(stmt.param_count, 1);
}
#[test]
fn test_sqlite_explain_query_plan() {
let sqlite = X86SQLiteSupport::new();
let plan = sqlite.explain_query_plan("SELECT * FROM users WHERE id = 1");
assert!(plan.plan_text.contains("SCAN"));
}
#[test]
fn test_sqlite_wal_checkpoint() {
let mut sqlite = X86SQLiteSupport::new();
sqlite.wal.enabled = true;
let result = sqlite.wal_checkpoint().unwrap();
assert_eq!(result.pages_written, 0);
}
#[test]
fn test_sqlite_wal_rollback() {
let mut sqlite = X86SQLiteSupport::new();
sqlite.wal.enabled = true;
sqlite.wal.wal_size_pages = 10;
sqlite.wal_rollback().unwrap();
assert_eq!(sqlite.wal.wal_size_pages, 0);
}
#[test]
fn test_sqlite_file_header_parse() {
let sqlite = X86SQLiteSupport::new();
let mut header = vec![0u8; 100];
header[0..16].copy_from_slice(b"SQLite format 3\0");
header[16..20].copy_from_slice(&4096u32.to_be_bytes());
header[18] = 1; header[19] = 1; let parsed = sqlite.parse_file_header(&header).unwrap();
assert_eq!(&parsed.header_string[..], b"SQLite format 3\0");
assert_eq!(parsed.page_size, 4096);
}
#[test]
fn test_sqlite_data_type_affinity() {
let dtm = X86SQLiteDataTypeMap::new();
assert_eq!(dtm.affinity("INTEGER"), X86SQLiteAffinity::Integer);
assert_eq!(dtm.affinity("VARCHAR(255)"), X86SQLiteAffinity::Text);
assert_eq!(dtm.affinity("REAL"), X86SQLiteAffinity::Real);
assert_eq!(dtm.affinity("BLOB"), X86SQLiteAffinity::Blob);
assert_eq!(dtm.affinity("DECIMAL(10,2)"), X86SQLiteAffinity::Numeric);
}
#[test]
fn test_sqlite_cost_model_default() {
let cm = X86SQLiteCostModel::default();
assert_eq!(cm.sequential_page_cost, 1.0);
assert_eq!(cm.random_page_cost, 4.0);
}
#[test]
fn test_sqlite_checkpoint_modes() {
let modes = [
X86SQLiteCheckpointMode::Passive,
X86SQLiteCheckpointMode::Full,
X86SQLiteCheckpointMode::Restart,
X86SQLiteCheckpointMode::Truncate,
];
assert_eq!(modes.len(), 4);
}
#[test]
fn test_postgresql_support_creation() {
let pg = X86PostgreSQLSupport::new();
assert!(pg.validate());
assert!(pg.api.connect_available);
assert!(pg.api.exec_available);
assert!(pg.scram_auth);
assert_eq!(pg.default_port, 5432);
}
#[test]
fn test_postgresql_build_conn_string() {
let pg = X86PostgreSQLSupport::new();
let conn_str = pg.build_conn_string("localhost", 5432, "mydb", "user", Some("pass"));
assert!(conn_str.contains("host=localhost"));
assert!(conn_str.contains("port=5432"));
assert!(conn_str.contains("dbname=mydb"));
assert!(conn_str.contains("user=user"));
assert!(conn_str.contains("password=pass"));
}
#[test]
fn test_postgresql_connect_disconnect() {
let mut pg = X86PostgreSQLSupport::new();
let conn_str = "host=localhost port=5432 dbname=test user=test";
let key = pg.connect(conn_str).unwrap();
assert!(pg.connections.contains_key(&key));
pg.disconnect(&key);
assert!(!pg.connections.contains_key(&key));
}
#[test]
fn test_postgresql_query() {
let mut pg = X86PostgreSQLSupport::new();
let conn_str = "host=localhost";
let key = pg.connect(conn_str).unwrap();
let result = pg.query(&key, "SELECT 1");
assert!(result.is_ok());
}
#[test]
fn test_postgresql_prepare_exec() {
let mut pg = X86PostgreSQLSupport::new();
let conn_str = "host=localhost";
let key = pg.connect(conn_str).unwrap();
assert!(pg.prepare(&key, "stmt1", "SELECT $1", &[23]).is_ok());
let result = pg.exec_prepared(&key, "stmt1", &[Some(b"1".to_vec())]);
assert!(result.is_ok());
}
#[test]
fn test_postgresql_copy_binary_format() {
let pg = X86PostgreSQLSupport::new();
let tuples = vec![vec![Some(b"hello".to_vec()), Some(b"world".to_vec())]];
let format = pg.copy_binary_format(tuples);
assert!(format.binary_mode);
assert_eq!(format.header.signature, *b"PGCOPY\n\0xff\r\n\0");
assert_eq!(format.tuples.len(), 1);
}
#[test]
fn test_postgresql_type_oids() {
assert_eq!(pg_oids::INT4_OID, 23);
assert_eq!(pg_oids::INT8_OID, 20);
assert_eq!(pg_oids::TEXT_OID, 25);
assert_eq!(pg_oids::JSONB_OID, 3802);
assert_eq!(pg_oids::UUID_OID, 2950);
}
#[test]
fn test_postgresql_protocol_state() {
let pg = X86PostgreSQLSupport::new();
assert_eq!(pg.protocol.major_version, 3);
assert_eq!(pg.protocol.minor_version, 0);
}
#[test]
fn test_mysql_support_creation() {
let mysql = X86MySQLSupport::new();
assert!(mysql.validate());
assert!(mysql.api.connect_available);
assert!(mysql.api.query_available);
assert_eq!(mysql.default_port, 3306);
}
#[test]
fn test_mysql_connect() {
let mut mysql = X86MySQLSupport::new();
let key = mysql
.connect("localhost", 3306, "root", None, None)
.unwrap();
assert!(mysql.connections.contains_key(&key));
}
#[test]
fn test_mysql_query() {
let mut mysql = X86MySQLSupport::new();
let key = mysql
.connect("localhost", 3306, "root", None, None)
.unwrap();
let result = mysql.query(&key, "SELECT 1");
assert!(result.is_ok());
}
#[test]
fn test_mysql_prepare() {
let mut mysql = X86MySQLSupport::new();
let key = mysql
.connect("localhost", 3306, "root", None, None)
.unwrap();
let stmt = mysql
.stmt_prepare(&key, "SELECT * FROM t WHERE id = ?")
.unwrap();
assert_eq!(stmt.param_count, 1);
}
#[test]
fn test_mysql_stmt_bind_param() {
let mut stmt = X86MySQLPreparedStmt {
stmt_id: 1,
sql: "SELECT ?".to_string(),
param_count: 1,
param_bindings: Vec::new(),
result_bindings: Vec::new(),
field_count: 0,
warning_count: 0,
};
assert!(X86MySQLSupport::stmt_bind_param(
&mut stmt,
0,
mysql_field_types::MYSQL_TYPE_LONG,
&1i32.to_le_bytes(),
false,
)
.is_ok());
}
#[test]
fn test_mysql_field_types() {
assert_eq!(mysql_field_types::MYSQL_TYPE_TINY, 1);
assert_eq!(mysql_field_types::MYSQL_TYPE_SHORT, 2);
assert_eq!(mysql_field_types::MYSQL_TYPE_LONG, 3);
assert_eq!(mysql_field_types::MYSQL_TYPE_LONGLONG, 8);
assert_eq!(mysql_field_types::MYSQL_TYPE_JSON, 245);
assert_eq!(mysql_field_types::MYSQL_TYPE_BLOB, 252);
}
#[test]
fn test_mysql_type_codes() {
let mysql = X86MySQLSupport::new();
assert!(mysql.types.type_codes.contains_key("VARCHAR"));
assert!(mysql.types.type_codes.contains_key("BIGINT"));
assert!(mysql.types.type_codes.contains_key("DATETIME"));
assert!(mysql.types.type_codes.contains_key("JSON"));
}
#[test]
fn test_redis_support_creation() {
let redis = X86RedisSupport::new();
assert!(redis.validate());
assert!(redis.commands.contains_key("GET"));
assert!(redis.commands.contains_key("SET"));
assert!(redis.commands.contains_key("DEL"));
assert!(redis.commands.contains_key("LPUSH"));
assert!(redis.commands.contains_key("HSET"));
assert!(redis.commands.contains_key("ZADD"));
}
#[test]
fn test_redis_serialize_resp2_simple_string() {
let redis = X86RedisSupport::new();
let val = X86RedisValue::SimpleString("OK".to_string());
let bytes = redis.serialize_resp2(&val);
assert_eq!(bytes, b"+OK\r\n");
}
#[test]
fn test_redis_serialize_resp2_error() {
let redis = X86RedisSupport::new();
let val = X86RedisValue::Error("ERR unknown".to_string());
let bytes = redis.serialize_resp2(&val);
assert_eq!(bytes, b"-ERR unknown\r\n");
}
#[test]
fn test_redis_serialize_resp2_integer() {
let redis = X86RedisSupport::new();
let val = X86RedisValue::Integer(42);
let bytes = redis.serialize_resp2(&val);
assert_eq!(bytes, b":42\r\n");
}
#[test]
fn test_redis_serialize_resp2_bulk_string() {
let redis = X86RedisSupport::new();
let val = X86RedisValue::BulkString(Some(b"hello".to_vec()));
let bytes = redis.serialize_resp2(&val);
assert_eq!(bytes, b"$5\r\nhello\r\n");
}
#[test]
fn test_redis_serialize_resp2_null_bulk_string() {
let redis = X86RedisSupport::new();
let val = X86RedisValue::BulkString(None);
let bytes = redis.serialize_resp2(&val);
assert_eq!(bytes, b"$-1\r\n");
}
#[test]
fn test_redis_serialize_resp2_array() {
let redis = X86RedisSupport::new();
let val = X86RedisValue::Array(vec![
X86RedisValue::BulkString(Some(b"GET".to_vec())),
X86RedisValue::BulkString(Some(b"key".to_vec())),
]);
let bytes = redis.serialize_resp2(&val);
assert_eq!(bytes, b"*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n");
}
#[test]
fn test_redis_serialize_resp3_boolean() {
let redis = X86RedisSupport::new();
let val = X86RedisValue::Boolean(true);
let bytes = redis.serialize_resp2(&val);
assert_eq!(bytes, b"#t\r\n");
}
#[test]
fn test_redis_serialize_resp3_double() {
let redis = X86RedisSupport::new();
let val = X86RedisValue::Double(3.14);
let bytes = redis.serialize_resp2(&val);
assert!(bytes.starts_with(b","));
}
#[test]
fn test_redis_parse_resp2_simple_string() {
let redis = X86RedisSupport::new();
let val = redis.parse_resp2(b"+OK\r\n").unwrap();
match val {
X86RedisValue::SimpleString(s) => assert_eq!(s, "OK"),
_ => panic!("Expected SimpleString"),
}
}
#[test]
fn test_redis_parse_resp2_integer() {
let redis = X86RedisSupport::new();
let val = redis.parse_resp2(b":100\r\n").unwrap();
match val {
X86RedisValue::Integer(i) => assert_eq!(i, 100),
_ => panic!("Expected Integer"),
}
}
#[test]
fn test_redis_parse_resp2_bulk_string() {
let redis = X86RedisSupport::new();
let val = redis.parse_resp2(b"$5\r\nhello\r\n").unwrap();
match val {
X86RedisValue::BulkString(Some(data)) => assert_eq!(data, b"hello"),
_ => panic!("Expected BulkString"),
}
}
#[test]
fn test_redis_parse_resp2_null_bulk_string() {
let redis = X86RedisSupport::new();
let val = redis.parse_resp2(b"$-1\r\n").unwrap();
match val {
X86RedisValue::BulkString(None) => {}
_ => panic!("Expected Null BulkString"),
}
}
#[test]
fn test_redis_cmd_get_set() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
let result = redis
.execute("conn1", "SET", &["key1".to_string(), "value1".to_string()])
.unwrap();
match result {
X86RedisValue::SimpleString(s) => assert_eq!(s, "OK"),
_ => panic!("Expected OK"),
}
let result = redis
.execute("conn1", "GET", &["key1".to_string()])
.unwrap();
match result {
X86RedisValue::BulkString(Some(data)) => assert_eq!(data, b"value1"),
_ => panic!("Expected value1"),
}
}
#[test]
fn test_redis_cmd_del() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
redis.store.insert(
"key1".to_string(),
X86RedisValue::SimpleString("OK".to_string()),
);
let result = redis
.execute("conn1", "DEL", &["key1".to_string()])
.unwrap();
match result {
X86RedisValue::Integer(1) => {}
_ => panic!("Expected 1"),
}
assert!(!redis.store.contains_key("key1"));
}
#[test]
fn test_redis_cmd_incr_decr() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
let result = redis
.execute("conn1", "INCR", &["counter".to_string()])
.unwrap();
match result {
X86RedisValue::Integer(1) => {}
_ => panic!("Expected 1"),
}
let result = redis
.execute("conn1", "DECR", &["counter".to_string()])
.unwrap();
match result {
X86RedisValue::Integer(0) => {}
_ => panic!("Expected 0"),
}
}
#[test]
fn test_redis_cmd_lpush_rpush_lpop_rpop_llen() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
redis
.execute("conn1", "RPUSH", &["list".to_string(), "a".to_string()])
.unwrap();
redis
.execute("conn1", "RPUSH", &["list".to_string(), "b".to_string()])
.unwrap();
redis
.execute("conn1", "LPUSH", &["list".to_string(), "c".to_string()])
.unwrap();
let result = redis
.execute("conn1", "LLEN", &["list".to_string()])
.unwrap();
match result {
X86RedisValue::Integer(3) => {}
_ => panic!("Expected 3"),
}
let result = redis
.execute("conn1", "LPOP", &["list".to_string()])
.unwrap();
match result {
X86RedisValue::BulkString(Some(data)) => assert_eq!(data, b"c"),
_ => panic!("Expected c"),
}
let result = redis
.execute("conn1", "RPOP", &["list".to_string()])
.unwrap();
match result {
X86RedisValue::BulkString(Some(data)) => assert_eq!(data, b"b"),
_ => panic!("Expected b"),
}
}
#[test]
fn test_redis_cmd_hset_hget_hgetall() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
redis
.execute(
"conn1",
"HSET",
&["hash".to_string(), "f1".to_string(), "v1".to_string()],
)
.unwrap();
redis
.execute(
"conn1",
"HSET",
&["hash".to_string(), "f2".to_string(), "v2".to_string()],
)
.unwrap();
let result = redis
.execute("conn1", "HGET", &["hash".to_string(), "f1".to_string()])
.unwrap();
match result {
X86RedisValue::BulkString(Some(data)) => assert_eq!(data, b"v1"),
_ => panic!("Expected v1"),
}
let result = redis
.execute("conn1", "HGETALL", &["hash".to_string()])
.unwrap();
match result {
X86RedisValue::Array(items) => assert_eq!(items.len(), 4),
_ => panic!("Expected Array of 4"),
}
}
#[test]
fn test_redis_cmd_zadd_zrange() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
redis
.execute(
"conn1",
"ZADD",
&["zset".to_string(), "1.0".to_string(), "m1".to_string()],
)
.unwrap();
redis
.execute(
"conn1",
"ZADD",
&["zset".to_string(), "2.0".to_string(), "m2".to_string()],
)
.unwrap();
let result = redis
.execute(
"conn1",
"ZRANGE",
&["zset".to_string(), "0".to_string(), "-1".to_string()],
)
.unwrap();
match result {
X86RedisValue::Array(items) => assert_eq!(items.len(), 2),
_ => panic!("Expected Array of 2"),
}
}
#[test]
fn test_redis_cmd_publish_subscribe() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
redis
.execute("conn1", "SUBSCRIBE", &["channel1".to_string()])
.unwrap();
let result = redis
.execute(
"conn1",
"PUBLISH",
&["channel1".to_string(), "hello".to_string()],
)
.unwrap();
match result {
X86RedisValue::Integer(1) => {}
_ => panic!("Expected 1 subscriber"),
}
}
#[test]
fn test_odbc_support_creation() {
let odbc = X86ODBCSupport::new();
assert!(odbc.validate());
assert!(odbc.api.alloc_handle_available);
assert!(odbc.api.connect_available);
assert!(odbc.api.exec_direct_available);
}
#[test]
fn test_odbc_allocate_free_handle() {
let mut odbc = X86ODBCSupport::new();
let env = odbc
.allocate_handle(X86ODBCHandleType::Environment)
.unwrap();
assert!(odbc.environments.contains_key(&env));
let conn = odbc.allocate_handle(X86ODBCHandleType::Connection).unwrap();
assert!(odbc.connections.contains_key(&conn));
let stmt = odbc.allocate_handle(X86ODBCHandleType::Statement).unwrap();
assert!(odbc.statements.contains_key(&stmt));
odbc.free_handle(X86ODBCHandleType::Statement, stmt)
.unwrap();
assert!(!odbc.statements.contains_key(&stmt));
}
#[test]
fn test_odbc_connect_disconnect() {
let mut odbc = X86ODBCSupport::new();
let conn = odbc.allocate_handle(X86ODBCHandleType::Connection).unwrap();
odbc.connect(conn, "localhost", "user", "pass").unwrap();
assert!(odbc.connections.get(&conn).unwrap().connected);
odbc.disconnect(conn).unwrap();
assert!(!odbc.connections.get(&conn).unwrap().connected);
}
#[test]
fn test_odbc_exec_direct() {
let mut odbc = X86ODBCSupport::new();
let stmt = odbc.allocate_handle(X86ODBCHandleType::Statement).unwrap();
odbc.exec_direct(stmt, "SELECT 1").unwrap();
assert!(odbc.statements.get(&stmt).unwrap().executed);
}
#[test]
fn test_odbc_prepare_execute() {
let mut odbc = X86ODBCSupport::new();
let stmt = odbc.allocate_handle(X86ODBCHandleType::Statement).unwrap();
odbc.prepare(stmt, "SELECT ?").unwrap();
assert!(odbc.statements.get(&stmt).unwrap().prepared);
odbc.execute(stmt).unwrap();
assert!(odbc.statements.get(&stmt).unwrap().executed);
}
#[test]
fn test_odbc_fetch() {
let mut odbc = X86ODBCSupport::new();
let stmt = odbc.allocate_handle(X86ODBCHandleType::Statement).unwrap();
odbc.exec_direct(stmt, "SELECT 1").unwrap();
let row = odbc.fetch(stmt).unwrap();
assert!(row.is_none()); }
#[test]
fn test_odbc_num_result_cols() {
let mut odbc = X86ODBCSupport::new();
let stmt = odbc.allocate_handle(X86ODBCHandleType::Statement).unwrap();
let cols = odbc.num_result_cols(stmt).unwrap();
assert_eq!(cols, 0);
}
#[test]
fn test_odbc_type_mapping() {
let odbc = X86ODBCSupport::new();
assert_eq!(odbc.types.type_codes.get("INTEGER"), Some(&4i16));
assert_eq!(odbc.types.type_codes.get("VARCHAR"), Some(&12i16));
assert_eq!(odbc.types.type_codes.get("BIGINT"), Some(&(-5i16)));
}
#[test]
fn test_odbc_sql_type_constants() {
assert_eq!(odbc_sql_types::SQL_CHAR, 1);
assert_eq!(odbc_sql_types::SQL_INTEGER, 4);
assert_eq!(odbc_sql_types::SQL_VARCHAR, 12);
assert_eq!(odbc_sql_types::SQL_BIGINT, -5);
assert_eq!(odbc_sql_types::SQL_TYPE_DATE, 91);
assert_eq!(odbc_sql_types::SQL_TYPE_TIMESTAMP, 93);
}
#[test]
fn test_x86_database_full_workflow() {
let mut db = X86Database::new();
let result = db.validate();
assert!(result.valid);
let sql = "SELECT * FROM users WHERE id = 1";
let stmt = db.sql_support.compile(sql).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::Select);
let conn_id = db.sqlite_support.open(":memory:").unwrap();
assert!(db.sqlite_support.connections.contains_key(&conn_id));
db.sqlite_support.close(conn_id).unwrap();
}
#[test]
fn test_sql_parser_complex_select() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Select),
X86SQLToken::Identifier("id".to_string()),
X86SQLToken::Comma,
X86SQLToken::Identifier("name".to_string()),
X86SQLToken::Comma,
X86SQLToken::Identifier("email".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::From),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Where),
X86SQLToken::Identifier("id".to_string()),
X86SQLToken::Operator(X86SQLOperator::GreaterThan),
X86SQLToken::NumericLiteral("10".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::And),
X86SQLToken::Identifier("active".to_string()),
X86SQLToken::Operator(X86SQLOperator::Equal),
X86SQLToken::NumericLiteral("1".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Order),
X86SQLToken::Keyword(X86SQLKeyword::By),
X86SQLToken::Identifier("name".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Limit),
X86SQLToken::NumericLiteral("100".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::Select);
assert_eq!(stmt.conditions.len(), 2);
assert_eq!(stmt.order_by.len(), 1);
assert_eq!(stmt.limit, Some(100));
}
#[test]
fn test_sql_data_type_display() {
assert_eq!(X86SQLDataType::Integer.to_string(), "INTEGER");
assert_eq!(X86SQLDataType::Varchar.to_string(), "VARCHAR");
assert_eq!(X86SQLDataType::Timestamp.to_string(), "TIMESTAMP");
}
#[test]
fn test_sql_join_type_display() {
assert_eq!(X86SQLJoinType::Inner.to_string(), "INNER JOIN");
assert_eq!(X86SQLJoinType::Left.to_string(), "LEFT JOIN");
assert_eq!(X86SQLJoinType::Cross.to_string(), "CROSS JOIN");
}
#[test]
fn test_sql_subquery_type_display() {
assert_eq!(X86SQLSubqueryType::Scalar.to_string(), "SCALAR");
assert_eq!(X86SQLSubqueryType::Exists.to_string(), "EXISTS");
}
#[test]
fn test_sql_window_fn_display() {
assert_eq!(X86SQLWindowFnType::RowNumber.to_string(), "ROW_NUMBER");
assert_eq!(X86SQLWindowFnType::Rank.to_string(), "RANK");
assert_eq!(X86SQLWindowFnType::DenseRank.to_string(), "DENSE_RANK");
}
#[test]
fn test_sql_operator_display() {
assert_eq!(X86SQLOperator::Equal.to_string(), "=");
assert_eq!(X86SQLOperator::NotEqual.to_string(), "!=");
assert_eq!(X86SQLOperator::Like.to_string(), "LIKE");
}
#[test]
fn test_sql_index_type_display() {
assert_eq!(X86SQLIndexType::Btree.to_string(), "BTREE");
assert_eq!(X86SQLIndexType::Hash.to_string(), "HASH");
assert_eq!(X86SQLIndexType::Gin.to_string(), "GIN");
assert_eq!(X86SQLIndexType::Gist.to_string(), "GIST");
assert_eq!(X86SQLIndexType::SpGist.to_string(), "SP-GIST");
assert_eq!(X86SQLIndexType::Brin.to_string(), "BRIN");
}
#[test]
fn test_sql_isolation_level_display() {
assert_eq!(
X86SQLIsolationLevel::ReadCommitted.to_string(),
"READ COMMITTED"
);
}
#[test]
fn test_sqlite_page_types() {
assert_eq!(X86SQLitePageType::InteriorIndex as u8, 0x02);
assert_eq!(X86SQLitePageType::InteriorTable as u8, 0x05);
assert_eq!(X86SQLitePageType::LeafIndex as u8, 0x0A);
assert_eq!(X86SQLitePageType::LeafTable as u8, 0x0D);
}
#[test]
fn test_redis_resp2_parse_array() {
let redis = X86RedisSupport::new();
let data = b"*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n";
match redis.parse_resp2(data) {
Ok(X86RedisValue::Array(items)) => {
assert_eq!(items.len(), 2);
}
_ => panic!("Expected Array of 2 elements"),
}
}
#[test]
fn test_redis_resp3_null() {
let redis = X86RedisSupport::new();
let val = redis.parse_resp2(b"_\r\n").unwrap_or(X86RedisValue::Null);
match val {
X86RedisValue::Null => {}
_ => {}
}
}
#[test]
fn test_sql_compile_stats() {
let mut sql = X86SQLSupport::new();
let _ = sql.compile("SELECT 1");
assert!(sql.stats.statements_compiled > 0);
assert!(sql.stats.tokens_lexed > 0);
}
#[test]
fn test_sql_lexer_all_join_keywords() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer
.tokenize("INNER LEFT RIGHT FULL OUTER CROSS NATURAL JOIN")
.unwrap();
assert_eq!(tokens[0], X86SQLToken::Keyword(X86SQLKeyword::Inner));
assert_eq!(tokens[1], X86SQLToken::Keyword(X86SQLKeyword::Left));
assert_eq!(tokens[2], X86SQLToken::Keyword(X86SQLKeyword::Right));
assert_eq!(tokens[3], X86SQLToken::Keyword(X86SQLKeyword::Full));
assert_eq!(tokens[4], X86SQLToken::Keyword(X86SQLKeyword::Outer));
assert_eq!(tokens[5], X86SQLToken::Keyword(X86SQLKeyword::Cross));
assert_eq!(tokens[6], X86SQLToken::Keyword(X86SQLKeyword::Natural));
assert_eq!(tokens[7], X86SQLToken::Keyword(X86SQLKeyword::Join));
}
#[test]
fn test_sql_lexer_all_window_function_keywords() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer.tokenize("ROW_NUMBER RANK DENSE_RANK NTILE LAG LEAD OVER PARTITION UNBOUNDED PRECEDING FOLLOWING CURRENT ROWS RANGE").unwrap();
assert_eq!(tokens[0], X86SQLToken::Keyword(X86SQLKeyword::RowNumber));
assert_eq!(tokens[1], X86SQLToken::Keyword(X86SQLKeyword::Rank));
assert_eq!(tokens[2], X86SQLToken::Keyword(X86SQLKeyword::DenseRank));
assert_eq!(tokens[3], X86SQLToken::Keyword(X86SQLKeyword::Ntile));
assert_eq!(tokens[4], X86SQLToken::Keyword(X86SQLKeyword::Lag));
assert_eq!(tokens[5], X86SQLToken::Keyword(X86SQLKeyword::Lead));
assert_eq!(tokens[6], X86SQLToken::Keyword(X86SQLKeyword::Over));
assert_eq!(tokens[7], X86SQLToken::Keyword(X86SQLKeyword::Partition));
assert_eq!(tokens[8], X86SQLToken::Keyword(X86SQLKeyword::Unbounded));
assert_eq!(tokens[9], X86SQLToken::Keyword(X86SQLKeyword::Preceding));
assert_eq!(tokens[10], X86SQLToken::Keyword(X86SQLKeyword::Following));
assert_eq!(tokens[11], X86SQLToken::Keyword(X86SQLKeyword::Current));
assert_eq!(tokens[12], X86SQLToken::Keyword(X86SQLKeyword::Rows));
assert_eq!(tokens[13], X86SQLToken::Keyword(X86SQLKeyword::Range));
}
#[test]
fn test_sql_lexer_all_aggregate_functions() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer
.tokenize("COUNT SUM AVG MIN MAX GROUP BY HAVING")
.unwrap();
assert_eq!(tokens[0], X86SQLToken::Keyword(X86SQLKeyword::Count));
assert_eq!(tokens[1], X86SQLToken::Keyword(X86SQLKeyword::Sum));
assert_eq!(tokens[2], X86SQLToken::Keyword(X86SQLKeyword::Avg));
assert_eq!(tokens[3], X86SQLToken::Keyword(X86SQLKeyword::Min));
assert_eq!(tokens[4], X86SQLToken::Keyword(X86SQLKeyword::Max));
assert_eq!(tokens[5], X86SQLToken::Keyword(X86SQLKeyword::Group));
assert_eq!(tokens[6], X86SQLToken::Keyword(X86SQLKeyword::By));
assert_eq!(tokens[7], X86SQLToken::Keyword(X86SQLKeyword::Having));
}
#[test]
fn test_sql_lexer_all_type_keywords() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer.tokenize("INTEGER BIGINT SMALLINT TINYINT MEDIUMINT REAL DOUBLE FLOAT DECIMAL NUMERIC BOOLEAN CHAR VARCHAR TEXT BLOB JSON JSONB UUID BYTEA INTERVAL DATE TIME TIMESTAMP").unwrap();
assert_eq!(tokens[0], X86SQLToken::Keyword(X86SQLKeyword::Integer));
assert_eq!(tokens[1], X86SQLToken::Keyword(X86SQLKeyword::BigInt));
assert_eq!(tokens[2], X86SQLToken::Keyword(X86SQLKeyword::SmallInt));
assert_eq!(tokens[3], X86SQLToken::Keyword(X86SQLKeyword::TinyInt));
assert_eq!(tokens[4], X86SQLToken::Keyword(X86SQLKeyword::MediumInt));
assert_eq!(tokens[5], X86SQLToken::Keyword(X86SQLKeyword::Real));
assert_eq!(tokens[6], X86SQLToken::Keyword(X86SQLKeyword::Double));
assert_eq!(tokens[7], X86SQLToken::Keyword(X86SQLKeyword::Float));
assert_eq!(tokens[8], X86SQLToken::Keyword(X86SQLKeyword::Decimal));
assert_eq!(tokens[9], X86SQLToken::Keyword(X86SQLKeyword::Numeric));
assert_eq!(tokens[10], X86SQLToken::Keyword(X86SQLKeyword::Boolean));
assert_eq!(tokens[11], X86SQLToken::Keyword(X86SQLKeyword::Char));
assert_eq!(tokens[12], X86SQLToken::Keyword(X86SQLKeyword::Varchar));
assert_eq!(tokens[13], X86SQLToken::Keyword(X86SQLKeyword::Text));
assert_eq!(tokens[14], X86SQLToken::Keyword(X86SQLKeyword::Blob));
assert_eq!(tokens[15], X86SQLToken::Keyword(X86SQLKeyword::Json));
assert_eq!(tokens[16], X86SQLToken::Keyword(X86SQLKeyword::Jsonb));
assert_eq!(tokens[17], X86SQLToken::Keyword(X86SQLKeyword::Uuid));
assert_eq!(tokens[18], X86SQLToken::Keyword(X86SQLKeyword::Bytea));
assert_eq!(tokens[19], X86SQLToken::Keyword(X86SQLKeyword::Interval));
assert_eq!(tokens[20], X86SQLToken::Keyword(X86SQLKeyword::Date));
assert_eq!(tokens[21], X86SQLToken::Keyword(X86SQLKeyword::Time));
assert_eq!(tokens[22], X86SQLToken::Keyword(X86SQLKeyword::Timestamp));
}
#[test]
fn test_sql_lexer_all_transaction_keywords() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer.tokenize("BEGIN COMMIT ROLLBACK SAVEPOINT RELEASE TRANSACTION ISOLATION LEVEL SERIALIZABLE REPEATABLE READ COMMITTED UNCOMMITTED").unwrap();
assert_eq!(tokens[0], X86SQLToken::Keyword(X86SQLKeyword::Begin));
assert_eq!(tokens[1], X86SQLToken::Keyword(X86SQLKeyword::Commit));
assert_eq!(tokens[2], X86SQLToken::Keyword(X86SQLKeyword::Rollback));
assert_eq!(tokens[3], X86SQLToken::Keyword(X86SQLKeyword::Savepoint));
assert_eq!(tokens[4], X86SQLToken::Keyword(X86SQLKeyword::Release));
assert_eq!(tokens[5], X86SQLToken::Keyword(X86SQLKeyword::Transaction));
assert_eq!(tokens[6], X86SQLToken::Keyword(X86SQLKeyword::Isolation));
assert_eq!(tokens[7], X86SQLToken::Keyword(X86SQLKeyword::Level));
assert_eq!(tokens[8], X86SQLToken::Keyword(X86SQLKeyword::Serializable));
assert_eq!(tokens[9], X86SQLToken::Keyword(X86SQLKeyword::Repeatable));
assert_eq!(tokens[10], X86SQLToken::Keyword(X86SQLKeyword::Read));
assert_eq!(tokens[11], X86SQLToken::Keyword(X86SQLKeyword::Committed));
assert_eq!(tokens[12], X86SQLToken::Keyword(X86SQLKeyword::Uncommitted));
}
#[test]
fn test_sql_lexer_operator_two_char() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer.tokenize("<= >= <> !=").unwrap();
assert_eq!(tokens[0], X86SQLToken::Operator(X86SQLOperator::LessEqual));
assert_eq!(
tokens[1],
X86SQLToken::Operator(X86SQLOperator::GreaterEqual)
);
assert_eq!(tokens[2], X86SQLToken::Operator(X86SQLOperator::NotEqual));
assert_eq!(tokens[3], X86SQLToken::Operator(X86SQLOperator::NotEqual));
}
#[test]
fn test_sql_lexer_float_numbers() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer.tokenize("SELECT 3.14").unwrap();
assert_eq!(tokens[1], X86SQLToken::NumericLiteral("3.14".to_string()));
let tokens = lexer.tokenize("SELECT 1.5e10").unwrap();
assert_eq!(tokens[1], X86SQLToken::NumericLiteral("1.5e10".to_string()));
let tokens = lexer.tokenize("SELECT .5").unwrap();
assert_eq!(tokens[1], X86SQLToken::NumericLiteral(".5".to_string()));
}
#[test]
fn test_sql_lexer_quoted_identifier() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer.tokenize("SELECT \"my column\" FROM t").unwrap();
assert_eq!(tokens[1], X86SQLToken::Identifier("my column".to_string()));
}
#[test]
fn test_sql_lexer_escaped_string() {
let mut lexer = X86SQLLexer::new();
let tokens = lexer.tokenize("SELECT 'it''s'").unwrap();
assert_eq!(tokens[1], X86SQLToken::StringLiteral("it's".to_string()));
}
#[test]
fn test_sql_parser_select_with_join() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Select),
X86SQLToken::Star,
X86SQLToken::Keyword(X86SQLKeyword::From),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Inner),
X86SQLToken::Keyword(X86SQLKeyword::Join),
X86SQLToken::Identifier("orders".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::On),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::Dot,
X86SQLToken::Identifier("id".to_string()),
X86SQLToken::Operator(X86SQLOperator::Equal),
X86SQLToken::Identifier("orders".to_string()),
X86SQLToken::Dot,
X86SQLToken::Identifier("user_id".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.joins.len(), 1);
assert_eq!(stmt.joins[0].table, "orders");
}
#[test]
fn test_sql_parser_select_with_left_join() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Select),
X86SQLToken::Star,
X86SQLToken::Keyword(X86SQLKeyword::From),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Left),
X86SQLToken::Keyword(X86SQLKeyword::Join),
X86SQLToken::Identifier("profiles".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::On),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::Dot,
X86SQLToken::Identifier("id".to_string()),
X86SQLToken::Operator(X86SQLOperator::Equal),
X86SQLToken::Identifier("profiles".to_string()),
X86SQLToken::Dot,
X86SQLToken::Identifier("user_id".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.joins.len(), 1);
}
#[test]
fn test_sql_parser_select_with_group_by_and_having() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Select),
X86SQLToken::Identifier("dept_id".to_string()),
X86SQLToken::Comma,
X86SQLToken::Identifier("COUNT".to_string()),
X86SQLToken::LeftParen,
X86SQLToken::Star,
X86SQLToken::RightParen,
X86SQLToken::Keyword(X86SQLKeyword::From),
X86SQLToken::Identifier("employees".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Group),
X86SQLToken::Keyword(X86SQLKeyword::By),
X86SQLToken::Identifier("dept_id".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Having),
X86SQLToken::Identifier("COUNT".to_string()),
X86SQLToken::LeftParen,
X86SQLToken::Star,
X86SQLToken::RightParen,
X86SQLToken::Operator(X86SQLOperator::GreaterThan),
X86SQLToken::NumericLiteral("5".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.group_by, vec!["dept_id".to_string()]);
assert!(stmt.having.is_some());
}
#[test]
fn test_sql_parser_create_table_with_references() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Create),
X86SQLToken::Keyword(X86SQLKeyword::Table),
X86SQLToken::Identifier("orders".to_string()),
X86SQLToken::LeftParen,
X86SQLToken::Identifier("id".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Integer),
X86SQLToken::Keyword(X86SQLKeyword::Primary),
X86SQLToken::Keyword(X86SQLKeyword::Key),
X86SQLToken::Comma,
X86SQLToken::Identifier("user_id".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Integer),
X86SQLToken::Keyword(X86SQLKeyword::Not),
X86SQLToken::Keyword(X86SQLKeyword::Null),
X86SQLToken::Keyword(X86SQLKeyword::References),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::LeftParen,
X86SQLToken::Identifier("id".to_string()),
X86SQLToken::RightParen,
X86SQLToken::RightParen,
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::CreateTable);
assert_eq!(stmt.columns.len(), 2);
assert!(stmt.columns[1].references.is_some());
}
#[test]
fn test_sql_parser_select_with_order_by_desc() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Select),
X86SQLToken::Star,
X86SQLToken::Keyword(X86SQLKeyword::From),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Order),
X86SQLToken::Keyword(X86SQLKeyword::By),
X86SQLToken::Identifier("created_at".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Desc),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.order_by.len(), 1);
assert!(!stmt.order_by[0].ascending);
}
#[test]
fn test_sql_parser_select_with_limit_offset() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Select),
X86SQLToken::Star,
X86SQLToken::Keyword(X86SQLKeyword::From),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Limit),
X86SQLToken::NumericLiteral("10".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Offset),
X86SQLToken::NumericLiteral("20".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.limit, Some(10));
assert_eq!(stmt.offset, Some(20));
}
#[test]
fn test_sql_parser_cte() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::With),
X86SQLToken::Identifier("cte".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::As),
X86SQLToken::LeftParen,
X86SQLToken::Keyword(X86SQLKeyword::Select),
X86SQLToken::NumericLiteral("1".to_string()),
X86SQLToken::RightParen,
X86SQLToken::Keyword(X86SQLKeyword::Select),
X86SQLToken::Star,
X86SQLToken::Keyword(X86SQLKeyword::From),
X86SQLToken::Identifier("cte".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.ctes.len(), 1);
assert_eq!(stmt.ctes[0].name, "cte");
assert!(!stmt.ctes[0].is_recursive);
}
#[test]
fn test_sql_parser_recursive_cte() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::With),
X86SQLToken::Keyword(X86SQLKeyword::Recursive),
X86SQLToken::Identifier("nums".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::As),
X86SQLToken::LeftParen,
X86SQLToken::Keyword(X86SQLKeyword::Select),
X86SQLToken::NumericLiteral("1".to_string()),
X86SQLToken::RightParen,
X86SQLToken::Keyword(X86SQLKeyword::Select),
X86SQLToken::Star,
X86SQLToken::Keyword(X86SQLKeyword::From),
X86SQLToken::Identifier("nums".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.ctes.len(), 1);
assert!(stmt.ctes[0].is_recursive);
}
#[test]
fn test_sql_parser_create_table_if_not_exists() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Create),
X86SQLToken::Keyword(X86SQLKeyword::Table),
X86SQLToken::Keyword(X86SQLKeyword::If),
X86SQLToken::Keyword(X86SQLKeyword::Not),
X86SQLToken::Keyword(X86SQLKeyword::Exists),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::LeftParen,
X86SQLToken::Identifier("id".to_string()),
X86SQLToken::Keyword(X86SQLKeyword::Integer),
X86SQLToken::RightParen,
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.table_name, Some("users".to_string()));
}
#[test]
fn test_sql_parser_drop_table_if_exists() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Drop),
X86SQLToken::Keyword(X86SQLKeyword::Table),
X86SQLToken::Keyword(X86SQLKeyword::If),
X86SQLToken::Keyword(X86SQLKeyword::Exists),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::DropTable);
}
#[test]
fn test_sql_parser_alter_table() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Alter),
X86SQLToken::Keyword(X86SQLKeyword::Table),
X86SQLToken::Identifier("users".to_string()),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::AlterTable);
}
#[test]
fn test_sql_parser_explain() {
let mut parser = X86SQLParser::new();
let tokens = vec![
X86SQLToken::Keyword(X86SQLKeyword::Explain),
X86SQLToken::EOF,
];
let stmt = parser.parse(&tokens).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::Explain);
}
#[test]
fn test_sql_statement_type_display_all() {
let types = [
(X86SQLStatementType::Select, "SELECT"),
(X86SQLStatementType::Insert, "INSERT"),
(X86SQLStatementType::Update, "UPDATE"),
(X86SQLStatementType::Delete, "DELETE"),
(X86SQLStatementType::CreateTable, "CREATE TABLE"),
(X86SQLStatementType::DropTable, "DROP TABLE"),
(X86SQLStatementType::AlterTable, "ALTER TABLE"),
(X86SQLStatementType::CreateIndex, "CREATE INDEX"),
(X86SQLStatementType::DropIndex, "DROP INDEX"),
(X86SQLStatementType::Begin, "BEGIN"),
(X86SQLStatementType::Commit, "COMMIT"),
(X86SQLStatementType::Rollback, "ROLLBACK"),
(X86SQLStatementType::Savepoint, "SAVEPOINT"),
(X86SQLStatementType::Release, "RELEASE"),
(X86SQLStatementType::Explain, "EXPLAIN"),
];
for (t, expected) in &types {
assert_eq!(t.to_string(), *expected);
}
}
#[test]
fn test_sql_compile_error_display() {
let e = X86SQLCompileError::LexerError("bad token".to_string());
assert!(e.to_string().contains("Lexer error"));
let e = X86SQLCompileError::ParseError("unexpected".to_string());
assert!(e.to_string().contains("Parse error"));
let e = X86SQLCompileError::TypeError("type mismatch".to_string());
assert!(e.to_string().contains("Type error"));
let e = X86SQLCompileError::SemanticError("invalid".to_string());
assert!(e.to_string().contains("Semantic error"));
let e = X86SQLCompileError::Unsupported("feature".to_string());
assert!(e.to_string().contains("Unsupported"));
}
#[test]
fn test_sql_type_system_all_types() {
let ts = X86SQLTypeSystem::new();
let types = &[
("INT", X86SQLDataType::Integer),
("INT8", X86SQLDataType::BigInt),
("INT2", X86SQLDataType::SmallInt),
("FLOAT4", X86SQLDataType::Real),
("FLOAT8", X86SQLDataType::Double),
("BOOLEAN", X86SQLDataType::Boolean),
("TEXT", X86SQLDataType::Text),
("JSON", X86SQLDataType::Json),
("UUID", X86SQLDataType::Uuid),
];
for (name, expected) in types {
assert_eq!(ts.resolve(name), *expected, "Failed for type {}", name);
}
}
#[test]
fn test_sql_data_type_from_keyword() {
assert_eq!(
X86SQLDataType::from_keyword(X86SQLKeyword::Integer),
X86SQLDataType::Integer
);
assert_eq!(
X86SQLDataType::from_keyword(X86SQLKeyword::BigInt),
X86SQLDataType::BigInt
);
assert_eq!(
X86SQLDataType::from_keyword(X86SQLKeyword::Json),
X86SQLDataType::Json
);
assert_eq!(
X86SQLDataType::from_keyword(X86SQLKeyword::Select),
X86SQLDataType::Unknown
);
}
#[test]
fn test_sqlite_page_structure() {
let page = X86SQLitePage {
page_number: 1,
page_type: X86SQLitePageType::LeafTable,
free_block_offset: 0,
cell_count: 2,
cell_content_offset: 4000,
fragmented_free_bytes: 0,
right_child: None,
cells: vec![
X86SQLiteCell {
cell_type: X86SQLiteCellType::TableLeaf,
left_child: None,
row_id: Some(1),
payload: b"data1".to_vec(),
overflow_pages: vec![],
},
X86SQLiteCell {
cell_type: X86SQLiteCellType::TableLeaf,
left_child: None,
row_id: Some(2),
payload: b"data2".to_vec(),
overflow_pages: vec![],
},
],
};
assert_eq!(page.page_number, 1);
assert_eq!(page.cell_count, 2);
assert_eq!(page.cells.len(), 2);
}
#[test]
fn test_sqlite_wal_frame_structure() {
let frame = X86SQLiteWALFrame {
frame_number: 1,
page_number: 2,
database_size: 5,
salt1: 12345,
salt2: 67890,
checksum1: 11111,
checksum2: 22222,
page_data: vec![0u8; 4096],
};
assert_eq!(frame.frame_number, 1);
assert_eq!(frame.page_number, 2);
assert_eq!(frame.page_data.len(), 4096);
}
#[test]
fn test_sqlite_wal_header() {
let header = X86SQLiteWALHeader {
magic: [0x37, 0x7F, 0x06, 0x82],
file_format: 3007000,
page_size: 4096,
checkpoint_seq: 1,
salt1: 12345,
salt2: 67890,
checksum1: 11111,
checksum2: 22222,
};
assert_eq!(header.page_size, 4096);
assert_eq!(header.file_format, 3007000);
}
#[test]
fn test_sqlite_prepared_stmt() {
let stmt = X86SQLitePreparedStmt {
conn_id: 42,
sql: "SELECT ? FROM t".to_string(),
param_count: 1,
is_explain: false,
};
assert_eq!(stmt.conn_id, 42);
assert_eq!(stmt.param_count, 1);
assert!(!stmt.is_explain);
}
#[test]
fn test_sqlite_checkpoint_result() {
let result = X86SQLiteCheckpointResult {
pages_written: 100,
pages_wal: 200,
pages_checkpointed: 100,
mode: X86SQLiteCheckpointMode::Full,
};
assert_eq!(result.pages_written, 100);
assert_eq!(result.pages_wal, 200);
}
#[test]
fn test_sqlite_query_plan() {
let plan = X86SQLiteQueryPlan {
plan_id: 1,
sql: "SELECT * FROM t".to_string(),
plan_text: "SCAN TABLE t".to_string(),
estimated_cost: 10.0,
estimated_rows: 1000,
uses_index: false,
uses_temp_table: false,
uses_filesort: true,
scan_type: "full scan".to_string(),
};
assert!(plan.uses_filesort);
assert_eq!(plan.estimated_rows, 1000);
}
#[test]
fn test_sqlite_stat_table() {
let stat = X86SQLiteStatTable {
table_name: "users".to_string(),
index_name: Some("idx_users_email".to_string()),
stat: "100 10".to_string(),
};
assert_eq!(stat.table_name, "users");
assert!(stat.index_name.is_some());
}
#[test]
fn test_sqlite_compile_flags() {
let sqlite = X86SQLiteSupport::new();
assert!(sqlite.compile_flags.len() > 5);
assert!(sqlite
.compile_flags
.contains(&"SQLITE_ENABLE_FTS5".to_string()));
assert!(sqlite
.compile_flags
.contains(&"SQLITE_ENABLE_JSON1".to_string()));
}
#[test]
fn test_sqlite_exec_with_closed_connection() {
let mut sqlite = X86SQLiteSupport::new();
let conn_id = sqlite.open(":memory:").unwrap();
sqlite.close(conn_id).unwrap();
let result = sqlite.exec(conn_id, "SELECT 1");
assert!(result.is_err());
}
#[test]
fn test_sqlite_wal_checkpoint_disabled() {
let mut sqlite = X86SQLiteSupport::new();
sqlite.wal.enabled = false;
let result = sqlite.wal_checkpoint();
assert!(result.is_err());
}
#[test]
fn test_sqlite_all_page_types() {
assert_eq!(X86SQLitePageType::InteriorIndex as u8, 0x02);
assert_eq!(X86SQLitePageType::InteriorTable as u8, 0x05);
assert_eq!(X86SQLitePageType::LeafIndex as u8, 0x0A);
assert_eq!(X86SQLitePageType::LeafTable as u8, 0x0D);
}
#[test]
fn test_postgresql_conn_build_string_no_password() {
let pg = X86PostgreSQLSupport::new();
let conn_str = pg.build_conn_string("pg.example.com", 5432, "analytics", "reader", None);
assert!(conn_str.contains("host=pg.example.com"));
assert!(!conn_str.contains("password"));
}
#[test]
fn test_postgresql_conn_status_values() {
let statuses = [
X86PostgreSQLConnStatus::Ok,
X86PostgreSQLConnStatus::Bad,
X86PostgreSQLConnStatus::Started,
X86PostgreSQLConnStatus::AuthOk,
];
assert_eq!(statuses.len(), 4);
}
#[test]
fn test_postgresql_trans_status() {
assert_eq!(X86PostgreSQLTransStatus::Idle as u8, 0);
assert_eq!(X86PostgreSQLTransStatus::InTransaction as u8, 2);
}
#[test]
fn test_postgresql_protocol_all_oids() {
assert!(pg_oids::BOOL_OID > 0);
assert!(pg_oids::JSONB_OID > 0);
assert_ne!(pg_oids::INT4_OID, pg_oids::INT8_OID);
assert_ne!(pg_oids::TEXT_OID, pg_oids::VARCHAR_OID);
}
#[test]
fn test_postgresql_column_desc() {
let col = X86PGColumnDesc {
name: "id".to_string(),
table_oid: 99999,
col_attr: 1,
type_oid: pg_oids::INT4_OID,
type_size: 4,
type_modifier: -1,
format_code: 0,
};
assert_eq!(col.name, "id");
assert_eq!(col.type_oid, 23);
}
#[test]
fn test_postgresql_copy_binary_format_multiple_tuples() {
let pg = X86PostgreSQLSupport::new();
let tuples = vec![
vec![Some(b"row1_col1".to_vec()), Some(b"row1_col2".to_vec())],
vec![Some(b"row2_col1".to_vec()), None],
];
let format = pg.copy_binary_format(tuples);
assert_eq!(format.tuples.len(), 2);
assert_eq!(format.tuples[0].field_count, 2);
assert_eq!(format.tuples[1].field_count, 2);
}
#[test]
fn test_postgresql_copy_binary_trailer() {
let pg = X86PostgreSQLSupport::new();
let format = pg.copy_binary_format(vec![]);
assert!(format.trailer.is_some());
assert_eq!(format.trailer.unwrap().marker, -1);
}
#[test]
fn test_postgresql_protocol_describe_variants() {
let v1 = X86PGDescribeVariant::Statement;
let v2 = X86PGDescribeVariant::Portal;
assert_ne!(v1, v2);
}
#[test]
fn test_postgresql_protocol_close_variants() {
let v1 = X86PGCloseVariant::Statement;
let v2 = X86PGCloseVariant::Portal;
assert_ne!(v1, v2);
}
#[test]
fn test_postgresql_protocol_states() {
let states = [
X86PGProtocolState::Startup,
X86PGProtocolState::Authentication,
X86PGProtocolState::Ready,
X86PGProtocolState::Idle,
X86PGProtocolState::Busy,
X86PGProtocolState::CopyIn,
X86PGProtocolState::CopyOut,
X86PGProtocolState::Error,
];
assert_eq!(states.len(), 8);
}
#[test]
fn test_mysql_connect_with_database() {
let mut mysql = X86MySQLSupport::new();
let key = mysql
.connect(
"db.example.com",
3306,
"app_user",
Some("secret"),
Some("production"),
)
.unwrap();
let conn = mysql.connections.get(&key).unwrap();
assert_eq!(conn.host, "db.example.com");
assert_eq!(conn.database, Some("production".to_string()));
assert!(conn.password.is_some());
}
#[test]
fn test_mysql_prepare_multiple_params() {
let mut mysql = X86MySQLSupport::new();
let key = mysql
.connect("localhost", 3306, "root", None, None)
.unwrap();
let stmt = mysql
.stmt_prepare(&key, "INSERT INTO t (a, b, c) VALUES (?, ?, ?)")
.unwrap();
assert_eq!(stmt.param_count, 3);
}
#[test]
fn test_mysql_stmt_bind_param_out_of_range() {
let mut stmt = X86MySQLPreparedStmt {
stmt_id: 1,
sql: "SELECT ?".to_string(),
param_count: 1,
param_bindings: Vec::new(),
result_bindings: Vec::new(),
field_count: 0,
warning_count: 0,
};
let result = X86MySQLSupport::stmt_bind_param(
&mut stmt,
5,
mysql_field_types::MYSQL_TYPE_LONG,
&[0u8; 4],
false,
);
assert!(result.is_err());
}
#[test]
fn test_mysql_all_field_types_have_codes() {
let codes = [
mysql_field_types::MYSQL_TYPE_DECIMAL,
mysql_field_types::MYSQL_TYPE_TINY,
mysql_field_types::MYSQL_TYPE_SHORT,
mysql_field_types::MYSQL_TYPE_LONG,
mysql_field_types::MYSQL_TYPE_FLOAT,
mysql_field_types::MYSQL_TYPE_DOUBLE,
mysql_field_types::MYSQL_TYPE_NULL,
mysql_field_types::MYSQL_TYPE_TIMESTAMP,
mysql_field_types::MYSQL_TYPE_LONGLONG,
mysql_field_types::MYSQL_TYPE_INT24,
mysql_field_types::MYSQL_TYPE_DATE,
mysql_field_types::MYSQL_TYPE_TIME,
mysql_field_types::MYSQL_TYPE_DATETIME,
mysql_field_types::MYSQL_TYPE_YEAR,
mysql_field_types::MYSQL_TYPE_VARCHAR,
mysql_field_types::MYSQL_TYPE_BIT,
mysql_field_types::MYSQL_TYPE_JSON,
mysql_field_types::MYSQL_TYPE_ENUM,
mysql_field_types::MYSQL_TYPE_SET,
mysql_field_types::MYSQL_TYPE_TINY_BLOB,
mysql_field_types::MYSQL_TYPE_MEDIUM_BLOB,
mysql_field_types::MYSQL_TYPE_LONG_BLOB,
mysql_field_types::MYSQL_TYPE_BLOB,
mysql_field_types::MYSQL_TYPE_GEOMETRY,
];
assert_eq!(codes.len(), 24);
}
#[test]
fn test_mysql_conn_status() {
assert_eq!(X86MySQLConnStatus::Connected as u8, 0);
assert_eq!(X86MySQLConnStatus::Disconnected as u8, 1);
assert_eq!(X86MySQLConnStatus::Error as u8, 2);
assert_eq!(X86MySQLConnStatus::Connecting as u8, 3);
}
#[test]
fn test_redis_serialize_resp2_big_number() {
let redis = X86RedisSupport::new();
let val = X86RedisValue::BigNumber("99999999999999999999".to_string());
let bytes = redis.serialize_resp2(&val);
assert!(bytes.starts_with(b"("));
assert!(bytes.ends_with(b"\r\n"));
}
#[test]
fn test_redis_serialize_resp3_null() {
let redis = X86RedisSupport::new();
let val = X86RedisValue::Null;
let bytes = redis.serialize_resp2(&val);
assert_eq!(bytes, b"_\r\n");
}
#[test]
fn test_redis_cmd_exists() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
redis.store.insert(
"key1".to_string(),
X86RedisValue::SimpleString("OK".to_string()),
);
let result = redis
.execute("conn1", "EXISTS", &["key1".to_string(), "key2".to_string()])
.unwrap();
match result {
X86RedisValue::Integer(1) => {}
_ => panic!("Expected 1"),
}
}
#[test]
fn test_redis_cmd_expire_ttl() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
redis.store.insert(
"key1".to_string(),
X86RedisValue::SimpleString("OK".to_string()),
);
let result = redis
.execute("conn1", "TTL", &["key1".to_string()])
.unwrap();
match result {
X86RedisValue::Integer(-1) => {}
_ => panic!("Expected -1 (no expiry)"),
}
let result = redis
.execute("conn1", "TTL", &["nonexistent".to_string()])
.unwrap();
match result {
X86RedisValue::Integer(-2) => {}
_ => panic!("Expected -2 (not found)"),
}
}
#[test]
fn test_redis_cmd_sadd_smembers_sismember() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
redis
.execute(
"conn1",
"SADD",
&["myset".to_string(), "a".to_string(), "b".to_string()],
)
.unwrap();
let result = redis
.execute("conn1", "SMEMBERS", &["myset".to_string()])
.unwrap();
match result {
X86RedisValue::Array(items) => assert_eq!(items.len(), 2),
_ => panic!("Expected Array"),
}
let result = redis
.execute(
"conn1",
"SISMEMBER",
&["myset".to_string(), "a".to_string()],
)
.unwrap();
match result {
X86RedisValue::Integer(1) => {}
_ => panic!("Expected 1"),
}
}
#[test]
fn test_redis_cmd_lrange() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
redis
.execute(
"conn1",
"RPUSH",
&[
"list".to_string(),
"x".to_string(),
"y".to_string(),
"z".to_string(),
],
)
.unwrap();
let result = redis
.execute(
"conn1",
"LRANGE",
&["list".to_string(), "0".to_string(), "1".to_string()],
)
.unwrap();
match result {
X86RedisValue::Array(items) => assert_eq!(items.len(), 2),
_ => panic!("Expected Array of 2"),
}
let result = redis
.execute(
"conn1",
"LRANGE",
&["list".to_string(), "-2".to_string(), "-1".to_string()],
)
.unwrap();
match result {
X86RedisValue::Array(items) => assert_eq!(items.len(), 2),
_ => panic!("Expected Array of 2"),
}
}
#[test]
fn test_redis_cmd_zrange_scores() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
redis
.execute(
"conn1",
"ZADD",
&["z".to_string(), "10.0".to_string(), "m1".to_string()],
)
.unwrap();
redis
.execute(
"conn1",
"ZADD",
&["z".to_string(), "20.0".to_string(), "m2".to_string()],
)
.unwrap();
let result = redis
.execute(
"conn1",
"ZRANGE",
&["z".to_string(), "0".to_string(), "-1".to_string()],
)
.unwrap();
match result {
X86RedisValue::Array(items) => assert_eq!(items.len(), 2),
_ => panic!("Expected Array of 2"),
}
}
#[test]
fn test_redis_cmd_publish_no_subscribers() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
let result = redis
.execute(
"conn1",
"PUBLISH",
&["empty".to_string(), "msg".to_string()],
)
.unwrap();
match result {
X86RedisValue::Integer(0) => {}
_ => panic!("Expected 0 subscribers"),
}
}
#[test]
fn test_redis_stream_entry() {
let entry = X86RedisStreamEntry {
id: "12345-0".to_string(),
fields: vec![
("name".to_string(), "Alice".to_string()),
("age".to_string(), "30".to_string()),
],
};
assert_eq!(entry.id, "12345-0");
assert_eq!(entry.fields.len(), 2);
}
#[test]
fn test_redis_command_definitions() {
let redis = X86RedisSupport::new();
let get_cmd = redis.commands.get("GET").unwrap();
assert_eq!(get_cmd.arity, 2);
assert!(get_cmd.flags.contains(&"readonly".to_string()));
let set_cmd = redis.commands.get("SET").unwrap();
assert_eq!(set_cmd.arity, -3);
assert!(set_cmd.flags.contains(&"write".to_string()));
let exists_cmd = redis.commands.get("EXISTS").unwrap();
assert_eq!(exists_cmd.arity, -2);
}
#[test]
fn test_redis_resp3_verbatim() {
let val = X86RedisValue::VerbatimString {
encoding: "txt".to_string(),
data: "hello".to_string(),
};
match val {
X86RedisValue::VerbatimString { encoding, data } => {
assert_eq!(encoding, "txt");
assert_eq!(data, "hello");
}
_ => panic!("Expected VerbatimString"),
}
}
#[test]
fn test_redis_resp3_map() {
let val = X86RedisValue::Map(vec![(
X86RedisValue::SimpleString("key".to_string()),
X86RedisValue::SimpleString("val".to_string()),
)]);
match val {
X86RedisValue::Map(pairs) => assert_eq!(pairs.len(), 1),
_ => panic!("Expected Map"),
}
}
#[test]
fn test_redis_resp3_set_type() {
let val = X86RedisValue::Set(vec![
X86RedisValue::SimpleString("a".to_string()),
X86RedisValue::SimpleString("b".to_string()),
]);
match val {
X86RedisValue::Set(items) => assert_eq!(items.len(), 2),
_ => panic!("Expected Set"),
}
}
#[test]
fn test_redis_update_existing_key() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
redis
.execute("conn1", "SET", &["k".to_string(), "v1".to_string()])
.unwrap();
redis
.execute("conn1", "SET", &["k".to_string(), "v2".to_string()])
.unwrap();
let result = redis.execute("conn1", "GET", &["k".to_string()]).unwrap();
match result {
X86RedisValue::BulkString(Some(data)) => assert_eq!(data, b"v2"),
_ => panic!("Expected v2"),
}
}
#[test]
fn test_redis_incr_new_key() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
let result = redis
.execute("conn1", "INCR", &["new_counter".to_string()])
.unwrap();
match result {
X86RedisValue::Integer(1) => {}
_ => panic!("Expected 1"),
}
}
#[test]
fn test_redis_hset_new_field() {
let mut redis = X86RedisSupport::new();
redis.connections.insert(
"conn1".to_string(),
X86RedisConnection {
id: "conn1".to_string(),
host: "localhost".to_string(),
port: 6379,
database: 0,
authenticated: true,
subscribed_channels: Vec::new(),
in_transaction: false,
transaction_queue: Vec::new(),
last_error: None,
},
);
let result = redis
.execute(
"conn1",
"HSET",
&["h".to_string(), "f1".to_string(), "v1".to_string()],
)
.unwrap();
match result {
X86RedisValue::Integer(1) => {}
_ => panic!("Expected 1 (created)"),
}
let result = redis
.execute(
"conn1",
"HSET",
&["h".to_string(), "f1".to_string(), "v2".to_string()],
)
.unwrap();
match result {
X86RedisValue::Integer(0) => {}
_ => panic!("Expected 0 (updated)"),
}
let result = redis
.execute("conn1", "HGET", &["h".to_string(), "f1".to_string()])
.unwrap();
match result {
X86RedisValue::BulkString(Some(data)) => assert_eq!(data, b"v2"),
_ => panic!("Expected v2"),
}
}
#[test]
fn test_odbc_descriptor_handle() {
let mut odbc = X86ODBCSupport::new();
let desc = odbc.allocate_handle(X86ODBCHandleType::Descriptor).unwrap();
assert!(desc > 0);
}
#[test]
fn test_odbc_connect_with_connection_string() {
let mut odbc = X86ODBCSupport::new();
let conn = odbc.allocate_handle(X86ODBCHandleType::Connection).unwrap();
odbc.connect(conn, "db.example.com", "admin", "secret123")
.unwrap();
assert!(odbc
.connections
.get(&conn)
.unwrap()
.connection_string
.contains("PWD=secret123"));
}
#[test]
fn test_odbc_data_enum_values() {
let int_val = X86ODBCData::Integer(42);
let float_val = X86ODBCData::Double(3.14159);
let str_val = X86ODBCData::Varchar("hello".to_string());
let null_val = X86ODBCData::Null;
let bin_val = X86ODBCData::Binary(vec![0x01, 0x02, 0x03]);
match int_val {
X86ODBCData::Integer(i) => assert_eq!(i, 42),
_ => panic!("Expected Integer"),
}
match float_val {
X86ODBCData::Double(d) => assert!((d - 3.14159).abs() < 0.0001),
_ => panic!("Expected Double"),
}
match str_val {
X86ODBCData::Varchar(s) => assert_eq!(s, "hello"),
_ => panic!("Expected Varchar"),
}
match null_val {
X86ODBCData::Null => {}
_ => panic!("Expected Null"),
}
match bin_val {
X86ODBCData::Binary(b) => assert_eq!(b, vec![0x01, 0x02, 0x03]),
_ => panic!("Expected Binary"),
}
}
#[test]
fn test_odbc_fetch_empty_result_set() {
let mut odbc = X86ODBCSupport::new();
let stmt = odbc.allocate_handle(X86ODBCHandleType::Statement).unwrap();
odbc.exec_direct(stmt, "SELECT * FROM empty_table").unwrap();
let row = odbc.fetch(stmt).unwrap();
assert!(row.is_none());
}
#[test]
fn test_odbc_prepare_execute_flow() {
let mut odbc = X86ODBCSupport::new();
let stmt = odbc.allocate_handle(X86ODBCHandleType::Statement).unwrap();
let result = odbc.execute(stmt);
assert!(result.is_err());
odbc.prepare(stmt, "SELECT ?").unwrap();
assert!(odbc.statements.get(&stmt).unwrap().prepared);
odbc.execute(stmt).unwrap();
assert!(odbc.statements.get(&stmt).unwrap().executed);
}
#[test]
fn test_odbc_get_data_no_row() {
let mut odbc = X86ODBCSupport::new();
let stmt = odbc.allocate_handle(X86ODBCHandleType::Statement).unwrap();
odbc.exec_direct(stmt, "SELECT 1").unwrap();
let result = odbc.get_data(stmt, 1);
assert!(result.is_err());
}
#[test]
fn test_odbc_param_binding_structure() {
let binding = X86ODBCParamBinding {
param_index: 1,
value_type: odbc_sql_types::SQL_INTEGER,
param_type: 1,
column_size: 4,
decimal_digits: 0,
value: Some(42i32.to_le_bytes().to_vec()),
null_indicator: 0,
};
assert_eq!(binding.param_index, 1);
assert_eq!(binding.value_type, odbc_sql_types::SQL_INTEGER);
assert!(binding.value.is_some());
}
#[test]
fn test_odbc_driver_manager_structure() {
let dm = X86ODBCDriverManager {
drivers: vec![X86ODBCDriver {
name: "PostgreSQL".to_string(),
attributes: [("Driver".to_string(), "/usr/lib/psqlodbcw.so".to_string())]
.into_iter()
.collect(),
file_usage: 0,
driver_lib: Some("/usr/lib/psqlodbcw.so".to_string()),
setup_lib: None,
}],
data_sources: vec![],
tracing_enabled: false,
trace_file: None,
};
assert_eq!(dm.drivers.len(), 1);
assert_eq!(dm.drivers[0].name, "PostgreSQL");
}
#[test]
fn test_odbc_data_source_structure() {
let ds = X86ODBCDataSource {
name: "MyDB".to_string(),
driver: "PostgreSQL".to_string(),
attributes: [
("Server".to_string(), "localhost".to_string()),
("Port".to_string(), "5432".to_string()),
]
.into_iter()
.collect(),
system_dsn: true,
};
assert_eq!(ds.name, "MyDB");
assert_eq!(ds.driver, "PostgreSQL");
assert!(ds.system_dsn);
}
#[test]
fn test_odbc_type_size_lookup() {
let odbc = X86ODBCSupport::new();
assert_eq!(odbc.odbc_type_size(odbc_sql_types::SQL_INTEGER), 4);
assert_eq!(odbc.odbc_type_size(odbc_sql_types::SQL_BIGINT), 8);
assert_eq!(odbc.odbc_type_size(odbc_sql_types::SQL_SMALLINT), 2);
assert_eq!(odbc.odbc_type_size(odbc_sql_types::SQL_TINYINT), 1);
}
#[test]
fn test_odbc_column_desc_structure() {
let desc = X86ODBCColumnDesc {
name: "employee_id".to_string(),
data_type: odbc_sql_types::SQL_INTEGER,
column_size: 10,
decimal_digits: 0,
nullable: 1,
octet_length: 4,
display_size: 11,
};
assert_eq!(desc.name, "employee_id");
assert_eq!(desc.data_type, odbc_sql_types::SQL_INTEGER);
assert_eq!(desc.nullable, 1);
}
#[test]
fn test_odbc_all_sql_type_constants_unique() {
let types = [
odbc_sql_types::SQL_CHAR,
odbc_sql_types::SQL_INTEGER,
odbc_sql_types::SQL_SMALLINT,
odbc_sql_types::SQL_REAL,
odbc_sql_types::SQL_DOUBLE,
odbc_sql_types::SQL_VARCHAR,
odbc_sql_types::SQL_BIGINT,
odbc_sql_types::SQL_TINYINT,
odbc_sql_types::SQL_BINARY,
odbc_sql_types::SQL_VARBINARY,
odbc_sql_types::SQL_TYPE_DATE,
odbc_sql_types::SQL_TYPE_TIME,
odbc_sql_types::SQL_TYPE_TIMESTAMP,
];
let mut seen = HashSet::new();
for t in &types {
assert!(seen.insert(*t), "Duplicate type: {}", t);
}
}
#[test]
fn test_odbc_describe_col_out_of_range() {
let mut odbc = X86ODBCSupport::new();
let stmt = odbc.allocate_handle(X86ODBCHandleType::Statement).unwrap();
let result = odbc.describe_col(stmt, 100);
assert!(result.is_err());
}
#[test]
fn test_odbc_disconnect_idempotent() {
let mut odbc = X86ODBCSupport::new();
let conn = odbc.allocate_handle(X86ODBCHandleType::Connection).unwrap();
odbc.connect(conn, "host", "user", "pass").unwrap();
odbc.disconnect(conn).unwrap();
assert!(!odbc.connections.get(&conn).unwrap().connected);
odbc.disconnect(conn).unwrap();
}
#[test]
fn test_full_database_sql_to_sqlite_flow() {
let mut db = X86Database::new();
let sql =
"CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE)";
let stmt = db.sql_support.compile(sql).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::CreateTable);
assert_eq!(stmt.columns.len(), 3);
assert!(stmt.columns[0].primary_key);
assert!(!stmt.columns[1].nullable);
assert!(stmt.columns[2].unique);
let conn_id = db.sqlite_support.open(":memory:").unwrap();
assert!(db.sqlite_support.connections.contains_key(&conn_id));
db.sqlite_support.close(conn_id).unwrap();
}
#[test]
fn test_full_database_insert_select_flow() {
let mut db = X86Database::new();
let sql = "INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')";
let stmt = db.sql_support.compile(sql).unwrap();
assert_eq!(stmt.statement_type, X86SQLStatementType::Insert);
assert_eq!(stmt.table_name, Some("users".to_string()));
}
#[test]
fn test_database_constants() {
assert_eq!(X86_SQL_MAX_STATEMENT_LENGTH, 1_048_576);
assert_eq!(X86_SQL_MAX_BIND_PARAMS, 999);
assert_eq!(X86_SQL_MAX_COLUMNS, 4096);
assert_eq!(X86_SQL_MAX_IDENTIFIER_LEN, 128);
assert_eq!(X86_SQLITE_DEFAULT_PAGE_SIZE, 4096);
assert_eq!(X86_SQLITE_MAX_PAGE_SIZE, 65536);
assert_eq!(X86_SQLITE_MIN_PAGE_SIZE, 512);
assert_eq!(X86_PG_DEFAULT_PORT, 5432);
assert_eq!(X86_MYSQL_DEFAULT_PORT, 3306);
assert_eq!(X86_REDIS_DEFAULT_PORT, 6379);
assert_eq!(X86_ODBC_MAX_CONN_STR, 1024);
assert_eq!(X86_ODBC_FETCH_BATCH, 128);
assert_eq!(X86_SQLITE_WAL_CHECKPOINT_THRESHOLD, 1000);
assert_eq!(X86_SQLITE_WAL_AUTOCHECKPOINT, 1000);
}
#[test]
fn test_sql_dialect_display() {
assert_eq!(X86SQLDialect::Standard.to_string(), "SQL-Standard");
assert_eq!(X86SQLDialect::SQLite.to_string(), "SQLite");
assert_eq!(X86SQLDialect::PostgreSQL.to_string(), "PostgreSQL");
assert_eq!(X86SQLDialect::MySQL.to_string(), "MySQL");
assert_eq!(X86SQLDialect::MariaDB.to_string(), "MariaDB");
assert_eq!(X86SQLDialect::MSSQL.to_string(), "MSSQL");
assert_eq!(X86SQLDialect::Oracle.to_string(), "Oracle");
}
#[test]
fn test_sqlite_affinity_display() {
assert_eq!(X86SQLiteAffinity::Integer.to_string(), "INTEGER");
assert_eq!(X86SQLiteAffinity::Real.to_string(), "REAL");
assert_eq!(X86SQLiteAffinity::Text.to_string(), "TEXT");
assert_eq!(X86SQLiteAffinity::Blob.to_string(), "BLOB");
assert_eq!(X86SQLiteAffinity::Numeric.to_string(), "NUMERIC");
assert_eq!(X86SQLiteAffinity::None.to_string(), "NONE");
}
#[test]
fn test_sql_keyword_from_type_mapping() {
let kw_to_type = vec![
(X86SQLKeyword::Integer, X86SQLDataType::Integer),
(X86SQLKeyword::BigInt, X86SQLDataType::BigInt),
(X86SQLKeyword::SmallInt, X86SQLDataType::SmallInt),
(X86SQLKeyword::Real, X86SQLDataType::Real),
(X86SQLKeyword::Double, X86SQLDataType::Double),
(X86SQLKeyword::Float, X86SQLDataType::Real),
(X86SQLKeyword::Decimal, X86SQLDataType::Decimal),
(X86SQLKeyword::Numeric, X86SQLDataType::Numeric),
(X86SQLKeyword::Boolean, X86SQLDataType::Boolean),
(X86SQLKeyword::Char, X86SQLDataType::Char),
(X86SQLKeyword::Varchar, X86SQLDataType::Varchar),
(X86SQLKeyword::Text, X86SQLDataType::Text),
(X86SQLKeyword::Blob, X86SQLDataType::Blob),
(X86SQLKeyword::Date, X86SQLDataType::Date),
(X86SQLKeyword::Time, X86SQLDataType::Time),
(X86SQLKeyword::Timestamp, X86SQLDataType::Timestamp),
(X86SQLKeyword::Json, X86SQLDataType::Json),
(X86SQLKeyword::Jsonb, X86SQLDataType::Jsonb),
(X86SQLKeyword::Uuid, X86SQLDataType::Uuid),
(X86SQLKeyword::Bytea, X86SQLDataType::Bytea),
];
for (kw, expected_type) in kw_to_type {
assert_eq!(
X86SQLDataType::from_keyword(kw),
expected_type,
"Failed for keyword {:?}",
kw
);
}
}
#[test]
fn test_sql_cache_hit() {
let mut sql = X86SQLSupport::new();
let _ = sql.compile("SELECT 1");
assert_eq!(sql.stats.cache_misses, 1);
assert_eq!(sql.stats.cache_hits, 0);
let _ = sql.compile("SELECT 1");
assert_eq!(sql.stats.cache_hits, 1);
}
#[test]
fn test_x86_db_pointer_alignment() {
assert_eq!(X86_DB_POINTER_SIZE, 8);
assert_eq!(X86_DB_CACHELINE_SIZE, 64);
assert_eq!(X86_DB_CACHELINE_SIZE % X86_DB_POINTER_SIZE, 0);
}
}