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

View file

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

View file

@ -1,4 +1,4 @@
use super::SQLIdent; use super::{SQLIdent, SQLObjectName};
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum AlterOperation { pub enum AlterOperation {
@ -30,7 +30,7 @@ pub enum TableKey {
Key(Key), Key(Key),
ForeignKey { ForeignKey {
key: Key, key: Key,
foreign_table: String, foreign_table: SQLObjectName,
referred_columns: Vec<SQLIdent>, referred_columns: Vec<SQLIdent>,
}, },
} }
@ -53,7 +53,7 @@ impl ToString for TableKey {
"{} FOREIGN KEY ({}) REFERENCES {}({})", "{} FOREIGN KEY ({}) REFERENCES {}({})",
key.name, key.name,
key.columns.join(", "), key.columns.join(", "),
foreign_table, foreign_table.to_string(),
referred_columns.join(", ") 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)?; let identifier = self.parse_compound_identifier(&Token::Period)?;
match identifier { match identifier {
// TODO: should store the compound identifier itself // 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)), other => parser_err!(format!("Expecting compound identifier, found: {:?}", other)),
} }
} }

View file

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

View file

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