Skip to content

Commit

Permalink
Merge pull request #8 from oniony/refactor-async-sqlx
Browse files Browse the repository at this point in the history
chore(sqlx): Refactor to use async Sqlx
  • Loading branch information
oniony authored Oct 12, 2024
2 parents f0c200e + 1ed441b commit a2214bb
Show file tree
Hide file tree
Showing 27 changed files with 2,658 additions and 1,126 deletions.
1,251 changes: 996 additions & 255 deletions Cargo.lock

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ version = "0.0.0"
edition = "2021"

[dependencies]
clap = { version = "4.5.4", features = ["derive"] }
colored = "2.1.0"
postgres = "0.19.7"
clap = { version = "=4.5.20", features = ["derive"] }
colored = "=2.1.0"
itertools = "=0.13.0"
sqlformat = "=0.2.6"
sqlx = { version = "=0.8.2", features = ["runtime-tokio", "postgres", "macros"] }
tokio = { version = "=1.40.0", features = ["rt", "rt-multi-thread", "macros", "signal"] }
futures-core = "=0.3.31"
futures = "=0.3.31"
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@

Little Bobby Diff Tool is a CLI tool to compare database schemas.

It currently compares the following across one or more schemas.
RDBMS support:

- [ ] MySQL
- [ ] Oracle
- [X] PostgreSQL
- [ ] SQLite
- [ ] SQL Server

Items compared:

- [X] Columns
- [X] Column Privileges
- [ ] Indices
- [X] Routines
- [X] Routine Privileges
- [X] Sequences
Expand Down
6 changes: 2 additions & 4 deletions db/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
version: '2'

services:
left:
image: postgres
image: postgres:latest
container_name: postgres-left
environment:
- POSTGRES_PASSWORD=postgres
ports:
- 8901:5432
restart: unless-stopped
right:
image: postgres
image: postgres:latest
container_name: postgres-right
environment:
- POSTGRES_PASSWORD=postgres
Expand Down
16 changes: 8 additions & 8 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod args;
use std::process;
use clap::{Parser};
use colored::Colorize;
use postgres::Error;
use sqlx::Error;

use crate::{compare, db};
use crate::cli::args::{Args, Colouring::Always, Colouring::Never};
Expand All @@ -20,8 +20,8 @@ use crate::compare::report::sequence::SequenceComparison;
use crate::compare::report::sequence::SequenceComparison::{SequenceAdded, SequenceMaintained, SequenceRemoved};
use crate::compare::report::table::TableComparison;
use crate::compare::report::table::TableComparison::{TableAdded, TableMaintained, TableRemoved};
use crate::compare::report::table_column::TableColumnComparison;
use crate::compare::report::table_column::TableColumnComparison::{ColumnAdded, ColumnMaintained, ColumnRemoved};
use crate::compare::report::column::ColumnComparison;
use crate::compare::report::column::ColumnComparison::{ColumnAdded, ColumnMaintained, ColumnRemoved};
use crate::compare::report::table_constraint::TableConstraintComparison;
use crate::compare::report::table_constraint::TableConstraintComparison::{ConstraintAdded, ConstraintMaintained, ConstraintRemoved};
use crate::compare::report::table_trigger::TableTriggerComparison::{TriggerAdded, TriggerMaintained, TriggerRemoved};
Expand Down Expand Up @@ -51,9 +51,9 @@ impl CLI {
CLI { args }
}

