mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-30 18:57:21 +00:00
support create external table
This commit is contained in:
parent
d1b5668fd3
commit
f0f6082eff
3 changed files with 150 additions and 49 deletions
1
sql.sql
Normal file
1
sql.sql
Normal file
|
@ -0,0 +1 @@
|
|||
CREATE EXTERNAL TABLE T (X INT) STORED AS TEXTFILE LOCATION '/home/admin/a.csv';
|
|
@ -250,6 +250,15 @@ pub enum SQLStatement {
|
|||
/// Optional schema
|
||||
columns: Vec<SQLColumnDef>,
|
||||
},
|
||||
/// CREATE EXTERNAL TABLE
|
||||
SQLCreateExternalTable {
|
||||
/// Table name
|
||||
name: SQLObjectName,
|
||||
/// Optional schema
|
||||
columns: Vec<SQLColumnDef>,
|
||||
file_format: FileFormat,
|
||||
location: String,
|
||||
},
|
||||
/// ALTER TABLE
|
||||
SQLAlterTable {
|
||||
/// Table name
|
||||
|
@ -370,6 +379,17 @@ impl ToString for SQLStatement {
|
|||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
),
|
||||
SQLStatement::SQLCreateExternalTable { name, columns, file_format, location } => format!(
|
||||
"CREATE TABLE {} ({}) STORED AS {} LOCATION {}",
|
||||
name.to_string(),
|
||||
columns
|
||||
.iter()
|
||||
.map(|c| c.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
file_format.to_string(),
|
||||
location
|
||||
),
|
||||
SQLStatement::SQLAlterTable { name, operation } => {
|
||||
format!("ALTER TABLE {} {}", name.to_string(), operation.to_string())
|
||||
}
|
||||
|
@ -429,3 +449,53 @@ impl ToString for SQLColumnDef {
|
|||
s
|
||||
}
|
||||
}
|
||||
|
||||
/// External table's available file format
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum FileFormat {
|
||||
TEXTFILE,
|
||||
SEQUENCEFILE,
|
||||
ORC,
|
||||
PARQUET,
|
||||
AVRO,
|
||||
RCFILE,
|
||||
JSONFILE,
|
||||
}
|
||||
|
||||
impl ToString for FileFormat {
|
||||
fn to_string(&self) -> String {
|
||||
use self::FileFormat::*;
|
||||
match self {
|
||||
TEXTFILE => "TEXTFILE".to_string(),
|
||||
SEQUENCEFILE => "SEQUENCEFILE".to_string(),
|
||||
ORC => "ORC".to_string(),
|
||||
PARQUET => "PARQUET".to_string(),
|
||||
AVRO => "AVRO".to_string(),
|
||||
RCFILE => "RCFILE".to_string(),
|
||||
JSONFILE => "TEXTFILE".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use std::str::FromStr;
|
||||
use sqlparser::ParserError;
|
||||
impl FromStr for FileFormat {
|
||||
type Err = ParserError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
use self::FileFormat::*;
|
||||
match s {
|
||||
"TEXTFILE" => Ok(TEXTFILE),
|
||||
"SEQUENCEFILE" => Ok(SEQUENCEFILE),
|
||||
"ORC" => Ok(ORC),
|
||||
"PARQUET" => Ok(PARQUET),
|
||||
"AVRO" => Ok(AVRO),
|
||||
"RCFILE" => Ok(RCFILE),
|
||||
"JSONFILE" => Ok(JSONFILE),
|
||||
_ => Err(ParserError::ParserError(format!(
|
||||
"Unexpected token for file format: {}",
|
||||
s
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
128
src/sqlparser.rs
128
src/sqlparser.rs
|
@ -620,6 +620,8 @@ impl Parser {
|
|||
} else if self.parse_keyword("MATERIALIZED") || self.parse_keyword("VIEW") {
|
||||
self.prev_token();
|
||||
self.parse_create_view()
|
||||
} else if self.parse_keyword("EXTERNAL") {
|
||||
self.parse_create_external_table()
|
||||
} else {
|
||||
parser_err!(format!(
|
||||
"Unexpected token after CREATE: {:?}",
|
||||
|
@ -628,6 +630,25 @@ impl Parser {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_create_external_table(&mut self) -> Result<SQLStatement, ParserError> {
|
||||
self.expect_keyword("TABLE")?;
|
||||
let table_name = self.parse_object_name()?;
|
||||
let columns = self.parse_columns()?;
|
||||
self.expect_keyword("STORED")?;
|
||||
self.expect_keyword("AS")?;
|
||||
let file_format = self.parse_identifier()?.parse::<FileFormat>()?;
|
||||
|
||||
self.expect_keyword("LOCATION")?;
|
||||
let location = self.parse_literal_string()?;
|
||||
|
||||
Ok(SQLStatement::SQLCreateExternalTable {
|
||||
name: table_name,
|
||||
columns,
|
||||
file_format,
|
||||
location
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_create_view(&mut self) -> Result<SQLStatement, ParserError> {
|
||||
let materialized = self.parse_keyword("MATERIALIZED");
|
||||
self.expect_keyword("VIEW")?;
|
||||
|
@ -650,62 +671,71 @@ impl Parser {
|
|||
pub fn parse_create_table(&mut self) -> Result<SQLStatement, ParserError> {
|
||||
let table_name = self.parse_object_name()?;
|
||||
// parse optional column list (schema)
|
||||
let mut columns = vec![];
|
||||
if self.consume_token(&Token::LParen) {
|
||||
loop {
|
||||
match self.next_token() {
|
||||
Some(Token::SQLWord(column_name)) => {
|
||||
let data_type = self.parse_data_type()?;
|
||||
let is_primary = self.parse_keywords(vec!["PRIMARY", "KEY"]);
|
||||
let is_unique = self.parse_keyword("UNIQUE");
|
||||
let default = if self.parse_keyword("DEFAULT") {
|
||||
let expr = self.parse_default_expr(0)?;
|
||||
Some(expr)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let allow_null = if self.parse_keywords(vec!["NOT", "NULL"]) {
|
||||
false
|
||||
} else if self.parse_keyword("NULL") {
|
||||
true
|
||||
} else {
|
||||
true
|
||||
};
|
||||
debug!("default: {:?}", default);
|
||||
let columns = self.parse_columns()?;
|
||||
|
||||
columns.push(SQLColumnDef {
|
||||
name: column_name.as_sql_ident(),
|
||||
data_type: data_type,
|
||||
allow_null,
|
||||
is_primary,
|
||||
is_unique,
|
||||
default,
|
||||
});
|
||||
match self.next_token() {
|
||||
Some(Token::Comma) => {}
|
||||
Some(Token::RParen) => {
|
||||
break;
|
||||
}
|
||||
other => {
|
||||
return parser_err!(format!(
|
||||
"Expected ',' or ')' after column definition but found {:?}",
|
||||
other
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
unexpected => {
|
||||
return parser_err!(format!("Expected column name, got {:?}", unexpected));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(SQLStatement::SQLCreateTable {
|
||||
name: table_name,
|
||||
columns,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_columns(&mut self) -> Result<Vec<SQLColumnDef>, ParserError> {
|
||||
let mut columns = vec![];
|
||||
if !self.consume_token(&Token::LParen) {
|
||||
return Ok(columns);
|
||||
}
|
||||
|
||||
loop {
|
||||
match self.next_token() {
|
||||
Some(Token::SQLWord(column_name)) => {
|
||||
let data_type = self.parse_data_type()?;
|
||||
let is_primary = self.parse_keywords(vec!["PRIMARY", "KEY"]);
|
||||
let is_unique = self.parse_keyword("UNIQUE");
|
||||
let default = if self.parse_keyword("DEFAULT") {
|
||||
let expr = self.parse_default_expr(0)?;
|
||||
Some(expr)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let allow_null = if self.parse_keywords(vec!["NOT", "NULL"]) {
|
||||
false
|
||||
} else if self.parse_keyword("NULL") {
|
||||
true
|
||||
} else {
|
||||
true
|
||||
};
|
||||
debug!("default: {:?}", default);
|
||||
|
||||
columns.push(SQLColumnDef {
|
||||
name: column_name.as_sql_ident(),
|
||||
data_type,
|
||||
allow_null,
|
||||
is_primary,
|
||||
is_unique,
|
||||
default,
|
||||
});
|
||||
match self.next_token() {
|
||||
Some(Token::Comma) => {}
|
||||
Some(Token::RParen) => {
|
||||
break;
|
||||
}
|
||||
other => {
|
||||
return parser_err!(format!(
|
||||
"Expected ',' or ')' after column definition but found {:?}",
|
||||
other
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
unexpected => {
|
||||
return parser_err!(format!("Expected column name, got {:?}", unexpected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(columns)
|
||||
}
|
||||
|
||||
pub fn parse_table_key(&mut self, constraint_name: SQLIdent) -> Result<TableKey, ParserError> {
|
||||
let is_primary_key = self.parse_keywords(vec!["PRIMARY", "KEY"]);
|
||||
let is_unique_key = self.parse_keywords(vec!["UNIQUE", "KEY"]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue