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, ListHackExpression,
162 MatchAgainstExpression, MatchMode, MemberOfExpression, NullExpression, Quantifier,
163 QuantifierExpression, RowExpression, SubqueryExpression, TimeUnit, TimestampAddExpression,
164 TimestampDiffExpression, TrimDirection, TrimExpression, TypeCastExpression, UnaryExpression,
165 UnaryOperator, UserVariableExpression, Variable, VariableExpression, When,
166};
167pub use flush::{Flush, FlushOption};
168pub use function_expression::{
169 AggregateFunctionCallExpression, CharFunctionExpression, Function, FunctionCallExpression,
170 WindowClause, WindowFrame, WindowFrameBound, WindowFrameMode, WindowFunctionCallExpression,
171 WindowSpec,
172};
173pub use grant::{
174 AllRoutineKind, Grant, GrantKind, GrantObject, GrantPrivilege, MembershipOption,
175 MembershipOptionKind, MembershipOptionValue, PrivilegeItem, RoleSpec, RoutineArgType,
176 RoutineKind, RoutineName,
177};
178pub use identifier::Identifier;
179pub use insert_replace::{
180 InsertReplace, InsertReplaceFlag, InsertReplaceOnDuplicateKeyUpdate, InsertReplaceSet,
181 InsertReplaceSetPair, InsertReplaceType, OnConflict, OnConflictAction, OnConflictTarget,
182};
183pub use issue::{Fragment, Issue, IssueHandle, Issues, Level};
184pub use kill::{Kill, KillType};
185pub use lock::{Lock, LockMember, LockType, Unlock};
186pub use operator::{
187 AlterOperator, AlterOperatorAction, AlterOperatorClass, AlterOperatorClassAction,
188 AlterOperatorFamily, AlterOperatorFamilyAction, CreateOperator, CreateOperatorClass,
189 CreateOperatorFamily, LeftOperatorType, OperatorClassItem, OperatorClassOperatorOption,
190 OperatorFamilyDropItem, OperatorFamilyItem, OperatorOption, OperatorRef,
191};
192pub use qualified_name::QualifiedName;
193pub use rename::{RenameTable, TableToTable};
194pub use select::{
195 IndexHint, IndexHintFor, IndexHintType, IndexHintUse, JoinSpecification, JoinType,
196 JsonTableColumn, JsonTableOnErrorEmpty, LockStrength, LockWait, Locking, OrderFlag, Select,
197 SelectExpr, SelectFlag, TableFunctionName, TableReference,
198};
199pub use show::{
200 ShowCharacterSet, ShowCollation, ShowColumns, ShowCreateDatabase, ShowCreateTable,
201 ShowCreateView, ShowDatabases, ShowEngines, ShowProcessList, ShowStatus, ShowTables,
202 ShowVariables,
203};
204pub use span::{OptSpanned, Span, Spanned};
205pub use sstring::SString;
206pub use statement::{
207 AlterSchema, AlterSchemaAction, Analyze, Assign, Begin, Block, Call, CaseStatement,
208 CloseCursor, CommentOn, CommentOnObjectType, Commit, CompoundOperator, CompoundQuantifier,
209 CompoundQuery, CompoundQueryBranch, CursorHold, CursorScroll, CursorSensitivity, DeclareCursor,
210 DeclareCursorMariaDb, DeclareHandler, DeclareVariable, Do, DoBody, End, ExceptionHandler,
211 Explain, ExplainFormat, ExplainOption, FetchCursor, HandlerAction, HandlerCondition, If,
212 IfCondition, Invalid, Iterate, Leave, Loop, OpenCursor, Perform, PlpgsqlExecute, Prepare,
213 Raise, RaiseLevel, RaiseOptionName, RefreshMaterializedView, Repeat, Return, Set, SetVariable,
214 Signal, SignalConditionInformationName, StartTransaction, Statement, Stdin, WhenStatement,
215 While,
216};
217pub use truncate::{IdentityOption, TruncateTable, TruncateTableSpec};
218pub use update::{Update, UpdateFlag};
219pub use values::{Fetch, FetchDirection, Values};
220pub use visit::{Visitor, walk_expression, walk_select, walk_statement, walk_table_reference};
221pub use with_query::{MaterializedHint, WithBlock, WithQuery};
222
223#[derive(Clone, Debug)]
225pub enum SQLDialect {
226 MariaDB,
228 PostgreSQL,
229 PostGIS,
231 Sqlite,
232}
233
234impl SQLDialect {
235 pub fn is_postgresql(&self) -> bool {
236 matches!(self, SQLDialect::PostgreSQL | SQLDialect::PostGIS)
237 }
238
239 pub fn is_postgis(&self) -> bool {
240 matches!(self, SQLDialect::PostGIS)
241 }
242
243 pub fn is_maria(&self) -> bool {
244 matches!(self, SQLDialect::MariaDB)
245 }
246
247 pub fn is_sqlite(&self) -> bool {
248 matches!(self, SQLDialect::Sqlite)
249 }
250}
251
252#[derive(Clone, Debug)]
254pub enum SQLArguments {
255 None,
257 Percent,
259 QuestionMark,
261 Dollar,
263}
264
265#[derive(Clone, Debug)]
267pub struct ParseOptions {
268 dialect: SQLDialect,
269 arguments: SQLArguments,
270 warn_unquoted_identifiers: bool,
271 warn_none_capital_keywords: bool,
272 list_hack: bool,
273 function_body: bool,
277 span_offset: usize,
281}
282
283impl Default for ParseOptions {
284 fn default() -> Self {
285 Self {
286 dialect: SQLDialect::MariaDB,
287 arguments: SQLArguments::None,
288 warn_none_capital_keywords: false,
289 warn_unquoted_identifiers: false,
290 list_hack: false,
291 function_body: false,
292 span_offset: 0,
293 }
294 }
295}
296
297impl ParseOptions {
298 pub fn new() -> Self {
299 Default::default()
300 }
301
302 pub fn dialect(self, dialect: SQLDialect) -> Self {
304 Self { dialect, ..self }
305 }
306
307 pub fn get_dialect(&self) -> SQLDialect {
308 self.dialect.clone()
309 }
310
311 pub fn arguments(self, arguments: SQLArguments) -> Self {
313 Self { arguments, ..self }
314 }
315
316 pub fn warn_unquoted_identifiers(self, warn_unquoted_identifiers: bool) -> Self {
318 Self {
319 warn_unquoted_identifiers,
320 ..self
321 }
322 }
323
324 pub fn warn_none_capital_keywords(self, warn_none_capital_keywords: bool) -> Self {
326 Self {
327 warn_none_capital_keywords,
328 ..self
329 }
330 }
331
332 pub fn list_hack(self, list_hack: bool) -> Self {
334 Self { list_hack, ..self }
335 }
336
337 pub fn function_body(self, function_body: bool) -> Self {
339 Self {
340 function_body,
341 ..self
342 }
343 }
344
345 pub fn get_function_body(&self) -> bool {
346 self.function_body
347 }
348
349 pub fn span_offset(self, span_offset: usize) -> Self {
353 Self {
354 span_offset,
355 ..self
356 }
357 }
358
359 pub fn get_span_offset(&self) -> usize {
360 self.span_offset
361 }
362}
363
364#[macro_export]
366macro_rules! issue_ice {
367 ( $issues: expr, $spanned:expr ) => {{
368 $issues.err(
369 alloc::format!("Internal compiler error in {}:{}", file!(), line!()),
370 $spanned,
371 );
372 }};
373}
374
375#[macro_export]
377macro_rules! issue_todo {
378 ( $issues: expr, $spanned:expr ) => {{
379 $issues.err(
380 alloc::format!("Not yet implemented {}:{}", file!(), line!()),
381 $spanned,
382 );
383 }};
384}
385
386pub fn parse_statements<'a>(
391 src: &'a str,
392 issues: &mut Issues<'a>,
393 options: &ParseOptions,
394) -> Vec<Statement<'a>> {
395 let mut parser = Parser::new(src, issues, options);
396 statement::parse_statements(&mut parser)
397}
398
399pub fn parse_statement<'a>(
404 src: &'a str,
405 issues: &mut Issues<'a>,
406 options: &ParseOptions,
407) -> Option<Statement<'a>> {
408 let mut parser = Parser::new(src, issues, options);
409 match statement::parse_statement(&mut parser) {
410 Ok(Some(v)) => {
411 if parser.token == Token::Delimiter {
413 parser.consume();
414 }
415 if parser.token != Token::Eof {
416 parser.expected_error("Unexpected token after statement")
417 }
418 Some(v)
419 }
420 Ok(None) => {
421 parser.expected_error("Statement");
422 None
423 }
424 Err(_) => None,
425 }
426}
427
428#[test]
429pub fn test_parse_alter_sql() {
430 let sql = "ALTER TABLE `test` ADD COLUMN `test1` VARCHAR (128) NULL DEFAULT NULL";
431 let options = ParseOptions::new()
432 .dialect(SQLDialect::MariaDB)
433 .arguments(SQLArguments::QuestionMark)
434 .warn_unquoted_identifiers(false);
435
436 let mut issues = Issues::new(sql);
437 parse_statement(sql, &mut issues, &options);
438 assert!(issues.is_ok(), "{}", issues);
439}
440
441#[test]
442pub fn test_parse_delete_sql_with_schema() {
443 let sql = "DROP TABLE IF EXISTS `test_schema`.`test`";
444 let options = ParseOptions::new()
445 .dialect(SQLDialect::MariaDB)
446 .arguments(SQLArguments::QuestionMark)
447 .warn_unquoted_identifiers(false);
448
449 let mut issues = Issues::new(sql);
450 parse_statement(sql, &mut issues, &options);
451 assert!(issues.is_ok(), "{}", issues);
452}
453
454#[test]
455pub fn parse_create_index_sql_with_schema() {
456 let sql = "CREATE INDEX `idx_test` ON test_schema.test(`col_test`)";
457 let options = ParseOptions::new()
458 .dialect(SQLDialect::MariaDB)
459 .arguments(SQLArguments::QuestionMark)
460 .warn_unquoted_identifiers(false);
461
462 let mut issues = Issues::new(sql);
463 parse_statement(sql, &mut issues, &options);
464 assert!(issues.is_ok(), "{}", issues);
465}
466
467#[test]
468pub fn parse_create_index_sql_with_opclass() {
469 let sql = "CREATE INDEX idx_test ON test(path text_pattern_ops)";
470 let options = ParseOptions::new()
471 .dialect(SQLDialect::PostgreSQL)
472 .arguments(SQLArguments::Dollar)
473 .warn_unquoted_identifiers(false);
474
475 let mut issues = Issues::new(sql);
476 parse_statement(sql, &mut issues, &options);
477 assert!(issues.is_ok(), "{}", issues);
478}
479
480#[test]
481pub fn parse_drop_index_sql_with_schema() {
482 let sql = "DROP INDEX `idx_test` ON test_schema.test";
483 let options = ParseOptions::new()
484 .dialect(SQLDialect::MariaDB)
485 .arguments(SQLArguments::QuestionMark)
486 .warn_unquoted_identifiers(false);
487
488 let mut issues = Issues::new(sql);
489 let _result = parse_statement(sql, &mut issues, &options);
490 assert!(issues.is_ok(), "{}", issues);
492}
493
494#[test]
495pub fn parse_create_view_sql_with_schema() {
496 let sql =
497 "CREATE OR REPLACE VIEW `test_schema`.`view_test` AS SELECT * FROM `test_schema`.`test`";
498 let options = ParseOptions::new()
499 .dialect(SQLDialect::MariaDB)
500 .arguments(SQLArguments::QuestionMark)
501 .warn_unquoted_identifiers(false);
502
503 let mut issues = Issues::new(sql);
504 let _result = parse_statement(sql, &mut issues, &options);
505 assert!(issues.is_ok(), "{}", issues);
507}
508
509#[test]
510pub fn parse_drop_view_sql_with_schema() {
511 let sql = "DROP VIEW `test_schema`.`view_test`";
512 let options = ParseOptions::new()
513 .dialect(SQLDialect::MariaDB)
514 .arguments(SQLArguments::QuestionMark)
515 .warn_unquoted_identifiers(false);
516
517 let mut issues = Issues::new(sql);
518 let _result = parse_statement(sql, &mut issues, &options);
519 assert!(issues.is_ok(), "{}", issues);
521}
522
523#[test]
524pub fn parse_truncate_table_sql_with_schema() {
525 let sql = "TRUNCATE TABLE `test_schema`.`table_test`";
526 let options = ParseOptions::new()
527 .dialect(SQLDialect::MariaDB)
528 .arguments(SQLArguments::QuestionMark)
529 .warn_unquoted_identifiers(false);
530
531 let mut issues = Issues::new(sql);
532 let _result = parse_statement(sql, &mut issues, &options);
533 assert!(issues.is_ok(), "{}", issues);
535}
536
537#[test]
538pub fn parse_rename_table_sql_with_schema() {
539 let sql = "RENAME TABLE `test_schema`.`table_test` To `test_schema`.`table_new_test`";
540 let options = ParseOptions::new()
541 .dialect(SQLDialect::MariaDB)
542 .arguments(SQLArguments::QuestionMark)
543 .warn_unquoted_identifiers(false);
544
545 let mut issues = Issues::new(sql);
546 let _result = parse_statement(sql, &mut issues, &options);
547 assert!(issues.is_ok(), "{}", issues);
549}
550
551#[test]
552pub fn parse_with_statement() {
553 let sql = "
554 WITH monkeys AS (DELETE FROM thing RETURNING id),
555 baz AS (SELECT id FROM cats WHERE comp IN (monkeys))
556 DELETE FROM dogs WHERE cat IN (cats)";
557 let options = ParseOptions::new()
558 .dialect(SQLDialect::PostgreSQL)
559 .arguments(SQLArguments::QuestionMark)
560 .warn_unquoted_identifiers(false);
561
562 let mut issues = Issues::new(sql);
563 let _result = parse_statement(sql, &mut issues, &options);
564 assert!(issues.is_ok(), "{}", issues);
566}
567
568#[test]
569pub fn parse_use_index() {
570 let sql = "SELECT `a` FROM `b` FORCE INDEX FOR GROUP BY (`b`, `c`)";
571 let options = ParseOptions::new()
572 .dialect(SQLDialect::MariaDB)
573 .arguments(SQLArguments::QuestionMark)
574 .warn_unquoted_identifiers(false);
575
576 let mut issues = Issues::new(sql);
577 let _result = parse_statement(sql, &mut issues, &options);
578 assert!(issues.is_ok(), "{}", issues);
579}