Skip to content

Create table builder structure #659

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/ast/helpers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod stmt_create_table;
323 changes: 323 additions & 0 deletions src/ast/helpers/stmt_create_table.rs
Original file line number Diff line number Diff line change
@@ -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]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

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()
)
);
}
}
1 change: 1 addition & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
//! SQL Abstract Syntax Tree (AST) types
mod data_type;
mod ddl;
pub mod helpers;
mod operator;
mod query;
mod value;
Expand Down
2 changes: 0 additions & 2 deletions src/ast/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
#[cfg(not(feature = "std"))]
use alloc::string::String;
use core::fmt;
Expand Down
Loading