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