Support SQLite's WITHOUT ROWID in CREATE TABLE (#208)

Per https://sqlite.org/lang_createtable.html

Co-authored-by: mashuai <mashuai@bytedance.com>
This commit is contained in:
mz 2020-06-26 20:11:46 +08:00 committed by GitHub
parent 0c82be5c3b
commit 0c83e5d9e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 57 additions and 3 deletions

View file

@ -11,6 +11,7 @@ Check https://github.com/andygrove/sqlparser-rs/commits/master for undocumented
### Changed ### Changed
### Added ### Added
- Support SQLite's `CREATE TABLE (...) WITHOUT ROWID` (#208) - thanks @mashuai!
### Fixed ### Fixed

View file

@ -482,6 +482,7 @@ pub enum Statement {
file_format: Option<FileFormat>, file_format: Option<FileFormat>,
location: Option<String>, location: Option<String>,
query: Option<Box<Query>>, query: Option<Box<Query>>,
without_rowid: bool,
}, },
/// CREATE INDEX /// CREATE INDEX
CreateIndex { CreateIndex {
@ -647,6 +648,7 @@ impl fmt::Display for Statement {
file_format, file_format,
location, location,
query, query,
without_rowid,
} => { } => {
// We want to allow the following options // We want to allow the following options
// Empty column list, allowed by PostgreSQL: // Empty column list, allowed by PostgreSQL:
@ -672,6 +674,10 @@ impl fmt::Display for Statement {
// PostgreSQL allows `CREATE TABLE t ();`, but requires empty parens // PostgreSQL allows `CREATE TABLE t ();`, but requires empty parens
write!(f, " ()")?; write!(f, " ()")?;
} }
// Only for SQLite
if *without_rowid {
write!(f, " WITHOUT ROWID")?;
}
if *external { if *external {
write!( write!(

View file

@ -351,6 +351,7 @@ define_keywords!(
ROLLBACK, ROLLBACK,
ROLLUP, ROLLUP,
ROW, ROW,
ROWID,
ROWS, ROWS,
ROW_NUMBER, ROW_NUMBER,
SAVEPOINT, SAVEPOINT,

View file

@ -1021,6 +1021,7 @@ impl Parser {
file_format: Some(file_format), file_format: Some(file_format),
location: Some(location), location: Some(location),
query: None, query: None,
without_rowid: false,
}) })
} }
@ -1110,6 +1111,9 @@ impl Parser {
// parse optional column list (schema) // parse optional column list (schema)
let (columns, constraints) = self.parse_columns()?; let (columns, constraints) = self.parse_columns()?;
// SQLite supports `WITHOUT ROWID` at the end of `CREATE TABLE`
let without_rowid = self.parse_keywords(&[Keyword::WITHOUT, Keyword::ROWID]);
// PostgreSQL supports `WITH ( options )`, before `AS` // PostgreSQL supports `WITH ( options )`, before `AS`
let with_options = self.parse_with_options()?; let with_options = self.parse_with_options()?;
@ -1130,6 +1134,7 @@ impl Parser {
file_format: None, file_format: None,
location: None, location: None,
query, query,
without_rowid,
}) })
} }

View file

@ -1044,7 +1044,7 @@ fn parse_create_table() {
external: false, external: false,
file_format: None, file_format: None,
location: None, location: None,
query: _query, ..
} => { } => {
assert_eq!("uk_cities", name.to_string()); assert_eq!("uk_cities", name.to_string());
assert_eq!( assert_eq!(
@ -1276,7 +1276,7 @@ fn parse_create_external_table() {
external, external,
file_format, file_format,
location, location,
query: _query, ..
} => { } => {
assert_eq!("uk_cities", name.to_string()); assert_eq!("uk_cities", name.to_string());
assert_eq!( assert_eq!(

View file

@ -43,7 +43,7 @@ fn parse_create_table_with_defaults() {
external: false, external: false,
file_format: None, file_format: None,
location: None, location: None,
query: _query, ..
} => { } => {
assert_eq!("public.customer", name.to_string()); assert_eq!("public.customer", name.to_string());
assert_eq!( assert_eq!(

41
tests/sqlparser_sqlite.rs Normal file
View file

@ -0,0 +1,41 @@
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![warn(clippy::all)]
//! Test SQL syntax specific to SQLite. The parser based on the
//! generic dialect is also tested (on the inputs it can handle).
use sqlparser::ast::*;
use sqlparser::dialect::GenericDialect;
use sqlparser::test_utils::*;
#[test]
fn parse_create_table_without_rowid() {
let sql = "CREATE TABLE t (a INT) WITHOUT ROWID";
match sqlite_and_generic().verified_stmt(sql) {
Statement::CreateTable {
name,
without_rowid: true,
..
} => {
assert_eq!("t", name.to_string());
}
_ => unreachable!(),
}
}
fn sqlite_and_generic() -> TestedDialects {
TestedDialects {
// we don't have a separate SQLite dialect, so test only the generic dialect for now
dialects: vec![Box::new(GenericDialect {})],
}
}