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