Introduce SQLObjectName struct (4.1/4.4)

(To store "A name of a table, view, custom type, etc., possibly
multi-part, i.e. db.schema.obj".)

Before this change

  - some places used `String` for this (these are updated in this commit)

  - while others (notably SQLStatement::SQLDelete::relation, which is
    the reason for this series of commits) relied on
    ASTNode::SQLCompoundIdentifier (which is also backed by a 
    Vec<SQLIdent>, but, as a variant of ASTNode enum, is not convenient
    to use when you know you need that specific variant).
This commit is contained in:
Nickolay Ponomarev 2019-01-30 21:16:31 +03:00
parent 215820ef66
commit 523f086be7
6 changed files with 42 additions and 27 deletions

View file

@ -153,7 +153,7 @@ pub enum SQLStatement {
/// INSERT
SQLInsert {
/// TABLE
table_name: String,
table_name: SQLObjectName,
/// COLUMNS
columns: Vec<SQLIdent>,
/// VALUES (vector of rows to insert)
@ -161,7 +161,7 @@ pub enum SQLStatement {
},
SQLCopy {
/// TABLE
table_name: String,
table_name: SQLObjectName,
/// COLUMNS
columns: Vec<SQLIdent>,
/// VALUES a vector of values to be copied
@ -170,7 +170,7 @@ pub enum SQLStatement {
/// UPDATE
SQLUpdate {
/// TABLE
table_name: String,
table_name: SQLObjectName,
/// Column assignments
assignments: Vec<SQLAssignment>,
/// WHERE
@ -186,14 +186,14 @@ pub enum SQLStatement {
/// CREATE TABLE
SQLCreateTable {
/// Table name
name: String,
name: SQLObjectName,
/// Optional schema
columns: Vec<SQLColumnDef>,
},
/// ALTER TABLE
SQLAlterTable {
/// Table name
name: String,
name: SQLObjectName,
operation: AlterOperation,
},
}
@ -207,7 +207,7 @@ impl ToString for SQLStatement {
columns,
values,
} => {
let mut s = format!("INSERT INTO {}", table_name);
let mut s = format!("INSERT INTO {}", table_name.to_string());
if columns.len() > 0 {
s += &format!(" ({})", columns.join(", "));
}
@ -232,7 +232,7 @@ impl ToString for SQLStatement {
columns,
values,
} => {
let mut s = format!("COPY {}", table_name);
let mut s = format!("COPY {}", table_name.to_string());
if columns.len() > 0 {
s += &format!(
" ({})",
@ -262,7 +262,7 @@ impl ToString for SQLStatement {
assignments,
selection,
} => {
let mut s = format!("UPDATE {}", table_name);
let mut s = format!("UPDATE {}", table_name.to_string());
if assignments.len() > 0 {
s += &format!(
"{}",
@ -293,7 +293,7 @@ impl ToString for SQLStatement {
}
SQLStatement::SQLCreateTable { name, columns } => format!(
"CREATE TABLE {} ({})",
name,
name.to_string(),
columns
.iter()
.map(|c| c.to_string())
@ -301,12 +301,22 @@ impl ToString for SQLStatement {
.join(", ")
),
SQLStatement::SQLAlterTable { name, operation } => {
format!("ALTER TABLE {} {}", name, operation.to_string())
format!("ALTER TABLE {} {}", name.to_string(), operation.to_string())
}
}
}
}
/// A name of a table, view, custom type, etc., possibly multi-part, i.e. db.schema.obj
#[derive(Debug, Clone, PartialEq)]
pub struct SQLObjectName(pub Vec<SQLIdent>);
impl ToString for SQLObjectName {
fn to_string(&self) -> String {
self.0.join(".")
}
}
/// SQL assignment `foo = expr` as used in SQLUpdate
#[derive(Debug, Clone, PartialEq)]
pub struct SQLAssignment {

View file

@ -1,3 +1,5 @@
use super::SQLObjectName;
/// SQL datatypes for literals in SQL statements
#[derive(Debug, Clone, PartialEq)]
pub enum SQLType {
@ -44,7 +46,7 @@ pub enum SQLType {
/// Bytea
Bytea,
/// Custom type such as enums
Custom(String),
Custom(SQLObjectName),
/// Arrays
Array(Box<SQLType>),
}

View file

@ -1,4 +1,4 @@
use super::SQLIdent;
use super::{SQLIdent, SQLObjectName};
#[derive(Debug, PartialEq, Clone)]
pub enum AlterOperation {
@ -30,7 +30,7 @@ pub enum TableKey {
Key(Key),
ForeignKey {
key: Key,
foreign_table: String,
foreign_table: SQLObjectName,
referred_columns: Vec<SQLIdent>,
},
}
@ -53,7 +53,7 @@ impl ToString for TableKey {
"{} FOREIGN KEY ({}) REFERENCES {}({})",
key.name,
key.columns.join(", "),
foreign_table,
foreign_table.to_string(),
referred_columns.join(", ")
),
}

View file

@ -1060,11 +1060,11 @@ impl Parser {
}
}
pub fn parse_tablename(&mut self) -> Result<String, ParserError> {
pub fn parse_tablename(&mut self) -> Result<SQLObjectName, ParserError> {
let identifier = self.parse_compound_identifier(&Token::Period)?;
match identifier {
// TODO: should store the compound identifier itself
ASTNode::SQLCompoundIdentifier(idents) => Ok(idents.join(".")),
ASTNode::SQLCompoundIdentifier(idents) => Ok(SQLObjectName(idents)),
other => parser_err!(format!("Expecting compound identifier, found: {:?}", other)),
}
}

View file

@ -364,7 +364,7 @@ fn parse_create_table() {
);
match ast {
SQLStatement::SQLCreateTable { name, columns } => {
assert_eq!("uk_cities", name);
assert_eq!("uk_cities", name.to_string());
assert_eq!(3, columns.len());
let c_name = &columns[0];

View file

@ -31,7 +31,7 @@ fn parse_simple_insert() {
values,
..
} => {
assert_eq!(table_name, "customer");
assert_eq!(table_name.to_string(), "customer");
assert!(columns.is_empty());
assert_eq!(
vec![vec![
@ -56,7 +56,7 @@ fn parse_common_insert() {
values,
..
} => {
assert_eq!(table_name, "public.customer");
assert_eq!(table_name.to_string(), "public.customer");
assert!(columns.is_empty());
assert_eq!(
vec![vec![
@ -81,7 +81,7 @@ fn parse_complex_insert() {
values,
..
} => {
assert_eq!(table_name, "db.public.customer");
assert_eq!(table_name.to_string(), "db.public.customer");
assert!(columns.is_empty());
assert_eq!(
vec![vec![
@ -120,7 +120,7 @@ fn parse_insert_with_columns() {
values,
..
} => {
assert_eq!(table_name, "public.customer");
assert_eq!(table_name.to_string(), "public.customer");
assert_eq!(
columns,
vec!["id".to_string(), "name".to_string(), "active".to_string()]
@ -164,7 +164,7 @@ fn parse_create_table_with_defaults() {
);
match one_statement_parses_to(&sql, "") {
SQLStatement::SQLCreateTable { name, columns } => {
assert_eq!("public.customer", name);
assert_eq!("public.customer", name.to_string());
assert_eq!(10, columns.len());
let c_name = &columns[0];
@ -205,7 +205,7 @@ fn parse_create_table_from_pg_dump() {
)");
match one_statement_parses_to(&sql, "") {
SQLStatement::SQLCreateTable { name, columns } => {
assert_eq!("public.customer", name);
assert_eq!("public.customer", name.to_string());
let c_customer_id = &columns[0];
assert_eq!("customer_id", c_customer_id.name);
@ -238,7 +238,10 @@ fn parse_create_table_from_pg_dump() {
let c_release_year = &columns[10];
assert_eq!(
SQLType::Custom("public.year".to_string()),
SQLType::Custom(SQLObjectName(vec![
"public".to_string(),
"year".to_string()
])),
c_release_year.data_type
);
}
@ -259,7 +262,7 @@ fn parse_create_table_with_inherit() {
);
match verified_stmt(&sql) {
SQLStatement::SQLCreateTable { name, columns } => {
assert_eq!("bazaar.settings", name);
assert_eq!("bazaar.settings", name.to_string());
let c_name = &columns[0];
assert_eq!("settings_id", c_name.name);
@ -288,7 +291,7 @@ fn parse_alter_table_constraint_primary_key() {
);
match verified_stmt(&sql) {
SQLStatement::SQLAlterTable { name, .. } => {
assert_eq!(name, "bazaar.address");
assert_eq!(name.to_string(), "bazaar.address");
}
_ => assert!(false),
}
@ -301,7 +304,7 @@ fn parse_alter_table_constraint_foreign_key() {
ADD CONSTRAINT customer_address_id_fkey FOREIGN KEY (address_id) REFERENCES public.address(address_id)");
match verified_stmt(&sql) {
SQLStatement::SQLAlterTable { name, .. } => {
assert_eq!(name, "public.customer");
assert_eq!(name.to_string(), "public.customer");
}
_ => assert!(false),
}