Skip to content

Commit 0c83e5d

Browse files
miuy56dcmashuai
andauthored
Support SQLite's WITHOUT ROWID in CREATE TABLE (apache#208)
Per https://sqlite.org/lang_createtable.html Co-authored-by: mashuai <mashuai@bytedance.com>
1 parent 0c82be5 commit 0c83e5d

7 files changed

Lines changed: 57 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Check https://github.com/andygrove/sqlparser-rs/commits/master for undocumented
1111
### Changed
1212

1313
### Added
14+
- Support SQLite's `CREATE TABLE (...) WITHOUT ROWID` (#208) - thanks @mashuai!
1415

1516
### Fixed
1617

src/ast/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ pub enum Statement {
482482
file_format: Option<FileFormat>,
483483
location: Option<String>,
484484
query: Option<Box<Query>>,
485+
without_rowid: bool,
485486
},
486487
/// CREATE INDEX
487488
CreateIndex {
@@ -647,6 +648,7 @@ impl fmt::Display for Statement {
647648
file_format,
648649
location,
649650
query,
651+
without_rowid,
650652
} => {
651653
// We want to allow the following options
652654
// Empty column list, allowed by PostgreSQL:
@@ -672,6 +674,10 @@ impl fmt::Display for Statement {
672674
// PostgreSQL allows `CREATE TABLE t ();`, but requires empty parens
673675
write!(f, " ()")?;
674676
}
677+
// Only for SQLite
678+
if *without_rowid {
679+
write!(f, " WITHOUT ROWID")?;
680+
}
675681

676682
if *external {
677683
write!(

src/dialect/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ define_keywords!(
351351
ROLLBACK,
352352
ROLLUP,
353353
ROW,
354+
ROWID,
354355
ROWS,
355356
ROW_NUMBER,
356357
SAVEPOINT,

src/parser.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,7 @@ impl Parser {
10211021
file_format: Some(file_format),
10221022
location: Some(location),
10231023
query: None,
1024+
without_rowid: false,
10241025
})
10251026
}
10261027

@@ -1110,6 +1111,9 @@ impl Parser {
11101111
// parse optional column list (schema)
11111112
let (columns, constraints) = self.parse_columns()?;
11121113

1114+
// SQLite supports `WITHOUT ROWID` at the end of `CREATE TABLE`
1115+
let without_rowid = self.parse_keywords(&[Keyword::WITHOUT, Keyword::ROWID]);
1116+
11131117
// PostgreSQL supports `WITH ( options )`, before `AS`
11141118
let with_options = self.parse_with_options()?;
11151119

@@ -1130,6 +1134,7 @@ impl Parser {
11301134
file_format: None,
11311135
location: None,
11321136
query,
1137+
without_rowid,
11331138
})
11341139
}
11351140

tests/sqlparser_common.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,7 +1044,7 @@ fn parse_create_table() {
10441044
external: false,
10451045
file_format: None,
10461046
location: None,
1047-
query: _query,
1047+
..
10481048
} => {
10491049
assert_eq!("uk_cities", name.to_string());
10501050
assert_eq!(
@@ -1276,7 +1276,7 @@ fn parse_create_external_table() {
12761276
external,
12771277
file_format,
12781278
location,
1279-
query: _query,
1279+
..
12801280
} => {
12811281
assert_eq!("uk_cities", name.to_string());
12821282
assert_eq!(

tests/sqlparser_postgres.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ fn parse_create_table_with_defaults() {
4343
external: false,
4444
file_format: None,
4545
location: None,
46-
query: _query,
46+
..
4747
} => {
4848
assert_eq!("public.customer", name.to_string());
4949
assert_eq!(

tests/sqlparser_sqlite.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License");
2+
// you may not use this file except in compliance with the License.
3+
// You may obtain a copy of the License at
4+
//
5+
// http://www.apache.org/licenses/LICENSE-2.0
6+
//
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
13+
#![warn(clippy::all)]
14+
//! Test SQL syntax specific to SQLite. The parser based on the
15+
//! generic dialect is also tested (on the inputs it can handle).
16+
17+
use sqlparser::ast::*;
18+
use sqlparser::dialect::GenericDialect;
19+
use sqlparser::test_utils::*;
20+
21+
#[test]
22+
fn parse_create_table_without_rowid() {
23+
let sql = "CREATE TABLE t (a INT) WITHOUT ROWID";
24+
match sqlite_and_generic().verified_stmt(sql) {
25+
Statement::CreateTable {
26+
name,
27+
without_rowid: true,
28+
..
29+
} => {
30+
assert_eq!("t", name.to_string());
31+
}
32+
_ => unreachable!(),
33+
}
34+
}
35+
36+
fn sqlite_and_generic() -> TestedDialects {
37+
TestedDialects {
38+
// we don't have a separate SQLite dialect, so test only the generic dialect for now
39+
dialects: vec![Box::new(GenericDialect {})],
40+
}
41+
}

0 commit comments

Comments
 (0)