pub fn run(&self) -> Result<i32, Error> {
let left_db = db::Database::connect(self.args.left.as_str())?;
let right_db = db::Database::connect(self.args.right.as_str())?;
pub async fn run(&self) -> Result<i32, Error> {
let left_db = db::Database::connect(self.args.left.as_str()).await?;
let right_db = db::Database::connect(self.args.right.as_str()).await?;

let mut comparer = compare::Comparer::new(
left_db,
Expand All @@ -64,7 +64,7 @@ impl CLI {

let mut differences = 0;

let report = comparer.compare(self.args.schema.clone())?;
let report = comparer.compare(self.args.schema.clone()).await?;
differences += self.render_schema_report(report);

process::exit(differences);
Expand Down Expand Up @@ -286,7 +286,7 @@ impl CLI {
differences
}

fn render_table_column_report(&self, report: &Report<TableColumnComparison>) -> i32 {
fn render_table_column_report(&self, report: &Report<ColumnComparison>) -> i32 {
let mut differences = 0;

for column in &report.entries {
Expand Down
624 changes: 399 additions & 225 deletions src/compare/mod.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ use crate::compare::report::privilege::{PrivilegeComparison};
use crate::compare::report::property::{PropertyComparison};
use crate::compare::report::{HasChanges, Report};

pub enum TableColumnComparison {
pub enum ColumnComparison {
ColumnAdded { column_name: String },
ColumnRemoved { column_name: String },
ColumnMaintained { column_name: String, properties: Report<PropertyComparison>, privileges: Report<PrivilegeComparison> }
}

impl HasChanges for TableColumnComparison {
impl HasChanges for ColumnComparison {
fn has_changes(&self) -> bool {
match self {
TableColumnComparison::ColumnAdded { .. } | TableColumnComparison::ColumnRemoved { .. } => true,
TableColumnComparison::ColumnMaintained { column_name: _column_name, properties, privileges } =>
ColumnComparison::ColumnAdded { .. } | ColumnComparison::ColumnRemoved { .. } => true,
ColumnComparison::ColumnMaintained { column_name: _column_name, properties, privileges } =>
properties.has_changes() |
privileges.has_changes(),
}
Expand Down
2 changes: 1 addition & 1 deletion src/compare/report/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub mod privilege;
pub mod routine;
pub mod sequence;
pub mod table;
pub mod table_column;
pub mod column;
pub mod table_constraint;
pub mod table_trigger;
pub mod view;
Expand Down
4 changes: 2 additions & 2 deletions src/compare/report/table.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use crate::compare::report::{HasChanges, Report};
use crate::compare::report::privilege::PrivilegeComparison;
use crate::compare::report::property::PropertyComparison;
use crate::compare::report::table_column::TableColumnComparison;
use crate::compare::report::column::ColumnComparison;
use crate::compare::report::table_constraint::TableConstraintComparison;
use crate::compare::report::table_trigger::TableTriggerComparison;

pub enum TableComparison {
TableAdded { table_name: String },
TableRemoved { table_name: String },
TableMaintained { table_name: String, properties: Report<PropertyComparison>, columns: Report<TableColumnComparison>, privileges: Report<PrivilegeComparison>, constraints: Report<TableConstraintComparison>, triggers: Report<TableTriggerComparison> },
TableMaintained { table_name: String, properties: Report<PropertyComparison>, columns: Report<ColumnComparison>, privileges: Report<PrivilegeComparison>, constraints: Report<TableConstraintComparison>, triggers: Report<TableTriggerComparison> },
}

impl HasChanges for TableComparison {
Expand Down
112 changes: 112 additions & 0 deletions src/db/column.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use sqlx::{Error, FromRow, PgConnection};

const QUERY: &str = r#"
SELECT
table_catalog,
table_schema,
table_name,
column_name,
ordinal_position,
column_default,
is_nullable,
data_type,
character_maximum_length,
character_octet_length,
numeric_precision,
numeric_precision_radix,
numeric_scale,
datetime_precision,
interval_type,
interval_precision,
character_set_catalog,
character_set_schema,
character_set_name,
collation_catalog,
collation_schema,
collation_name,
domain_catalog,
domain_schema,
domain_name,
udt_catalog,
udt_schema,
udt_name,
scope_catalog,
scope_schema,
scope_name,
maximum_cardinality,
dtd_identifier,
is_self_referencing,
is_identity,
identity_generation,
identity_start,
identity_increment,
identity_maximum,
identity_minimum,
identity_cycle,
is_generated,
generation_expression,
is_updatable
FROM
information_schema.columns
WHERE
table_schema = ANY($1)
ORDER BY
table_catalog,
table_schema,
table_name,
column_name;"#;

pub async fn columns(connection: &mut PgConnection, schema_names: &[String]) -> Result<Vec<Column>, Error> {
sqlx::query_as(QUERY)
.bind(&schema_names[..])
.fetch_all(connection).await
}

#[derive(Debug, Clone, PartialEq, FromRow)]
pub struct Column {
pub table_catalog: String,
pub table_schema: String,
pub table_name: String,
pub column_name: String,
pub ordinal_position: i32,
pub column_default: Option<String>,
pub is_nullable: String,
pub data_type: String,
pub character_maximum_length: Option<i32>,
pub character_octet_length: Option<i32>,
pub numeric_precision: Option<i32>,
pub numeric_precision_radix: Option<i32>,
pub numeric_scale: Option<i32>,
pub datetime_precision: Option<i32>,
pub interval_type: Option<String>,
pub interval_precision: Option<i32>,
pub character_set_catalog: Option<String>,
pub character_set_schema: Option<String>,
pub character_set_name: Option<String>,
pub collation_catalog: Option<String>,
pub collation_schema: Option<String>,
pub collation_name: Option<String>,
pub domain_catalog: Option<String>,
pub domain_schema: Option<String>,
pub domain_name: Option<String>,
pub udt_catalog: Option<String>,
pub udt_schema: Option<String>,
pub udt_name: Option<String>,
pub scope_catalog: Option<String>,
pub scope_schema: Option<String>,
pub scope_name: Option<String>,
pub maximum_cardinality: Option<i32>,
pub dtd_identifier: Option<String>,
pub is_self_referencing: String,
pub is_identity: String,
pub identity_generation: Option<String>,
pub identity_start: Option<String>,
pub identity_increment: Option<String>,
pub identity_maximum: Option<String>,
pub identity_minimum: Option<String>,
pub identity_cycle: Option<String>,
pub is_generated: String,
pub generation_expression: Option<String>,
pub is_updatable: String,
}

57 changes: 57 additions & 0 deletions src/db/column_privilege.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use sqlx::{Error, FromRow, PgConnection};
use crate::db::privilege::Privilege;

const QUERY: &str = r#"
SELECT
grantor,
grantee,
table_catalog,
table_schema,
table_name,
column_name,
privilege_type,
is_grantable
FROM
information_schema.column_privileges
WHERE
table_schema = ANY($1)
ORDER BY
table_catalog,
table_schema,
table_name,
column_name,
grantor,
grantee,
privilege_type;"#;

pub async fn column_privileges(connection: &mut PgConnection, schema_names: &[String]) -> Result<Vec<ColumnPrivilege>, Error> {
sqlx::query_as(QUERY)
.bind(&schema_names[..])
.fetch_all(connection).await
}

#[derive(Debug, Clone, PartialEq, FromRow)]
pub struct ColumnPrivilege {
pub grantor: String,
pub grantee: String,
pub table_catalog: String,
pub table_schema: String,
pub table_name: String,
pub column_name: String,
pub privilege_type: String,
pub is_grantable: String,
}

impl Privilege for &ColumnPrivilege {
fn grantor(&self) -> &str {
&self.grantor
}

fn grantee(&self) -> &str {
&self.grantee
}

fn privilege_type(&self) -> &str {
&self.privilege_type
}
}
Loading

0 comments on commit a2214bb

Please sign in to comment.