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