mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-10-17 09:17:14 +00:00
Create table builder structure (#659)
* Create table builder structure * Adding comments and examples and adjusting file structure
This commit is contained in:
parent
6392a216f0
commit
a3194ddd52
6 changed files with 360 additions and 53 deletions
1
src/ast/helpers/mod.rs
Normal file
1
src/ast/helpers/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod stmt_create_table;
|
323
src/ast/helpers/stmt_create_table.rs
Normal file
323
src/ast/helpers/stmt_create_table.rs
Normal file
|
@ -0,0 +1,323 @@
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
use alloc::{boxed::Box, format, string::String, vec, vec::Vec};
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::ast::{
|
||||||
|
ColumnDef, FileFormat, HiveDistributionStyle, HiveFormat, ObjectName, OnCommit, Query,
|
||||||
|
SqlOption, Statement, TableConstraint,
|
||||||
|
};
|
||||||
|
use crate::parser::ParserError;
|
||||||
|
|
||||||
|
/// Builder for create table statement variant ([1]).
|
||||||
|
///
|
||||||
|
/// This structure helps building and accessing a create table with more ease, without needing to:
|
||||||
|
/// - Match the enum itself a lot of times; or
|
||||||
|
/// - Moving a lot of variables around the code.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```rust
|
||||||
|
/// use sqlparser::ast::helpers::stmt_create_table::CreateTableBuilder;
|
||||||
|
/// use sqlparser::ast::{ColumnDef, DataType, Ident, ObjectName};
|
||||||
|
/// let builder = CreateTableBuilder::new(ObjectName(vec![Ident::new("table_name")]))
|
||||||
|
/// .if_not_exists(true)
|
||||||
|
/// .columns(vec![ColumnDef {
|
||||||
|
/// name: Ident::new("c1"),
|
||||||
|
/// data_type: DataType::Int(None),
|
||||||
|
/// collation: None,
|
||||||
|
/// options: vec![],
|
||||||
|
/// }]);
|
||||||
|
/// // You can access internal elements with ease
|
||||||
|
/// assert!(builder.if_not_exists);
|
||||||
|
/// // Convert to a statement
|
||||||
|
/// assert_eq!(
|
||||||
|
/// builder.build().to_string(),
|
||||||
|
/// "CREATE TABLE IF NOT EXISTS table_name (c1 INT)"
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [1]: crate::ast::Statement::CreateTable
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub struct CreateTableBuilder {
|
||||||
|
pub or_replace: bool,
|
||||||
|
pub temporary: bool,
|
||||||
|
pub external: bool,
|
||||||
|
pub global: Option<bool>,
|
||||||
|
pub if_not_exists: bool,
|
||||||
|
pub name: ObjectName,
|
||||||
|
pub columns: Vec<ColumnDef>,
|
||||||
|
pub constraints: Vec<TableConstraint>,
|
||||||
|
pub hive_distribution: HiveDistributionStyle,
|
||||||
|
pub hive_formats: Option<HiveFormat>,
|
||||||
|
pub table_properties: Vec<SqlOption>,
|
||||||
|
pub with_options: Vec<SqlOption>,
|
||||||
|
pub file_format: Option<FileFormat>,
|
||||||
|
pub location: Option<String>,
|
||||||
|
pub query: Option<Box<Query>>,
|
||||||
|
pub without_rowid: bool,
|
||||||
|
pub like: Option<ObjectName>,
|
||||||
|
pub clone: Option<ObjectName>,
|
||||||
|
pub engine: Option<String>,
|
||||||
|
pub default_charset: Option<String>,
|
||||||
|
pub collation: Option<String>,
|
||||||
|
pub on_commit: Option<OnCommit>,
|
||||||
|
pub on_cluster: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreateTableBuilder {
|
||||||
|
pub fn new(name: ObjectName) -> Self {
|
||||||
|
Self {
|
||||||
|
or_replace: false,
|
||||||
|
temporary: false,
|
||||||
|
external: false,
|
||||||
|
global: None,
|
||||||
|
if_not_exists: false,
|
||||||
|
name,
|
||||||
|
columns: vec![],
|
||||||
|
constraints: vec![],
|
||||||
|
hive_distribution: HiveDistributionStyle::NONE,
|
||||||
|
hive_formats: None,
|
||||||
|
table_properties: vec![],
|
||||||
|
with_options: vec![],
|
||||||
|
file_format: None,
|
||||||
|
location: None,
|
||||||
|
query: None,
|
||||||
|
without_rowid: false,
|
||||||
|
like: None,
|
||||||
|
clone: None,
|
||||||
|
engine: None,
|
||||||
|
default_charset: None,
|
||||||
|
collation: None,
|
||||||
|
on_commit: None,
|
||||||
|
on_cluster: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn or_replace(mut self, or_replace: bool) -> Self {
|
||||||
|
self.or_replace = or_replace;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn temporary(mut self, temporary: bool) -> Self {
|
||||||
|
self.temporary = temporary;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn external(mut self, external: bool) -> Self {
|
||||||
|
self.external = external;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn global(mut self, global: Option<bool>) -> Self {
|
||||||
|
self.global = global;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn if_not_exists(mut self, if_not_exists: bool) -> Self {
|
||||||
|
self.if_not_exists = if_not_exists;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn columns(mut self, columns: Vec<ColumnDef>) -> Self {
|
||||||
|
self.columns = columns;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn constraints(mut self, constraints: Vec<TableConstraint>) -> Self {
|
||||||
|
self.constraints = constraints;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hive_distribution(mut self, hive_distribution: HiveDistributionStyle) -> Self {
|
||||||
|
self.hive_distribution = hive_distribution;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hive_formats(mut self, hive_formats: Option<HiveFormat>) -> Self {
|
||||||
|
self.hive_formats = hive_formats;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn table_properties(mut self, table_properties: Vec<SqlOption>) -> Self {
|
||||||
|
self.table_properties = table_properties;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_options(mut self, with_options: Vec<SqlOption>) -> Self {
|
||||||
|
self.with_options = with_options;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn file_format(mut self, file_format: Option<FileFormat>) -> Self {
|
||||||
|
self.file_format = file_format;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn location(mut self, location: Option<String>) -> Self {
|
||||||
|
self.location = location;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn query(mut self, query: Option<Box<Query>>) -> Self {
|
||||||
|
self.query = query;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn without_rowid(mut self, without_rowid: bool) -> Self {
|
||||||
|
self.without_rowid = without_rowid;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn like(mut self, like: Option<ObjectName>) -> Self {
|
||||||
|
self.like = like;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
// Different name to allow the object to be cloned
|
||||||
|
pub fn clone_clause(mut self, clone: Option<ObjectName>) -> Self {
|
||||||
|
self.clone = clone;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn engine(mut self, engine: Option<String>) -> Self {
|
||||||
|
self.engine = engine;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_charset(mut self, default_charset: Option<String>) -> Self {
|
||||||
|
self.default_charset = default_charset;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collation(mut self, collation: Option<String>) -> Self {
|
||||||
|
self.collation = collation;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_commit(mut self, on_commit: Option<OnCommit>) -> Self {
|
||||||
|
self.on_commit = on_commit;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_cluster(mut self, on_cluster: Option<String>) -> Self {
|
||||||
|
self.on_cluster = on_cluster;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Statement {
|
||||||
|
Statement::CreateTable {
|
||||||
|
or_replace: self.or_replace,
|
||||||
|
temporary: self.temporary,
|
||||||
|
external: self.external,
|
||||||
|
global: self.global,
|
||||||
|
if_not_exists: self.if_not_exists,
|
||||||
|
name: self.name,
|
||||||
|
columns: self.columns,
|
||||||
|
constraints: self.constraints,
|
||||||
|
hive_distribution: self.hive_distribution,
|
||||||
|
hive_formats: self.hive_formats,
|
||||||
|
table_properties: self.table_properties,
|
||||||
|
with_options: self.with_options,
|
||||||
|
file_format: self.file_format,
|
||||||
|
location: self.location,
|
||||||
|
query: self.query,
|
||||||
|
without_rowid: self.without_rowid,
|
||||||
|
like: self.like,
|
||||||
|
clone: self.clone,
|
||||||
|
engine: self.engine,
|
||||||
|
default_charset: self.default_charset,
|
||||||
|
collation: self.collation,
|
||||||
|
on_commit: self.on_commit,
|
||||||
|
on_cluster: self.on_cluster,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Statement> for CreateTableBuilder {
|
||||||
|
type Error = ParserError;
|
||||||
|
|
||||||
|
// As the builder can be transformed back to a statement, it shouldn't be a problem to take the
|
||||||
|
// ownership.
|
||||||
|
fn try_from(stmt: Statement) -> Result<Self, Self::Error> {
|
||||||
|
match stmt {
|
||||||
|
Statement::CreateTable {
|
||||||
|
or_replace,
|
||||||
|
temporary,
|
||||||
|
external,
|
||||||
|
global,
|
||||||
|
if_not_exists,
|
||||||
|
name,
|
||||||
|
columns,
|
||||||
|
constraints,
|
||||||
|
hive_distribution,
|
||||||
|
hive_formats,
|
||||||
|
table_properties,
|
||||||
|
with_options,
|
||||||
|
file_format,
|
||||||
|
location,
|
||||||
|
query,
|
||||||
|
without_rowid,
|
||||||
|
like,
|
||||||
|
clone,
|
||||||
|
engine,
|
||||||
|
default_charset,
|
||||||
|
collation,
|
||||||
|
on_commit,
|
||||||
|
on_cluster,
|
||||||
|
} => Ok(Self {
|
||||||
|
or_replace,
|
||||||
|
temporary,
|
||||||
|
external,
|
||||||
|
global,
|
||||||
|
if_not_exists,
|
||||||
|
name,
|
||||||
|
columns,
|
||||||
|
constraints,
|
||||||
|
hive_distribution,
|
||||||
|
hive_formats,
|
||||||
|
table_properties,
|
||||||
|
with_options,
|
||||||
|
file_format,
|
||||||
|
location,
|
||||||
|
query,
|
||||||
|
without_rowid,
|
||||||
|
like,
|
||||||
|
clone,
|
||||||
|
engine,
|
||||||
|
default_charset,
|
||||||
|
collation,
|
||||||
|
on_commit,
|
||||||
|
on_cluster,
|
||||||
|
}),
|
||||||
|
_ => Err(ParserError::ParserError(format!(
|
||||||
|
"Expected create table statement, but received: {stmt}"
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::ast::helpers::stmt_create_table::CreateTableBuilder;
|
||||||
|
use crate::ast::{Ident, ObjectName, Statement};
|
||||||
|
use crate::parser::ParserError;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_from_valid_statement() {
|
||||||
|
let builder = CreateTableBuilder::new(ObjectName(vec![Ident::new("table_name")]));
|
||||||
|
|
||||||
|
let stmt = builder.clone().build();
|
||||||
|
|
||||||
|
assert_eq!(builder, CreateTableBuilder::try_from(stmt).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_from_invalid_statement() {
|
||||||
|
let stmt = Statement::Commit { chain: false };
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
CreateTableBuilder::try_from(stmt).unwrap_err(),
|
||||||
|
ParserError::ParserError(
|
||||||
|
"Expected create table statement, but received: COMMIT".to_owned()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@
|
||||||
//! SQL Abstract Syntax Tree (AST) types
|
//! SQL Abstract Syntax Tree (AST) types
|
||||||
mod data_type;
|
mod data_type;
|
||||||
mod ddl;
|
mod ddl;
|
||||||
|
pub mod helpers;
|
||||||
mod operator;
|
mod operator;
|
||||||
mod query;
|
mod query;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
use alloc::boxed::Box;
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
|
@ -27,6 +27,7 @@ use log::debug;
|
||||||
use IsLateral::*;
|
use IsLateral::*;
|
||||||
use IsOptional::*;
|
use IsOptional::*;
|
||||||
|
|
||||||
|
use crate::ast::helpers::stmt_create_table::CreateTableBuilder;
|
||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
use crate::dialect::*;
|
use crate::dialect::*;
|
||||||
use crate::keywords::{self, Keyword};
|
use crate::keywords::{self, Keyword};
|
||||||
|
@ -2032,31 +2033,18 @@ impl<'a> Parser<'a> {
|
||||||
};
|
};
|
||||||
let location = hive_formats.location.clone();
|
let location = hive_formats.location.clone();
|
||||||
let table_properties = self.parse_options(Keyword::TBLPROPERTIES)?;
|
let table_properties = self.parse_options(Keyword::TBLPROPERTIES)?;
|
||||||
Ok(Statement::CreateTable {
|
Ok(CreateTableBuilder::new(table_name)
|
||||||
name: table_name,
|
.columns(columns)
|
||||||
columns,
|
.constraints(constraints)
|
||||||
constraints,
|
.hive_distribution(hive_distribution)
|
||||||
hive_distribution,
|
.hive_formats(Some(hive_formats))
|
||||||
hive_formats: Some(hive_formats),
|
.table_properties(table_properties)
|
||||||
with_options: vec![],
|
.or_replace(or_replace)
|
||||||
table_properties,
|
.if_not_exists(if_not_exists)
|
||||||
or_replace,
|
.external(true)
|
||||||
if_not_exists,
|
.file_format(file_format)
|
||||||
external: true,
|
.location(location)
|
||||||
global: None,
|
.build())
|
||||||
temporary: false,
|
|
||||||
file_format,
|
|
||||||
location,
|
|
||||||
query: None,
|
|
||||||
without_rowid: false,
|
|
||||||
like: None,
|
|
||||||
clone: None,
|
|
||||||
default_charset: None,
|
|
||||||
engine: None,
|
|
||||||
collation: None,
|
|
||||||
on_commit: None,
|
|
||||||
on_cluster: None,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_file_format(&mut self) -> Result<FileFormat, ParserError> {
|
pub fn parse_file_format(&mut self) -> Result<FileFormat, ParserError> {
|
||||||
|
@ -2667,31 +2655,27 @@ impl<'a> Parser<'a> {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Statement::CreateTable {
|
Ok(CreateTableBuilder::new(table_name)
|
||||||
name: table_name,
|
.temporary(temporary)
|
||||||
temporary,
|
.columns(columns)
|
||||||
columns,
|
.constraints(constraints)
|
||||||
constraints,
|
.with_options(with_options)
|
||||||
with_options,
|
.table_properties(table_properties)
|
||||||
table_properties,
|
.or_replace(or_replace)
|
||||||
or_replace,
|
.if_not_exists(if_not_exists)
|
||||||
if_not_exists,
|
.hive_distribution(hive_distribution)
|
||||||
hive_distribution,
|
.hive_formats(Some(hive_formats))
|
||||||
hive_formats: Some(hive_formats),
|
.global(global)
|
||||||
external: false,
|
.query(query)
|
||||||
global,
|
.without_rowid(without_rowid)
|
||||||
file_format: None,
|
.like(like)
|
||||||
location: None,
|
.clone_clause(clone)
|
||||||
query,
|
.engine(engine)
|
||||||
without_rowid,
|
.default_charset(default_charset)
|
||||||
like,
|
.collation(collation)
|
||||||
clone,
|
.on_commit(on_commit)
|
||||||
engine,
|
.on_cluster(on_cluster)
|
||||||
default_charset,
|
.build())
|
||||||
collation,
|
|
||||||
on_commit,
|
|
||||||
on_cluster,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_columns(&mut self) -> Result<(Vec<ColumnDef>, Vec<TableConstraint>), ParserError> {
|
pub fn parse_columns(&mut self) -> Result<(Vec<ColumnDef>, Vec<TableConstraint>), ParserError> {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
/// on this module, as it will change without notice.
|
/// on this module, as it will change without notice.
|
||||||
//
|
//
|
||||||
// Integration tests (i.e. everything under `tests/`) import this
|
// Integration tests (i.e. everything under `tests/`) import this
|
||||||
// via `tests/test_utils/mod.rs`.
|
// via `tests/test_utils/helpers`.
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use alloc::{
|
use alloc::{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue