1#![no_std]
58extern crate alloc;
59
60use alloc::vec::Vec;
61use lexer::Token;
62use parser::Parser;
63mod alter_role;
64mod alter_table;
65mod alter_type;
66mod byte_to_char;
67mod copy;
68mod create;
69mod create_constraint_trigger;
70mod create_function;
71mod create_index;
72mod create_option;
73mod create_role;
74mod create_table;
75mod create_trigger;
76mod create_view;
77mod data_type;
78mod delete;
79mod drop;
80mod expression;
81mod flush;
82mod function_expression;
83mod grant;
84mod identifier;
85mod insert_replace;
86mod issue;
87mod keywords;
88mod kill;
89mod lexer;
90mod lock;
91mod operator;
92mod parser;
93mod qualified_name;
94mod rename;
95mod select;
96mod show;
97mod span;
98mod sstring;
99mod statement;
100mod truncate;
101mod update;
102mod values;
103mod visit;
104mod with_query;
105
106pub use alter_role::{AlterRole, AlterRoleAction, AlterRoleValue};
107pub use alter_table::{
108 AddColumn, AddForeignKey, AddIndex, AddTableConstraint, Algorithm, AlterAlgorithm, AlterColumn,
109 AlterColumnAction, AlterLock, AlterSpecification, AlterTable, AlterTableOwner, AutoIncrement,
110 Change, DisableRowLevelSecurity, DisableRule, DisableTrigger, DropColumn, DropForeignKey,
111 DropPrimaryKey, EnableRowLevelSecurity, EnableRule, EnableTrigger, ForceRowLevelSecurity,
112 ForeignKeyMatch, ForeignKeyOn, ForeignKeyOnAction, ForeignKeyOnType, IndexCol, IndexColExpr,
113 IndexOption, IndexType, ModifyColumn, NoForceRowLevelSecurity, OwnerTo, RenameColumn,
114 RenameConstraint, RenameIndex, RenameTo, ReplicaIdentity, ReplicaIdentityOption,
115 TableConstraintType, TriggerName, ValidateConstraint,
116};
117pub use alter_type::{AlterType, AlterTypeAction, AttributeAction};
118pub use byte_to_char::ByteToChar;
119pub use copy::{
120 CopyColumnList, CopyFrom, CopyHeaderValue, CopyLocation, CopyOption, CopySource, CopyTo,
121};
122pub use create::{
123 CreateDatabase, CreateDatabaseOption, CreateDomain, CreateExtension, CreateSchema,
124 CreateSequence, CreateServer, CreateTypeEnum, DomainConstraint, SequenceOption,
125};
126pub use create_constraint_trigger::{AfterEvent, CreateConstraintTrigger, Deferrable, Initially};
127pub use create_function::{
128 CreateFunction, CreateProcedure, FunctionBody, FunctionCharacteristic, FunctionLanguage,
129 FunctionParallel, FunctionParam, FunctionParamDirection,
130};
131pub use create_index::{
132 CreateIndex, CreateIndexOption, IncludeClause, UsingIndexMethod, WithOption,
133};
134pub use create_option::{CreateAlgorithm, CreateOption};
135pub use create_role::{CreateRole, RoleMembership, RoleMembershipType, RoleOption};
136pub use create_table::{
137 CreateDefinition, CreateTable, CreateTableAs, CreateTablePartitionOf, OnCommitAction,
138 PartitionBoundExpr, PartitionBoundSpec, PartitionBy, PartitionMethod, PartitionOfBound,
139 TableOption,
140};
141pub use create_trigger::{
142 CreateTrigger, ExecuteFunction, TriggerEvent, TriggerForEach, TriggerReference,
143 TriggerReferenceDirection, TriggerTime,
144};
145pub use create_view::CreateView;
146pub use data_type::{
147 DataType, DataTypeProperty, Interval, IntervalField, RangeSubtype, Timestamp, Type,
148};
149pub use delete::{Delete, DeleteFlag};
150pub use drop::{
151 CascadeOrRestrict, DropDatabase, DropDomain, DropEvent, DropExtension, DropFunction,
152 DropFunctionArg, DropFunctionArgMode, DropIndex, DropOperator, DropOperatorClass,
153 DropOperatorFamily, DropOperatorItem, DropProcedure, DropSchema, DropSequence, DropServer,
154 DropTable, DropTrigger, DropType, DropView,
155};
156pub use expression::{
157 ArgExpression, ArrayExpression, ArraySubscriptExpression, BetweenExpression, BinaryExpression,
158 BinaryOperator, BoolExpression, CaseExpression, CastExpression, ConvertExpression,
159 DefaultExpression, ExistsExpression, Expression, ExtractExpression, FieldAccessExpression,
160 FloatExpression, GroupConcatExpression, IdentifierExpression, IdentifierPart, InExpression,
161 IntegerExpression, IntervalExpression, InvalidExpression, Is, IsExpression, LikeExpression,
162 LikeOperator, ListHackExpression, MatchAgainstExpression, MatchMode, MemberOfExpression,
163 NullExpression, Quantifier, QuantifierExpression, RowExpression, SubqueryExpression, TimeUnit,
164 TimestampAddExpression, TimestampDiffExpression, TrimDirection, TrimExpression,
165 TypeCastExpression, UnaryExpression, UnaryOperator, UserVariableExpression, Variable,
166 VariableExpression, When,
167};
168pub use flush::{Flush, FlushOption};
169pub use function_expression::{
170 AggregateFunctionCallExpression, CharFunctionExpression, Function, FunctionCallExpression,
171 WindowClause, WindowFrame, WindowFrameBound, WindowFrameMode, WindowFunctionCallExpression,
172 WindowSpec,
173};
174pub use grant::{
175 AllRoutineKind, Grant, GrantKind, GrantObject, GrantPrivilege, MembershipOption,
176 MembershipOptionKind, MembershipOptionValue, PrivilegeItem, RoleSpec, RoutineArgType,
177 RoutineKind, RoutineName,
178};
179pub use identifier::Identifier;
180pub use insert_replace::{
181 InsertReplace, InsertReplaceFlag, InsertReplaceOnDuplicateKeyUpdate, InsertReplaceSet,
182 InsertReplaceSetPair, InsertReplaceType, OnConflict, OnConflictAction, OnConflictTarget,
183};
184pub use issue::{Fragment, Issue, IssueHandle, Issues, Level};
185pub use kill::{Kill, KillType};
186pub use lock::{Lock, LockMember, LockType, Unlock};
187pub use operator::{
188 AlterOperator, AlterOperatorAction, AlterOperatorClass, AlterOperatorClassAction,
189 AlterOperatorFamily, AlterOperatorFamilyAction, CreateOperator, CreateOperatorClass,
190 CreateOperatorFamily, LeftOperatorType, OperatorClassItem, OperatorClassOperatorOption,
191 OperatorFamilyDropItem, OperatorFamilyItem, OperatorOption, OperatorRef,
192};
193pub use qualified_name::QualifiedName;
194pub use rename::{RenameTable, TableToTable};
195pub use select::{
196 IndexHint, IndexHintFor, IndexHintType, IndexHintUse, JoinSpecification, JoinType,
197 JsonTableColumn, JsonTableOnErrorEmpty, LockStrength, LockWait, Locking, OrderFlag, Select,
198 SelectExpr, SelectFlag, TableFunctionName, TableReference,
199};
200pub use show::{
201 ShowCharacterSet, ShowCollation, ShowColumns, ShowCreateDatabase, ShowCreateTable,
202 ShowCreateView, ShowDatabases, ShowEngines, ShowProcessList, ShowStatus, ShowTables,
203 ShowVariables,
204};
205pub use span::{OptSpanned, Span, Spanned};
206pub use sstring::SString;
207pub use statement::{
208 AlterSchema, AlterSchemaAction, Analyze, Assign, Begin, Block, Call, CaseStatement,
209 CloseCursor, CommentOn, CommentOnObjectType, Commit, CompoundOperator, CompoundQuantifier,
210 CompoundQuery, CompoundQueryBranch, CursorHold, CursorScroll, CursorSensitivity, DeclareCursor,
211 DeclareCursorMariaDb, DeclareHandler, DeclareVariable, Do, DoBody, End, ExceptionHandler,
212 Explain, ExplainFormat, ExplainOption, FetchCursor, HandlerAction, HandlerCondition, If,
213 IfCondition, Invalid, Iterate, Leave, Loop, OpenCursor, Perform, PlpgsqlExecute, Prepare,
214 Raise, RaiseLevel, RaiseOptionName, RefreshMaterializedView, Repeat, Return, Set, SetVariable,
215 Signal, SignalConditionInformationName, StartTransaction, Statement, Stdin, WhenStatement,
216 While,
217};
218pub use truncate::{IdentityOption, TruncateTable, TruncateTableSpec};
219pub use update::{Update, UpdateFlag};
220pub use values::{Fetch, FetchDirection, Values};
221pub use visit::{Visitor, walk_expression, walk_select, walk_statement, walk_table_reference};
222pub use with_query::{MaterializedHint, WithBlock, WithQuery};
223
224#[derive(Clone, Debug)]
226pub enum SQLDialect {
227 MariaDB,
229 PostgreSQL,
230 PostGIS,
232 Sqlite,
233}
234
235impl SQLDialect {
236 pub fn is_postgresql(&self) -> bool {
237 matches!(self, SQLDialect::PostgreSQL | SQLDialect::PostGIS)
238 }
239
240 pub fn is_postgis(&self) -> bool {
241 matches!(self, SQLDialect::PostGIS)
242 }
243
244 pub fn is_maria(&self) -> bool {
245 matches!(self, SQLDialect::MariaDB)
246 }
247
248 pub fn is_sqlite(&self) -> bool {
249 matches!(self, SQLDialect::Sqlite)
250 }
251}
252
253#[derive(Clone, Debug)]
255pub enum SQLArguments {
256 None,
258 Percent,
260 QuestionMark,
262 Dollar,
264}
265
266#[derive(Clone, Debug)]
268pub struct ParseOptions {
269 dialect: SQLDialect,
270 arguments: SQLArguments,
271 warn_unquoted_identifiers: bool,
272 warn_none_capital_keywords: bool,
273 list_hack: bool,
274 function_body: bool,
278 span_offset: usize,
282}
283
284impl Default for ParseOptions {
285 fn default() -> Self {
286 Self {
287 dialect: SQLDialect::MariaDB,
288 arguments: SQLArguments::None,
289 warn_none_capital_keywords: false,
290 warn_unquoted_identifiers: false,
291 list_hack: false,
292 function_body: false,
293 span_offset: 0,
294 }
295 }
296}
297
298impl ParseOptions {
299 pub fn new() -> Self {
300 Default::default()
301 }
302
303 pub fn dialect(self, dialect: SQLDialect) -> Self {
305 Self { dialect, ..self }
306 }
307
308 pub fn get_dialect(&self) -> SQLDialect {
309 self.dialect.clone()
310 }
311
312 pub fn arguments(self, arguments: SQLArguments) -> Self {
314 Self { arguments, ..self }
315 }
316
317 pub fn warn_unquoted_identifiers(self, warn_unquoted_identifiers: bool) -> Self {
319 Self {
320 warn_unquoted_identifiers,
321 ..self
322 }
323 }
324
325 pub fn warn_none_capital_keywords(self, warn_none_capital_keywords: bool) -> Self {
327 Self {
328 warn_none_capital_keywords,
329 ..self
330 }
331 }
332
333 pub fn list_hack(self, list_hack: bool) -> Self {
335 Self { list_hack, ..self }
336 }
337
338 pub fn function_body(self, function_body: bool) -> Self {
340 Self {
341 function_body,
342 ..self
343 }
344 }
345
346 pub fn get_function_body(&self) -> bool {
347 self.function_body
348 }
349
350 pub fn span_offset(self, span_offset: usize) -> Self {
354 Self {
355 span_offset,
356 ..self
357 }
358 }
359
360 pub fn get_span_offset(&self) -> usize {
361 self.span_offset
362 }
363}
364
365#[macro_export]
367macro_rules! issue_ice {
368 ( $issues: expr, $spanned:expr ) => {{
369 $issues.err(
370 alloc::format!("Internal compiler error in {}:{}", file!(), line!()),
371 $spanned,
372 );
373 }};
374}
375
376#[macro_export]
378macro_rules! issue_todo {
379 ( $issues: expr, $spanned:expr ) => {{
380 $issues.err(
381 alloc::format!("Not yet implemented {}:{}", file!(), line!()),
382 $spanned,
383 );
384 }};
385}
386
387pub fn parse_statements<'a>(
392 src: &'a str,
393 issues: &mut Issues<'a>,
394 options: &ParseOptions,
395) -> Vec<Statement<'a>> {
396 let mut parser = Parser::new(src, issues, options);
397 statement::parse_statements(&mut parser)
398}
399
400pub fn parse_statement<'a>(
405 src: &'a str,
406 issues: &mut Issues<'a>,
407 options: &ParseOptions,
408) -> Option<Statement<'a>> {
409 let mut parser = Parser::new(src, issues, options);
410 match statement::parse_statement(&mut parser) {
411 Ok(Some(v)) => {
412 if parser.token == Token::Delimiter {
414 parser.consume();
415 }
416 if parser.token != Token::Eof {
417 parser.expected_error("Unexpected token after statement")
418 }
419 Some(v)
420 }
421 Ok(None) => {
422 parser.expected_error("Statement");
423 None
424 }
425 Err(_) => None,
426 }
427}
428
429#[test]
430pub fn test_parse_alter_sql() {
431 let sql = "ALTER TABLE `test` ADD COLUMN `test1` VARCHAR (128) NULL DEFAULT NULL";
432 let options = ParseOptions::new()
433 .dialect(SQLDialect::MariaDB)
434 .arguments(SQLArguments::QuestionMark)
435 .warn_unquoted_identifiers(false);
436
437 let mut issues = Issues::new(sql);
438 parse_statement(sql, &mut issues, &options);
439 assert!(issues.is_ok(), "{}", issues);
440}
441
442#[test]
443pub fn test_parse_delete_sql_with_schema() {
444 let sql = "DROP TABLE IF EXISTS `test_schema`.`test`";
445 let options = ParseOptions::new()
446 .dialect(SQLDialect::MariaDB)
447 .arguments(SQLArguments::QuestionMark)
448 .warn_unquoted_identifiers(false);
449
450 let mut issues = Issues::new(sql);
451 parse_statement(sql, &mut issues, &options);
452 assert!(issues.is_ok(), "{}", issues);
453}
454
455#[test]
456pub fn parse_create_index_sql_with_schema() {
457 let sql = "CREATE INDEX `idx_test` ON test_schema.test(`col_test`)";
458 let options = ParseOptions::new()
459 .dialect(SQLDialect::MariaDB)
460 .arguments(SQLArguments::QuestionMark)
461 .warn_unquoted_identifiers(false);
462
463 let mut issues = Issues::new(sql);
464 parse_statement(sql, &mut issues, &options);
465 assert!(issues.is_ok(), "{}", issues);
466}
467
468#[test]
469pub fn parse_create_index_sql_with_opclass() {
470 let sql = "CREATE INDEX idx_test ON test(path text_pattern_ops)";
471 let options = ParseOptions::new()
472 .dialect(SQLDialect::PostgreSQL)
473 .arguments(SQLArguments::Dollar)
474 .warn_unquoted_identifiers(false);
475
476 let mut issues = Issues::new(sql);
477 parse_statement(sql, &mut issues, &options);
478 assert!(issues.is_ok(), "{}", issues);
479}
480
481#[test]
482pub fn parse_drop_index_sql_with_schema() {
483 let sql = "DROP INDEX `idx_test` ON test_schema.test";
484 let options = ParseOptions::new()
485 .dialect(SQLDialect::MariaDB)
486 .arguments(SQLArguments::QuestionMark)
487 .warn_unquoted_identifiers(false);
488
489 let mut issues = Issues::new(sql);
490 let _result = parse_statement(sql, &mut issues, &options);
491 assert!(issues.is_ok(), "{}", issues);
493}
494
495#[test]
496pub fn parse_create_view_sql_with_schema() {
497 let sql =
498 "CREATE OR REPLACE VIEW `test_schema`.`view_test` AS SELECT * FROM `test_schema`.`test`";
499 let options = ParseOptions::new()
500 .dialect(SQLDialect::MariaDB)
501 .arguments(SQLArguments::QuestionMark)
502 .warn_unquoted_identifiers(false);
503
504 let mut issues = Issues::new(sql);
505 let _result = parse_statement(sql, &mut issues, &options);
506 assert!(issues.is_ok(), "{}", issues);
508}
509
510#[test]
511pub fn parse_drop_view_sql_with_schema() {
512 let sql = "DROP VIEW `test_schema`.`view_test`";
513 let options = ParseOptions::new()
514 .dialect(SQLDialect::MariaDB)
515 .arguments(SQLArguments::QuestionMark)
516 .warn_unquoted_identifiers(false);
517
518 let mut issues = Issues::new(sql);
519 let _result = parse_statement(sql, &mut issues, &options);
520 assert!(issues.is_ok(), "{}", issues);
522}
523
524#[test]
525pub fn parse_truncate_table_sql_with_schema() {
526 let sql = "TRUNCATE TABLE `test_schema`.`table_test`";
527 let options = ParseOptions::new()
528 .dialect(SQLDialect::MariaDB)
529 .arguments(SQLArguments::QuestionMark)
530 .warn_unquoted_identifiers(false);
531
532 let mut issues = Issues::new(sql);
533 let _result = parse_statement(sql, &mut issues, &options);
534 assert!(issues.is_ok(), "{}", issues);
536}
537
538#[test]
539pub fn parse_rename_table_sql_with_schema() {
540 let sql = "RENAME TABLE `test_schema`.`table_test` To `test_schema`.`table_new_test`";
541 let options = ParseOptions::new()
542 .dialect(SQLDialect::MariaDB)
543 .arguments(SQLArguments::QuestionMark)
544 .warn_unquoted_identifiers(false);
545
546 let mut issues = Issues::new(sql);
547 let _result = parse_statement(sql, &mut issues, &options);
548 assert!(issues.is_ok(), "{}", issues);
550}
551
552#[test]
553pub fn parse_with_statement() {
554 let sql = "
555 WITH monkeys AS (DELETE FROM thing RETURNING id),
556 baz AS (SELECT id FROM cats WHERE comp IN (monkeys))
557 DELETE FROM dogs WHERE cat IN (cats)";
558 let options = ParseOptions::new()
559 .dialect(SQLDialect::PostgreSQL)
560 .arguments(SQLArguments::QuestionMark)
561 .warn_unquoted_identifiers(false);
562
563 let mut issues = Issues::new(sql);
564 let _result = parse_statement(sql, &mut issues, &options);
565 assert!(issues.is_ok(), "{}", issues);
567}
568
569#[test]
570pub fn parse_use_index() {
571 let sql = "SELECT `a` FROM `b` FORCE INDEX FOR GROUP BY (`b`, `c`)";
572 let options = ParseOptions::new()
573 .dialect(SQLDialect::MariaDB)
574 .arguments(SQLArguments::QuestionMark)
575 .warn_unquoted_identifiers(false);
576
577 let mut issues = Issues::new(sql);
578 let _result = parse_statement(sql, &mut issues, &options);
579 assert!(issues.is_ok(), "{}", issues);
580}