From 1ed441b023669f48a38d9e97054eebed1f61abc0 Mon Sep 17 00:00:00 2001 From: Paul Ruane Date: Sat, 12 Oct 2024 00:22:09 +0100 Subject: [PATCH] chore(sqlx): Refactor to use async Sqlx --- Cargo.lock | 1251 +++++++++++++---- Cargo.toml | 11 +- README.md | 11 +- db/docker-compose.yml | 6 +- src/cli/mod.rs | 16 +- src/compare/mod.rs | 624 +++++--- .../report/{table_column.rs => column.rs} | 8 +- src/compare/report/mod.rs | 2 +- src/compare/report/table.rs | 4 +- src/db/column.rs | 112 ++ src/db/column_privilege.rs | 57 + src/db/mod.rs | 561 +------- src/db/privilege.rs | 5 + src/db/routine.rs | 116 ++ src/db/routine_privilege.rs | 56 + src/db/schema.rs | 33 + src/db/sequence.rs | 47 + src/db/table.rs | 46 + src/db/table_constraint.rs | 48 + src/db/table_privilege.rs | 56 + src/db/table_trigger.rs | 55 + src/db/thing.rs | 101 -- src/db/view.rs | 40 + src/main.rs | 17 +- tests/identical-schemas-verbose/expected.txt | 488 +++++++ tests/main/expected.txt | 10 +- tests/main/right.sql | 3 +- 27 files changed, 2658 insertions(+), 1126 deletions(-) rename src/compare/report/{table_column.rs => column.rs} (64%) create mode 100644 src/db/column.rs create mode 100644 src/db/column_privilege.rs create mode 100644 src/db/privilege.rs create mode 100644 src/db/routine.rs create mode 100644 src/db/routine_privilege.rs create mode 100644 src/db/schema.rs create mode 100644 src/db/sequence.rs create mode 100644 src/db/table.rs create mode 100644 src/db/table_constraint.rs create mode 100644 src/db/table_privilege.rs create mode 100644 src/db/table_trigger.rs delete mode 100644 src/db/thing.rs create mode 100644 src/db/view.rs diff --git a/Cargo.lock b/Cargo.lock index 3b13838..6967dbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,24 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "anstream" version = "0.6.14" @@ -34,9 +52,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" @@ -67,21 +85,19 @@ dependencies = [ ] [[package]] -name = "async-trait" -version = "0.1.80" +name = "atoi" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" dependencies = [ - "proc-macro2", - "quote", - "syn", + "num-traits", ] [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" @@ -100,21 +116,24 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] -name = "bitflags" -version = "1.3.2" +name = "base64ct" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] [[package]] name = "block-buffer" @@ -125,12 +144,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - [[package]] name = "byteorder" version = "1.5.0" @@ -139,15 +152,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cc" -version = "1.0.98" +version = "1.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -157,9 +173,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.4" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -167,9 +183,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -179,9 +195,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", @@ -211,15 +227,60 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "crypto-common" version = "0.1.6" @@ -230,6 +291,17 @@ dependencies = [ "typenum", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + [[package]] name = "digest" version = "0.10.7" @@ -237,21 +309,110 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] [[package]] -name = "fallible-iterator" -version = "0.2.0" +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -259,15 +420,43 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + +[[package]] +name = "futures-io" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -276,26 +465,29 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -328,12 +520,58 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -343,6 +581,35 @@ dependencies = [ "digest", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown 0.15.0", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.0" @@ -350,19 +617,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" [[package]] -name = "js-sys" -version = "0.3.69" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ - "wasm-bindgen", + "either", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "lbdt" @@ -370,14 +646,42 @@ version = "0.0.0" dependencies = [ "clap", "colored", - "postgres", + "futures", + "futures-core", + "itertools", + "sqlformat", + "sqlx", + "tokio", ] [[package]] name = "libc" -version = "0.2.155" +version = "0.2.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" @@ -391,9 +695,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "md-5" @@ -411,6 +715,12 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.3" @@ -422,13 +732,71 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", ] [[package]] @@ -442,9 +810,18 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "parking" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" @@ -464,34 +841,31 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] -name = "percent-encoding" -version = "2.3.1" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] -name = "phf" -version = "0.11.2" +name = "pem-rfc7468" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ - "phf_shared", + "base64ct", ] [[package]] -name = "phf_shared" -version = "0.11.2" +name = "percent-encoding" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" @@ -506,53 +880,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "postgres" -version = "0.19.7" +name = "pkcs1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7915b33ed60abc46040cbcaa25ffa1c7ec240668e0477c4f3070786f5916d451" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "bytes", - "fallible-iterator", - "futures-util", - "log", - "tokio", - "tokio-postgres", + "der", + "pkcs8", + "spki", ] [[package]] -name = "postgres-protocol" -version = "0.6.6" +name = "pkcs8" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b6c5ef183cd3ab4ba005f1ca64c21e8bd97ce4699cfea9e8d9a2c4958ca520" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "base64", - "byteorder", - "bytes", - "fallible-iterator", - "hmac", - "md-5", - "memchr", - "rand", - "sha2", - "stringprep", + "der", + "spki", ] [[package]] -name = "postgres-types" -version = "0.2.6" +name = "pkg-config" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d2234cdee9408b523530a9b6d2d6b373d1db34f6a8e51dc03ded1828d7fb67c" -dependencies = [ - "bytes", - "fallible-iterator", - "postgres-protocol", -] +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro2" @@ -604,20 +971,31 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] -name = "redox_syscall" -version = "0.5.1" +name = "rsa" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" dependencies = [ - "bitflags 2.5.0", + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", ] [[package]] @@ -626,12 +1004,86 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.8" @@ -644,10 +1096,29 @@ dependencies = [ ] [[package]] -name = "siphasher" -version = "0.3.11" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] [[package]] name = "slab" @@ -663,6 +1134,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -674,6 +1148,229 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" +dependencies = [ + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e" +dependencies = [ + "atoi", + "byteorder", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.14.5", + "hashlink", + "hex", + "indexmap", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5" +dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" +dependencies = [ + "atoi", + "base64", + "bitflags", + "byteorder", + "bytes", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" +dependencies = [ + "atoi", + "base64", + "bitflags", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "tracing", + "url", +] + [[package]] name = "stringprep" version = "0.1.5" @@ -693,9 +1390,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -708,11 +1405,44 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -725,58 +1455,75 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", "mio", "pin-project-lite", + "signal-hook-registry", "socket2", - "windows-sys 0.48.0", + "tokio-macros", + "windows-sys 0.52.0", ] [[package]] -name = "tokio-postgres" -version = "0.7.10" +name = "tokio-macros" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d340244b32d920260ae7448cb72b6e238bddc3d4f7603394e7dd46ed8e48f5b8" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ - "async-trait", - "byteorder", - "bytes", - "fallible-iterator", - "futures-channel", - "futures-util", - "log", - "parking_lot", - "percent-encoding", - "phf", - "pin-project-lite", - "postgres-protocol", - "postgres-types", - "rand", - "socket2", - "tokio", - "tokio-util", - "whoami", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "tokio-util" -version = "0.7.11" +name = "tokio-stream" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" dependencies = [ - "bytes", "futures-core", - "futures-sink", "pin-project-lite", "tokio", ] +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + [[package]] name = "typenum" version = "1.17.0" @@ -785,9 +1532,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" @@ -797,116 +1544,74 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-properties" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] -name = "wasite" -version = "0.1.0" +name = "unicode_categories" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] -name = "wasm-bindgen" -version = "0.2.92" +name = "url" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ - "cfg-if", - "wasm-bindgen-macro", + "form_urlencoded", + "idna", + "percent-encoding", ] [[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" +name = "utf8parse" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" +name = "vcpkg" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" +name = "version_check" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "web-sys" -version = "0.3.69" +name = "wasite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "whoami" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ - "redox_syscall 0.4.1", + "redox_syscall", "wasite", - "web-sys", ] [[package]] @@ -924,7 +1629,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -944,18 +1658,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -966,9 +1680,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -978,9 +1692,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -990,15 +1704,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -1008,9 +1722,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -1020,9 +1734,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -1032,9 +1746,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -1044,6 +1758,33 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 7973c3f..144c251 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" \ No newline at end of file diff --git a/README.md b/README.md index 26ef5eb..87770c0 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/db/docker-compose.yml b/db/docker-compose.yml index 3c3f185..414eb2c 100644 --- a/db/docker-compose.yml +++ b/db/docker-compose.yml @@ -1,8 +1,6 @@ -version: '2' - services: left: - image: postgres + image: postgres:latest container_name: postgres-left environment: - POSTGRES_PASSWORD=postgres @@ -10,7 +8,7 @@ services: - 8901:5432 restart: unless-stopped right: - image: postgres + image: postgres:latest container_name: postgres-right environment: - POSTGRES_PASSWORD=postgres diff --git a/src/cli/mod.rs b/src/cli/mod.rs index f2eca4e..dc9f82e 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -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}; @@ -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}; @@ -51,9 +51,9 @@ impl CLI { CLI { args } } - pub fn run(&self) -> Result { - 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 { + 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, @@ -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); @@ -286,7 +286,7 @@ impl CLI { differences } - fn render_table_column_report(&self, report: &Report) -> i32 { + fn render_table_column_report(&self, report: &Report) -> i32 { let mut differences = 0; for column in &report.entries { diff --git a/src/compare/mod.rs b/src/compare/mod.rs index 0fc765b..729d7d4 100644 --- a/src/compare/mod.rs +++ b/src/compare/mod.rs @@ -1,21 +1,23 @@ use std::collections::HashMap; use std::fmt::Display; -use postgres::Error; +use itertools::Itertools; +use sqlx::Error; + use crate::compare::report::privilege::PrivilegeComparison; use crate::compare::report::privilege::PrivilegeComparison::{PrivilegeAdded, PrivilegeMaintained, PrivilegeRemoved}; use crate::compare::report::property::PropertyComparison; use crate::compare::report::property::PropertyComparison::{PropertyChanged, PropertyUnchanged}; use crate::compare::report::Report; -use crate::compare::report::routine::RoutineComparison; -use crate::compare::report::routine::RoutineComparison::{RoutineAdded, RoutineMaintained, RoutineRemoved}; use crate::compare::report::schema::SchemaComparison; use crate::compare::report::schema::SchemaComparison::{SchemaAdded, SchemaMaintained, SchemaMissing, SchemaRemoved}; 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::routine::RoutineComparison; +use crate::compare::report::routine::RoutineComparison::{RoutineAdded, RoutineMaintained, RoutineRemoved}; use crate::compare::report::table_constraint::TableConstraintComparison; use crate::compare::report::table_constraint::TableConstraintComparison::{ConstraintAdded, ConstraintMaintained, ConstraintRemoved}; use crate::compare::report::table_trigger::TableTriggerComparison; @@ -23,7 +25,18 @@ use crate::compare::report::table_trigger::TableTriggerComparison::{TriggerAdded use crate::compare::report::view::ViewComparison; use crate::compare::report::view::ViewComparison::ViewMaintained; use crate::db::Database; -use crate::db::thing::{Column, Constraint, Privilege, Routine, Schema, Sequence, Table, Trigger, View}; +use crate::db::schema::Schema; +use crate::db::table::Table; +use crate::db::column::Column; +use crate::db::column_privilege::ColumnPrivilege; +use crate::db::privilege::Privilege; +use crate::db::routine::Routine; +use crate::db::routine_privilege::RoutinePrivilege; +use crate::db::table_constraint::TableConstraint; +use crate::db::table_privilege::TablePrivilege; +use crate::db::table_trigger::TableTrigger; +use crate::db::sequence::Sequence; +use crate::db::view::View; use crate::string::EqualIgnoreWhitespace; pub mod report; @@ -47,12 +60,35 @@ impl Comparer { } } - pub fn compare(&mut self, schemas: Vec) -> Result, Error> { + pub async fn compare(&mut self, schemas: Vec) -> Result, Error> { let mut entries = Vec::new(); + let left_schemas = self.left_db.schemas(&schemas[..]).await?; + let right_schemas = self.right_db.schemas(&schemas[..]).await?; + let left_routines = self.left_db.routines(&schemas[..]).await?; + let right_routines = self.right_db.routines(&schemas[..]).await?; + let left_routine_privileges = self.left_db.routine_privileges(&schemas[..]).await?; + let right_routine_privileges = self.right_db.routine_privileges(&schemas[..]).await?; + let left_sequences = self.left_db.sequences(&schemas[..]).await?; + let right_sequences = self.right_db.sequences(&schemas[..]).await?; + let left_tables = self.left_db.tables(&schemas[..]).await?; + let right_tables = self.right_db.tables(&schemas[..]).await?; + let left_columns = self.left_db.columns(&schemas[..]).await?; + let right_columns = self.right_db.columns(&schemas[..]).await?; + let left_table_constraints = self.left_db.table_constraints(&schemas[..]).await?; + let right_table_constraints = self.right_db.table_constraints(&schemas[..]).await?; + let left_table_privileges = self.left_db.table_privileges(&schemas[..]).await?; + let right_table_privileges = self.right_db.table_privileges(&schemas[..]).await?; + let left_table_triggers = self.left_db.table_triggers(&schemas[..]).await?; + let right_table_triggers = self.right_db.table_triggers(&schemas[..]).await?; + let left_column_privileges = self.left_db.column_privileges(&schemas[..]).await?; + let right_column_privileges = self.right_db.column_privileges(&schemas[..]).await?; + let left_views = self.left_db.views(&schemas[..]).await?; + let right_views = self.right_db.views(&schemas[..]).await?; + for schema in schemas { - let left_schema = self.left_db.schema(&schema)?; - let right_schema = self.right_db.schema(&schema)?; + let left_schema = left_schemas.iter().filter(|s| s.schema_name == schema).at_most_one().map_err(|_| Error::RowNotFound )?; + let right_schema = right_schemas.iter().filter(|s| s.schema_name == schema).at_most_one().map_err(|_| Error::RowNotFound)?; if left_schema.is_none() && right_schema.is_none() { entries.push(SchemaMissing { schema_name: String::from(schema) }); @@ -64,10 +100,46 @@ impl Comparer { } else { let properties = self.compare_schema_properties(&left_schema.unwrap(), &right_schema.unwrap()); - let routines = self.compare_routines(&schema)?; - let sequences = self.compare_sequences(&schema)?; - let tables = self.compare_tables(&schema)?; - let views = self.compare_views(&schema)?; + let left_schema_routines = left_routines.iter().filter(|r| r.routine_schema == schema).collect(); + let right_schema_routines = right_routines.iter().filter(|r| r.routine_schema == schema).collect(); + let left_schema_routine_privileges = left_routine_privileges.iter().filter(|p| p.routine_schema == schema).collect(); + let right_schema_routine_privileges = right_routine_privileges.iter().filter(|p| p.routine_schema == schema).collect(); + let routines = self.compare_routines(left_schema_routines, right_schema_routines, left_schema_routine_privileges, right_schema_routine_privileges)?; + + let left_schema_sequences = left_sequences.iter().filter(|s| s.sequence_schema == schema).collect(); + let right_schema_sequences = right_sequences.iter().filter(|s| s.sequence_schema == schema).collect(); + let sequences = self.compare_sequences(left_schema_sequences, right_schema_sequences)?; + + let left_schema_tables = left_tables.iter().filter(|t| t.table_schema == schema).collect(); + let right_schema_tables = right_tables.iter().filter(|t| t.table_schema == schema).collect(); + let left_schema_columns = left_columns.iter().filter(|c| c.table_schema == schema).collect(); + let right_schema_columns = right_columns.iter().filter(|c| c.table_schema == schema).collect(); + let left_schema_table_privileges = left_table_privileges.iter().filter(|p| p.table_schema == schema).collect(); + let right_schema_table_privileges = right_table_privileges.iter().filter(|p| p.table_schema == schema).collect(); + let left_schema_table_constraints = left_table_constraints.iter().filter(|c| c.table_schema == schema).collect(); + let right_schema_table_constraints = right_table_constraints.iter().filter(|c| c.table_schema == schema).collect(); + let left_schema_table_triggers = left_table_triggers.iter().filter(|t| t.event_object_schema == schema).collect(); + let right_schema_table_triggers = right_table_triggers.iter().filter(|t| t.event_object_schema == schema).collect(); + let left_schema_column_privileges = left_column_privileges.iter().filter(|p| p.table_schema == schema).collect(); + let right_schema_column_privileges = right_column_privileges.iter().filter(|p| p.table_schema == schema).collect(); + let tables = self.compare_tables( + left_schema_tables, + right_schema_tables, + left_schema_columns, + right_schema_columns, + left_schema_column_privileges, + right_schema_column_privileges, + left_schema_table_privileges, + right_schema_table_privileges, + left_schema_table_constraints, + right_schema_table_constraints, + left_schema_table_triggers, + right_schema_table_triggers, + )?; + + let left_schema_views = left_views.iter().filter(|t| t.table_schema == schema).collect(); + let right_schema_views = right_views.iter().filter(|t| t.table_schema == schema).collect(); + let views = self.compare_views(left_schema_views, right_schema_views)?; entries.push(SchemaMaintained { schema_name: String::from(schema), properties, routines, sequences, tables, views }); } @@ -77,410 +149,499 @@ impl Comparer { Ok(Report { entries }) } - fn compare_schema_properties(&self, left_schema: &Schema, right_schema: &Schema) -> Report { + fn compare_schema_properties(&self, left: &Schema, right: &Schema) -> Report { Report { entries: vec![ - self.compare_property("schema_owner", &left_schema.schema_owner, &right_schema.schema_owner) + self.compare_property("schema_owner", left, right, |s| &s.schema_owner), + self.compare_option_property("default_character_set_catalog", left, right, |s| &s.default_character_set_catalog), + self.compare_option_property("default_character_set_schema", left, right, |s| &s.default_character_set_schema), + self.compare_option_property("default_character_set_name", left, right, |s| &s.default_character_set_name), ] } } - fn compare_routines(&mut self, schema_name: &str) -> Result, Error> { - let left_routines = self.left_db.routines(schema_name)?; - let right_routines = self.right_db.routines(schema_name)?; - - let left_routine_privileges_by_signature = self.left_db.routine_privileges(schema_name)?; - let right_routine_privileges_by_signature = self.right_db.routine_privileges(schema_name)?; - - let mut right_routines_map: HashMap = right_routines.into_iter().map(|t| (t.signature.clone(), t)).collect(); + fn compare_routines(&mut self, + left_routines: Vec<&Routine>, + right_routines: Vec<&Routine>, + left_routine_privileges: Vec<&RoutinePrivilege>, + right_routine_privileges: Vec<&RoutinePrivilege>, + ) -> Result, Error> { + let mut right_routines_map: HashMap = right_routines.into_iter().map(|t| (t.signature.clone(), t)).collect(); let mut entries = Vec::new(); - + for left_routine in left_routines { let right_routine = right_routines_map.get(&left_routine.signature); - + match right_routine { None => { - entries.push(RoutineRemoved { routine_signature: left_routine.signature }); + entries.push(RoutineRemoved { routine_signature: left_routine.signature.clone() }); }, Some(rr) => { - let no_privileges = Vec::new(); - let left_routine_privileges = left_routine_privileges_by_signature.get(&left_routine.signature).unwrap_or(&no_privileges); - let right_routine_privileges = right_routine_privileges_by_signature.get(&left_routine.signature).unwrap_or(&no_privileges); - let properties = self.compare_routine_properties(&left_routine, rr); - let privileges = self.compare_routine_privileges(left_routine_privileges, right_routine_privileges)?; - - entries.push(RoutineMaintained { routine_signature: left_routine.signature, properties, privileges }); - + + let left_routine_routine_privileges: Vec<&RoutinePrivilege> = left_routine_privileges.iter().filter(|p| p.signature == left_routine.signature).cloned().collect(); + let right_routine_routine_privileges: Vec<&RoutinePrivilege> = right_routine_privileges.iter().filter(|p| p.signature == rr.signature).cloned().collect(); + let privileges = self.compare_routine_privileges(left_routine_routine_privileges, right_routine_routine_privileges)?; + + entries.push(RoutineMaintained { routine_signature: left_routine.signature.clone(), properties, privileges }); + right_routines_map.remove(&rr.signature.clone()); } } } - + if right_routines_map.len() > 0 { - let mut added_routines: Vec<&Routine> = right_routines_map.values().collect(); + let mut added_routines: Vec<&&Routine> = right_routines_map.values().collect(); added_routines.sort_unstable_by_key(|r| &r.signature); - + for right_routine in added_routines { entries.push(RoutineAdded { routine_signature: right_routine.signature.clone() }); } } - + Ok(Report { entries }) } fn compare_routine_properties(&self, left: &Routine, right: &Routine) -> Report { Report { entries: vec![ - self.compare_option_property("routine_type", &left.routine_type, &right.routine_type), - self.compare_option_property("data_type", &left.data_type, &right.data_type), - self.compare_option_property("type_udt_name", &left.type_udt_name, &right.type_udt_name), - self.compare_property("routine_body", &left.routine_body, &right.routine_body), + self.compare_option_property("routine_type", left, right, |p| &p.routine_type), + self.compare_option_property("module_catalog", left, right, |p| &p.module_catalog), + self.compare_option_property("module_schema", left, right, |p| &p.module_schema), + self.compare_option_property("module_name", left, right, |p| &p.module_name), + self.compare_option_property("udt_catalog", left, right, |p| &p.udt_catalog), + self.compare_option_property("udt_schema", left, right, |p| &p.udt_schema), + self.compare_option_property("udt_name", left, right, |p| &p.udt_name), + self.compare_option_property("data_type", left, right, |p| &p.data_type), + self.compare_option_property("character_maximum_length", left, right, |p| &p.character_maximum_length), + self.compare_option_property("character_octet_length", left, right, |p| &p.character_octet_length), + self.compare_option_property("character_set_catalog", left, right, |p| &p.character_set_catalog), + self.compare_option_property("character_set_schema", left, right, |p| &p.character_set_schema), + self.compare_option_property("character_set_name", left, right, |p| &p.character_set_name), + self.compare_option_property("collation_catalog", left, right, |p| &p.collation_catalog), + self.compare_option_property("collation_schema", left, right, |p| &p.collation_schema), + self.compare_option_property("collation_name", left, right, |p| &p.collation_name), + self.compare_option_property("numeric_precision", left, right, |p| &p.numeric_precision), + self.compare_option_property("numeric_precision_radix", left, right, |p| &p.numeric_precision_radix), + self.compare_option_property("numeric_scale", left, right, |p| &p.numeric_scale), + self.compare_option_property("datetime_precision", left, right, |p| &p.datetime_precision), + self.compare_option_property("interval_type", left, right, |p| &p.interval_type), + self.compare_option_property("interval_precision", left, right, |p| &p.interval_precision), + self.compare_option_property("type_udt_catalog", left, right, |p| &p.type_udt_catalog), + self.compare_option_property("type_udt_schema", left, right, |p| &p.type_udt_schema), + self.compare_option_property("type_udt_name", left, right, |p| &p.type_udt_name), + self.compare_option_property("maximum_cardinality", left, right, |p| &p.maximum_cardinality), + self.compare_option_property("dtd_identifier", left, right, |p| &p.dtd_identifier), + self.compare_property("routine_body", left, right, |p| &p.routine_body), if self.ignore_whitespace { - self.compare_option_property_ignore_whitespace("routine_definition", &left.routine_definition, &right.routine_definition) + self.compare_option_property_ignore_whitespace("routine_definition", left, right, |p| &p.routine_definition) } else { - self.compare_option_property("routine_definition", &left.routine_definition, &right.routine_definition) + self.compare_option_property("routine_definition", left, right, |p| &p.routine_definition) }, - self.compare_option_property("external_name", &left.external_name, &right.external_name), - self.compare_property("external_language", &left.external_language, &right.external_language), - self.compare_property("is_deterministic", &left.is_deterministic, &right.is_deterministic), - self.compare_option_property("is_null_call", &left.is_null_call, &right.is_null_call), - self.compare_property("security_type", &left.security_type, &right.security_type), + self.compare_option_property("external_name", left, right, |p| &p.external_name), + self.compare_property("external_language", left, right, |p| &p.external_language), + self.compare_property("parameter_style", left, right, |p| &p.parameter_style), + self.compare_property("is_deterministic", left, right, |p| &p.is_deterministic), + self.compare_property("sql_data_access", left, right, |p| &p.sql_data_access), + self.compare_option_property("is_null_call", left, right, |p| &p.is_null_call), + self.compare_option_property("sql_path", left, right, |p| &p.sql_path), + self.compare_property("schema_level_routine", left, right, |p| &p.schema_level_routine), + self.compare_option_property("max_dynamic_result_sets", left, right, |p| &p.max_dynamic_result_sets), + self.compare_option_property("is_user_defined_cast", left, right, |p| &p.is_user_defined_cast), + self.compare_option_property("is_implicitly_invocable", left, right, |p| &p.is_implicitly_invocable), + self.compare_property("security_type", left, right, |p| &p.security_type), ] } } - fn compare_routine_privileges(&mut self, left_routine_privileges: &Vec, right_routine_privileges: &Vec) -> Result, Error> { + fn compare_routine_privileges(&mut self, left_routine_privileges: Vec<&RoutinePrivilege>, right_routine_privileges: Vec<&RoutinePrivilege>) -> Result, Error> { if self.ignore_privileges { return Ok(Report { entries: vec![] }) } - - Ok(self.compare_privileges(left_routine_privileges, right_routine_privileges)) + + Ok(self.compare_privileges(&left_routine_privileges, &right_routine_privileges)) } - fn compare_privileges(&self, left_privileges: &Vec, right_privileges: &Vec) -> Report { - let mut right_privileges_map: HashMap<(String, String, String), &Privilege> = right_privileges.into_iter().map(|c| ((c.privilege_type.clone(), c.grantor.clone(), c.grantee.clone()), c)).collect(); + fn compare_privileges

(&self, left_privileges: &Vec

, right_privileges: &Vec

) -> Report + where P : Privilege + { + let mut right_privileges_map: HashMap<(&str, &str, &str), &P> = right_privileges.into_iter().map(|c| ((c.privilege_type(), c.grantor(), c.grantee()), c)).collect(); let mut entries = Vec::new(); - + for left_privilege in left_privileges { - let key = &(left_privilege.privilege_type.clone(), left_privilege.grantor.clone(), left_privilege.grantee.clone()); + let key = &(left_privilege.privilege_type(), left_privilege.grantor(), left_privilege.grantee()); let right_privilege = right_privileges_map.get_mut(&key); - + match right_privilege { - None => entries.push(PrivilegeRemoved { privilege_name: left_privilege.privilege_type.clone(), grantor: left_privilege.grantor.clone(), grantee: left_privilege.grantee.clone() }), + None => entries.push(PrivilegeRemoved { privilege_name: left_privilege.privilege_type().to_string(), grantor: left_privilege.grantor().to_string(), grantee: left_privilege.grantee().to_string() }), Some(..) => { - entries.push(PrivilegeMaintained { privilege_name: left_privilege.privilege_type.clone(), grantor: left_privilege.grantor.clone(), grantee: left_privilege.grantee.clone() }); - + entries.push(PrivilegeMaintained { privilege_name: left_privilege.privilege_type().to_string(), grantor: left_privilege.grantor().to_string(), grantee: left_privilege.grantee().to_string() }); + _ = right_privileges_map.remove(&key); }, } } - + if right_privileges_map.len() > 0 { - let mut added_privileges: Vec<&&Privilege> = right_privileges_map.values().collect(); - added_privileges.sort_unstable_by_key(|rp| (&rp.privilege_type, &rp.grantor, &rp.grantee)); - + let mut added_privileges: Vec<&&P> = right_privileges_map.values().collect(); + added_privileges.sort_unstable_by_key(|p| (p.privilege_type(), p.grantor(), p.grantee())); + for added_privilege in added_privileges { - entries.push(PrivilegeAdded { privilege_name: added_privilege.privilege_type.clone(), grantor: added_privilege.grantor.clone(), grantee: added_privilege.grantee.clone() }); + entries.push(PrivilegeAdded { privilege_name: added_privilege.privilege_type().to_string(), grantor: added_privilege.grantor().to_string(), grantee: added_privilege.grantee().to_string() }); } } - + Report { entries } } - fn compare_sequences(&mut self, schema_name: &str) -> Result, Error> { - let left_sequences = self.left_db.sequences(schema_name)?; - let right_sequences = self.right_db.sequences(schema_name)?; - - let mut right_sequences_map: HashMap = right_sequences.into_iter().map(|t| (t.sequence_name.clone(), t)).collect(); + fn compare_sequences(&mut self, left_sequences: Vec<&Sequence>, right_sequences: Vec<&Sequence>) -> Result, Error> { + let mut right_sequences_map: HashMap = right_sequences.into_iter().map(|t| (t.sequence_name.clone(), t)).collect(); let mut entries = Vec::new(); - + for left_sequence in left_sequences { let right_sequence = right_sequences_map.get(&left_sequence.sequence_name); - + match right_sequence { None => { - entries.push(SequenceRemoved { sequence_name: left_sequence.sequence_name }); + entries.push(SequenceRemoved { sequence_name: left_sequence.sequence_name.clone() }); }, Some(rs) => { let properties = self.compare_sequence_properties(&left_sequence, rs); - - entries.push(SequenceMaintained { sequence_name: left_sequence.sequence_name, properties }); - + + entries.push(SequenceMaintained { sequence_name: left_sequence.sequence_name.clone(), properties }); + right_sequences_map.remove(&rs.sequence_name.clone()); } } } - + if right_sequences_map.len() > 0 { - let mut added_sequences: Vec<&Sequence> = right_sequences_map.values().collect(); + let mut added_sequences: Vec<&&Sequence> = right_sequences_map.values().collect(); added_sequences.sort_unstable_by_key(|s| &s.sequence_name); - + for right_sequence in added_sequences { entries.push(SequenceAdded { sequence_name: right_sequence.sequence_name.clone() }); } } - + Ok(Report { entries }) } fn compare_sequence_properties(&self, left: &Sequence, right: &Sequence) -> Report { Report { entries: vec![ - self.compare_property("data_type", &left.data_type, &right.data_type), - self.compare_property("numeric_precision", &left.numeric_precision, &right.numeric_precision), - self.compare_property("numeric_precision_radix", &left.numeric_precision_radix, &right.numeric_precision_radix), - self.compare_property("numeric_scale", &left.numeric_scale, &right.numeric_scale), - self.compare_property("start_value", &left.start_value, &right.start_value), - self.compare_property("minimum_value", &left.minimum_value, &right.minimum_value), - self.compare_property("maximum_value", &left.maximum_value, &right.maximum_value), - self.compare_property("increment", &left.increment, &right.increment), - self.compare_property("cycle_option", &left.cycle_option, &right.cycle_option), + self.compare_property("sequence_catalog", left, right, |p| &p.sequence_catalog), + self.compare_property("sequence_schema", left, right, |p| &p.sequence_schema), + self.compare_property("sequence_name", left, right, |p| &p.sequence_name), + self.compare_property("data_type", left, right, |c| &c.data_type), + self.compare_property("numeric_precision", left, right, |c| &c.numeric_precision), + self.compare_property("numeric_precision_radix", left, right, |c| &c.numeric_precision_radix), + self.compare_property("numeric_scale", left, right, |c| &c.numeric_scale), + self.compare_property("start_value", left, right, |c| &c.start_value), + self.compare_property("minimum_value", left, right, |c| &c.minimum_value), + self.compare_property("maximum_value", left, right, |c| &c.maximum_value), + self.compare_property("increment", left, right, |c| &c.increment), + self.compare_property("cycle_option", left, right, |c| &c.cycle_option), ] } } - fn compare_tables(&mut self, schema_name: &str) -> Result, Error> { - let left_tables = self.left_db.tables(schema_name)?; - let right_tables = self.right_db.tables(schema_name)?; - - let mut right_tables_map: HashMap = right_tables.into_iter().map(|t| (t.table_name.clone(), t)).collect(); + fn compare_tables(&mut self, + left_tables: Vec<&Table>, + right_tables: Vec<&Table>, + left_columns: Vec<&Column>, + right_columns: Vec<&Column>, + left_column_privileges: Vec<&ColumnPrivilege>, + right_column_privileges: Vec<&ColumnPrivilege>, + left_table_privileges : Vec<&TablePrivilege>, + right_table_privileges : Vec<&TablePrivilege>, + left_table_constraints : Vec<&TableConstraint>, + right_table_constraints : Vec<&TableConstraint>, + left_table_triggers : Vec<&TableTrigger>, + right_table_triggers : Vec<&TableTrigger>, + ) -> Result, Error> { let mut entries = Vec::new(); + let mut right_tables_map: HashMap = right_tables.into_iter().map(|t| (t.table_name.clone(), t)).collect(); for left_table in left_tables { let right_table = right_tables_map.get(&left_table.table_name); - + match right_table { None => { - entries.push(TableRemoved { table_name: left_table.table_name }); + entries.push(TableRemoved { table_name: left_table.table_name.clone() }); }, Some(rt) => { let properties = self.compare_table_properties(&left_table, rt); - let columns = self.compare_table_columns(schema_name, &rt.table_name)?; - let privileges = self.compare_table_privileges(schema_name, &rt.table_name)?; - let constraints = self.compare_table_constraints(schema_name, &rt.table_name)?; - let triggers = self.compare_table_triggers(schema_name, &rt.table_name)?; - - entries.push(TableMaintained { table_name: left_table.table_name, properties, columns, privileges, constraints, triggers }); + let left_table_columns : Vec<&Column> = left_columns.iter().filter(|t| t.table_name == left_table.table_name).cloned().collect(); + let right_table_columns : Vec<&Column> = right_columns.iter().filter(|t| t.table_name == rt.table_name).cloned().collect(); + let left_table_column_privileges : Vec<&ColumnPrivilege> = left_column_privileges.iter().filter(|p| p.table_name == left_table.table_name).cloned().collect(); + let right_table_column_privileges : Vec<&ColumnPrivilege> = right_column_privileges.iter().filter(|p| p.table_name == rt.table_name).cloned().collect(); + let left_table_table_privileges: Vec<&TablePrivilege> = left_table_privileges.iter().filter(|p| p.table_name == left_table.table_name).cloned().collect(); + let right_table_table_privileges: Vec<&TablePrivilege> = right_table_privileges.iter().filter(|p| p.table_name == left_table.table_name).cloned().collect(); + let left_table_table_constraints: Vec<&TableConstraint> = left_table_constraints.iter().filter(|c| c.table_name == left_table.table_name).cloned().collect(); + let right_table_table_constraints: Vec<&TableConstraint> = right_table_constraints.iter().filter(|c| c.table_name == left_table.table_name).cloned().collect(); + let left_table_table_triggers : Vec<&TableTrigger> = left_table_triggers.iter().filter(|t| t.event_object_table == left_table.table_name).cloned().collect(); + let right_table_table_triggers : Vec<&TableTrigger> = right_table_triggers.iter().filter(|t| t.event_object_table == rt.table_name).cloned().collect(); + + let columns = self.compare_table_columns(left_table_columns, right_table_columns, left_table_column_privileges, right_table_column_privileges)?; + let privileges = self.compare_table_privileges(left_table_table_privileges, right_table_table_privileges)?; + let constraints = self.compare_table_constraints(left_table_table_constraints, right_table_table_constraints)?; + let triggers = self.compare_table_triggers(left_table_table_triggers, right_table_table_triggers)?; + + entries.push(TableMaintained { table_name: left_table.table_name.clone(), properties, columns, privileges, constraints, triggers }); + right_tables_map.remove(&rt.table_name.clone()); }, } } - + if right_tables_map.len() > 0 { - let mut added_tables: Vec<&Table> = right_tables_map.values().collect(); + let mut added_tables: Vec<&&Table> = right_tables_map.values().collect(); added_tables.sort_unstable_by_key(|t| &t.table_name); - + for right_table in added_tables { entries.push(TableAdded { table_name: right_table.table_name.clone() }); } } - + Ok(Report { entries }) } fn compare_table_properties(&self, left: &Table, right: &Table) -> Report { Report { entries: vec![ - self.compare_property("table_type", &left.table_type, &right.table_type), - self.compare_property("is_insertable_into", &left.is_insertable_into, &right.is_insertable_into), + self.compare_property("table_catalog", left, right, |c| &c.table_catalog), + self.compare_property("table_schema", left, right, |c| &c.table_schema), + self.compare_property("table_name", left, right, |c| &c.table_name), + self.compare_property("table_type", left, right, |c| &c.table_type), + self.compare_option_property("self_referencing_column_name", left, right, |t| &t.self_referencing_column_name), + self.compare_option_property("reference_generation", left, right, |c| &c.reference_generation), + self.compare_option_property("user_defined_type_catalog", left, right, |c| &c.user_defined_type_catalog), + self.compare_option_property("user_defined_type_schema", left, right, |c| &c.user_defined_type_schema), + self.compare_option_property("user_defined_type_name", left, right, |c| &c.user_defined_type_name), + self.compare_property("is_insertable_into", left, right, |c| &c.is_insertable_into), + self.compare_property("is_typed", left, right, |c| &c.is_typed), + self.compare_option_property("commit_action", left, right, |c| &c.commit_action), ] } } - fn compare_table_privileges(&mut self, schema_name: &str, table_name: &str) -> Result, Error> { + fn compare_table_privileges(&mut self, left_table_privileges: Vec<&TablePrivilege>, right_table_privileges: Vec<&TablePrivilege>) -> Result, Error> { if self.ignore_privileges { return Ok(Report { entries: vec![] }) } - - let left_table_privileges = self.left_db.table_privileges(schema_name, table_name)?; - let right_table_privileges = self.right_db.table_privileges(schema_name, table_name)?; - + Ok(self.compare_privileges(&left_table_privileges, &right_table_privileges)) } - fn compare_table_columns(&mut self, schema_name: &str, table_name: &str) -> Result, Error> { - let left_columns = self.left_db.columns(schema_name, table_name)?; - let right_columns = self.right_db.columns(schema_name, table_name)?; - - let mut right_columns_map : HashMap = right_columns.into_iter().map(|c| (c.column_name.clone(), c)).collect(); + fn compare_table_columns(&mut self, + left_columns: Vec<&Column>, + right_columns: Vec<&Column>, + left_column_privileges: Vec<&ColumnPrivilege>, + right_column_privileges: Vec<&ColumnPrivilege>, + ) -> Result, Error> { let mut entries = Vec::new(); - + let mut right_columns_map : HashMap = right_columns.into_iter().map(|c| (c.column_name.clone(), c)).collect(); + for left_column in left_columns { let key = left_column.column_name.clone(); let right_column = right_columns_map.get_mut(&key); - + match right_column { None => { entries.push(ColumnRemoved { column_name: left_column.column_name.clone() }); }, Some(rc) => { let properties = self.compare_table_column_properties(&left_column, rc); - let privileges = self.compare_table_column_privileges(schema_name, table_name, &rc.column_name)?; - + + let left_column_privileges = left_column_privileges.iter().filter(|p| p.column_name == left_column.column_name).cloned().collect(); + let right_column_privileges = right_column_privileges.iter().filter(|p| p.column_name == rc.column_name).cloned().collect(); + let privileges = self.compare_table_column_privileges(left_column_privileges, right_column_privileges)?; + entries.push(ColumnMaintained { column_name: rc.column_name.clone(), properties, privileges }); - + right_columns_map.remove(&key); }, } } - + if right_columns_map.len() > 0 { - let mut added_columns : Vec<&Column> = right_columns_map.values().collect(); + let mut added_columns : Vec<&&Column> = right_columns_map.values().collect(); added_columns.sort_unstable_by_key(|c| &c.column_name); - + for right_column in added_columns { entries.push(ColumnAdded { column_name: right_column.column_name.clone() }); } } - + Ok(Report { entries }) } - + fn compare_table_column_properties(&self, left: &Column, right: &Column) -> Report { let mut properties = vec![ - self.compare_option_property("column_default", &left.column_default, &right.column_default), - self.compare_property("is_nullable", &left.is_nullable, &right.is_nullable), - self.compare_property("data_type", &left.data_type, &right.data_type), - self.compare_option_property("character_maximum_length", &left.character_maximum_length, &right.character_maximum_length), - self.compare_option_property("numeric_precision", &left.numeric_precision, &right.numeric_precision), - self.compare_option_property("numeric_scale", &left.numeric_scale, &right.numeric_scale), - self.compare_option_property("datetime_precision", &left.datetime_precision, &right.datetime_precision), - self.compare_property("is_identity", &left.is_identity, &right.is_identity), - self.compare_option_property("identity_generation", &left.identity_generation, &right.identity_generation), - self.compare_property("is_generated", &left.is_generated, &right.is_generated), - self.compare_option_property("generation_expression", &left.generation_expression, &right.generation_expression), - self.compare_property("is_updatable", &left.is_updatable, &right.is_updatable), + self.compare_option_property("column_default", left, right, |c| &c.column_default), + self.compare_property("is_nullable", left, right, |c| &c.is_nullable), + self.compare_property("data_type", left, right, |c| &c.data_type), + self.compare_option_property("character_maximum_length", left, right, |c| &c.character_maximum_length), + self.compare_option_property("character_octet_length", left, right, |c| &c.character_octet_length), + self.compare_option_property("numeric_precision", left, right, |c| &c.numeric_precision), + self.compare_option_property("numeric_precision_radix", left, right, |c| &c.numeric_precision_radix), + self.compare_option_property("numeric_scale", left, right, |c| &c.numeric_scale), + self.compare_option_property("datetime_precision", left, right, |c| &c.datetime_precision), + self.compare_option_property("interval_type", left, right, |c| &c.interval_type), + self.compare_option_property("interval_precision", left, right, |c| &c.interval_precision), + self.compare_option_property("character_set_catalog", left, right, |c| &c.character_set_catalog), + self.compare_option_property("character_set_schema", left, right, |c| &c.character_set_schema), + self.compare_option_property("character_set_name", left, right, |c| &c.character_set_name), + self.compare_option_property("collation_catalog", left, right, |c| &c.collation_catalog), + self.compare_option_property("collation_schema", left, right, |c| &c.collation_schema), + self.compare_option_property("collation_name", left, right, |c| &c.collation_name), + self.compare_option_property("domain_catalog", left, right, |c| &c.domain_catalog), + self.compare_option_property("domain_schema", left, right, |c| &c.domain_schema), + self.compare_option_property("domain_name", left, right, |c| &c.domain_name), + self.compare_option_property("udt_catalog", left, right, |c| &c.udt_catalog), + self.compare_option_property("udt_schema", left, right, |c| &c.udt_schema), + self.compare_option_property("udt_name", left, right, |c| &c.udt_name), + self.compare_option_property("scope_catalog", left, right, |c| &c.scope_catalog), + self.compare_option_property("scope_schema", left, right, |c| &c.scope_schema), + self.compare_option_property("scope_name", left, right, |c| &c.scope_name), + self.compare_option_property("maximum_cardinality", left, right, |c| &c.maximum_cardinality), + self.compare_option_property("dtd_identifier", left, right, |c| &c.character_set_name), + self.compare_property("is_self_referencing", left, right, |c| &c.is_self_referencing), + self.compare_property("is_identity", left, right, |c| &c.is_identity), + self.compare_option_property("identity_generation", left, right, |c| &c.identity_generation), + self.compare_option_property("identity_start", left, right, |c| &c.identity_start), + self.compare_option_property("identity_increment", left, right, |c| &c.identity_increment), + self.compare_option_property("identity_maximum", left, right, |c| &c.identity_maximum), + self.compare_option_property("identity_minimum", left, right, |c| &c.identity_minimum), + self.compare_option_property("identity_cycle", left, right, |c| &c.identity_cycle), + self.compare_property("is_generated", left, right, |c| &c.is_generated), + self.compare_option_property("generation_expression", left, right, |c| &c.generation_expression), + self.compare_property("is_updatable", left, right, |c| &c.is_updatable), ]; - + if !self.ignore_column_ordinal { - properties.push(self.compare_property("ordinal_position", &left.ordinal_position, &right.ordinal_position)); + properties.push(self.compare_property("ordinal_position", left, right, |c| &c.ordinal_position)); } - + Report { entries: properties } } - fn compare_table_column_privileges(&mut self, schema_name: &str, table_name: &str, column_name: &str) -> Result, Error> { + fn compare_table_column_privileges(&mut self, left_column_privileges: Vec<&ColumnPrivilege>, right_column_privileges: Vec<&ColumnPrivilege>) -> Result, Error> { if self.ignore_privileges { return Ok(Report { entries: vec![] }) } - - let left_column_privileges = self.left_db.column_privileges(schema_name, table_name, column_name)?; - let right_column_privileges = self.right_db.column_privileges(schema_name, table_name, column_name)?; - + Ok(self.compare_privileges(&left_column_privileges, &right_column_privileges)) } - fn compare_table_constraints(&mut self, schema_name: &str, table_name: &str) -> Result, Error> { - let left_table_constraints = self.left_db.table_constraints(schema_name, table_name)?; - let right_table_constraints = self.right_db.table_constraints(schema_name, table_name)?; - - let mut right_table_constraints_map : HashMap = right_table_constraints.into_iter().map(|t| (t.constraint_name.clone(), t)).collect(); + fn compare_table_constraints(&mut self, left_table_constraints: Vec<&TableConstraint>, right_table_constraints: Vec<&TableConstraint>) -> Result, Error> { + let mut right_table_constraints_map : HashMap = right_table_constraints.into_iter().map(|t| (t.constraint_name.clone(), t)).collect(); let mut entries = Vec::new(); - + for left_table_constraint in left_table_constraints { let key = left_table_constraint.constraint_name.clone(); let right_table_constraint = right_table_constraints_map.get(&key); - + match right_table_constraint { None => { - entries.push(ConstraintRemoved { constraint_name: left_table_constraint.constraint_name }); + entries.push(ConstraintRemoved { constraint_name: left_table_constraint.clone().constraint_name }); }, Some(rtc) => { - let properties = self.compare_table_constraint(&left_table_constraint, rtc); - + let properties = self.compare_table_constraint_properties(&left_table_constraint, rtc); + entries.push(ConstraintMaintained { constraint_name: rtc.constraint_name.clone(), properties }); - + right_table_constraints_map.remove(&key); }, } } - + if right_table_constraints_map.len() > 0 { - let mut added_table_constraints : Vec<&Constraint> = right_table_constraints_map.values().collect(); + let mut added_table_constraints : Vec<&&TableConstraint> = right_table_constraints_map.values().collect(); added_table_constraints.sort_unstable_by_key(|tc| &tc.constraint_name); - + for right_table_constraint in added_table_constraints { entries.push(ConstraintAdded { constraint_name: right_table_constraint.constraint_name.clone() }); } } - + Ok(Report { entries }) } - fn compare_table_constraint(&mut self, left: &Constraint, right: &Constraint) -> Report { + fn compare_table_constraint_properties(&mut self, left: &TableConstraint, right: &TableConstraint) -> Report { Report { entries: vec![ - self.compare_property("constraint_type", &left.constraint_type, &right.constraint_type), - self.compare_property("is_deferrable", &left.is_deferrable, &right.is_deferrable), - self.compare_property("initially_deferred", &left.initially_deferred, &right.initially_deferred), - self.compare_option_property("nulls_distinct", &left.nulls_distinct, &right.nulls_distinct), + self.compare_property("constraint_catalog", left, right, |c| &c.constraint_catalog), + self.compare_property("constraint_schema", left, right, |c| &c.constraint_schema), + self.compare_property("table_name", left, right, |c| &c.table_name), + self.compare_property("constraint_type", left, right, |c| &c.constraint_type), + self.compare_property("is_deferrable", left, right, |c| &c.is_deferrable), + self.compare_property("initially_deferred", left, right, |c| &c.initially_deferred), + self.compare_property("enforced", left, right, |c| &c.enforced), + self.compare_option_property("nulls_distinct", left, right, |c| &c.nulls_distinct), ] } } - fn compare_table_triggers(&mut self, schema_name: &str, table_name: &str) -> Result, Error> { - let left_triggers = self.left_db.triggers(schema_name, table_name)?; - let right_triggers = self.right_db.triggers(schema_name, table_name)?; - - let mut right_triggers_map : HashMap<(String, String), Trigger> = right_triggers.into_iter().map(|t| ((t.trigger_name.clone(), t.event_manipulation.clone()), t)).collect(); + fn compare_table_triggers(&mut self, left_table_triggers: Vec<&TableTrigger>, right_table_triggers: Vec<&TableTrigger>) -> Result, Error> { + let mut right_triggers_map : HashMap<(String, String), &TableTrigger> = right_table_triggers.into_iter().map(|t| ((t.trigger_name.clone(), t.event_manipulation.clone()), t)).collect(); let mut entries = Vec::new(); - - for left_trigger in left_triggers { - let key = &(left_trigger.trigger_name.clone(), left_trigger.event_manipulation.clone()); - let right_trigger = right_triggers_map.get(&key); - - match right_trigger { + + for left_table_trigger in left_table_triggers { + let key = &(left_table_trigger.trigger_name.clone(), left_table_trigger.event_manipulation.clone()); + let right_table_trigger = right_triggers_map.get(&key); + + match right_table_trigger { None => { - entries.push(TriggerRemoved { trigger_name: left_trigger.trigger_name, event_manipulation: left_trigger.event_manipulation }); + entries.push(TriggerRemoved { trigger_name: left_table_trigger.trigger_name.clone(), event_manipulation: left_table_trigger.event_manipulation.clone() }); }, Some(rt) => { - let properties = self.compare_trigger_properties(&left_trigger, rt); - + let properties = self.compare_trigger_properties(&left_table_trigger, rt); + entries.push(TriggerMaintained { trigger_name: rt.trigger_name.clone(), event_manipulation: rt.event_manipulation.clone(), properties }); - + right_triggers_map.remove(key); }, } } - + if right_triggers_map.len() > 0 { - let mut added_triggers : Vec<&Trigger> = right_triggers_map.values().collect(); + let mut added_triggers : Vec<&&TableTrigger> = right_triggers_map.values().collect(); added_triggers.sort_unstable_by_key(|t| (&t.trigger_name, &t.event_manipulation)); - + for right_trigger in added_triggers { entries.push(TriggerAdded { trigger_name: right_trigger.trigger_name.clone(), event_manipulation: right_trigger.event_manipulation.clone() }); } } - + Ok(Report { entries }) } - fn compare_trigger_properties(&mut self, left: &Trigger, right: &Trigger) -> Report { + fn compare_trigger_properties(&mut self, left: &TableTrigger, right: &TableTrigger) -> Report { Report { entries: vec![ - self.compare_property("action_order", &left.action_order, &right.action_order), - self.compare_option_property("action_condition", &left.action_condition, &right.action_condition), - self.compare_property("action_statement", &left.action_statement, &right.action_statement), - self.compare_property("action_orientation", &left.action_orientation, &right.action_orientation), - self.compare_property("action_timing", &left.action_timing, &right.action_timing), - self.compare_option_property("action_reference_old_table", &left.action_reference_old_table, &right.action_reference_old_table), - self.compare_option_property("action_reference_new_table", &left.action_reference_new_table, &right.action_reference_new_table), + self.compare_property("trigger_catalog", left, right, |t| &t.trigger_catalog), + self.compare_property("trigger_schema", left, right, |t| &t.trigger_schema), + self.compare_property("action_order", left, right, |t| &t.action_order), + self.compare_option_property("action_condition", left, right, |t| &t.action_condition), + self.compare_property("action_statement", left, right, |t| &t.action_statement), + self.compare_property("action_orientation", left, right, |t| &t.action_orientation), + self.compare_property("action_timing", left, right, |t| &t.action_timing), + self.compare_option_property("action_reference_old_table", left, right, |t| &t.action_reference_old_table), + self.compare_option_property("action_reference_new_table", left, right, |t| &t.action_reference_new_table), ] } } - fn compare_views(&mut self, schema_name: &str) -> Result, Error> { - let left_views = self.left_db.views(schema_name)?; - let right_views = self.right_db.views(schema_name)?; - - let right_views_map: HashMap = right_views.into_iter().map(|t| (t.view_name.clone(), t)).collect(); + fn compare_views(&mut self, left_views: Vec<&View>, right_views: Vec<&View>) -> Result, Error> { + let right_views_map: HashMap = right_views.into_iter().map(|t| (t.table_name.clone(), t)).collect(); let mut entries = Vec::new(); - + for left_view in left_views { - let right_view = right_views_map.get(&left_view.view_name); - + let right_view = right_views_map.get(&left_view.table_name); + match right_view { None => { // already detected by table comparison @@ -488,33 +649,37 @@ impl Comparer { }, Some(rv) => { let properties = self.compare_view_properties(&left_view, rv); - - entries.push(ViewMaintained { view_name: rv.view_name.clone(), properties }); + + entries.push(ViewMaintained { view_name: rv.table_name.clone(), properties }); } } } - + // added already detected by table comparison - + Ok(Report { entries }) } fn compare_view_properties(&mut self, left: &View, right: &View) -> Report { Report { entries: vec![ - self.compare_option_property("view_definition", &left.view_definition, &right.view_definition), - self.compare_property("check_option", &left.check_option, &right.check_option), - self.compare_property("is_updatable", &left.is_updatable, &right.is_updatable), - self.compare_property("is_insertable_into", &left.is_insertable_into, &right.is_insertable_into), - self.compare_property("is_trigger_updatable", &left.is_trigger_updatable, &right.is_trigger_updatable), - self.compare_property("is_trigger_deletable", &left.is_trigger_deletable, &right.is_trigger_deletable), - self.compare_property("is_trigger_insertable_into", &left.is_trigger_insertable_into, &right.is_trigger_insertable_into), + self.compare_option_property("view_definition", left, right, |c| &c.view_definition), + self.compare_property("check_option", left, right, |c| &c.check_option), + self.compare_property("is_updatable", left, right, |c| &c.is_updatable), + self.compare_property("is_insertable_into", left, right, |c| &c.is_insertable_into), + self.compare_property("is_trigger_updatable", left, right, |c| &c.is_trigger_updatable), + self.compare_property("is_trigger_deletable", left, right, |c| &c.is_trigger_deletable), + self.compare_property("is_trigger_insertable_into", left, right, |c| &c.is_trigger_insertable_into), ] } } - fn compare_property(&self, property_name: &str, left_value: T, right_value: T) -> PropertyComparison - where T: PartialEq, T: Display { + fn compare_property<'a, T, P>(&self, property_name: &str, left: T, right: T, accessor: fn(T) -> &'a P) -> PropertyComparison + where P: PartialEq, P: Display + { + let left_value = accessor(left); + let right_value = accessor(right); + if left_value == right_value { PropertyUnchanged { property_name: String::from(property_name), value: left_value.to_string() } } else { @@ -522,18 +687,27 @@ impl Comparer { } } - fn compare_option_property(&self, property_name: &str, left_value: &Option, right_value: &Option) -> PropertyComparison - where T: PartialEq, T: Display { - self.compare_option_property_impl(property_name, left_value, right_value, &|l: &T, r: &T| l == r) + fn compare_option_property<'a, T, P>(&self, property_name: &str, left: T, right: T, accessor: fn(T) -> &'a Option

) -> PropertyComparison + where P: PartialEq, P: Display + { + let left_value = accessor(left); + let right_value = accessor(right); + + self.compare_option_property_impl(property_name, &left_value, &right_value, &|l: &P, r: &P| l == r) } - - fn compare_option_property_ignore_whitespace(&self, property_name: &str, left_value: &Option, right_value: &Option) -> PropertyComparison - where T: PartialEq, T: Display { - self.compare_option_property_impl(property_name, left_value, right_value, &|l: &T, r: &T| l.to_string().as_str().eq_ignore_whitespace(r.to_string().as_str())) + + fn compare_option_property_ignore_whitespace<'a, T, P>(&self, property_name: &str, left: T, right: T, accessor: fn(T) -> &'a Option

) -> PropertyComparison + where P: PartialEq, P: Display + { + let left_value = accessor(left); + let right_value = accessor(right); + + self.compare_option_property_impl(property_name, left_value, right_value, &|l: &P, r: &P| l.to_string().as_str().eq_ignore_whitespace(r.to_string().as_str())) } fn compare_option_property_impl(&self, property_name: &str, left_value: &Option, right_value: &Option, compare: &dyn Fn(&T, &T) -> bool) -> PropertyComparison - where T: PartialEq, T: Display { + where T: PartialEq, T: Display + { if left_value.is_none() && right_value.is_none() { return PropertyUnchanged { property_name: String::from(property_name), value: String::from("") } } diff --git a/src/compare/report/table_column.rs b/src/compare/report/column.rs similarity index 64% rename from src/compare/report/table_column.rs rename to src/compare/report/column.rs index 350f11a..75153c2 100644 --- a/src/compare/report/table_column.rs +++ b/src/compare/report/column.rs @@ -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, privileges: Report } } -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(), } diff --git a/src/compare/report/mod.rs b/src/compare/report/mod.rs index 040b0df..37a0179 100644 --- a/src/compare/report/mod.rs +++ b/src/compare/report/mod.rs @@ -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; diff --git a/src/compare/report/table.rs b/src/compare/report/table.rs index 635994a..2b497a5 100644 --- a/src/compare/report/table.rs +++ b/src/compare/report/table.rs @@ -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, columns: Report, privileges: Report, constraints: Report, triggers: Report }, + TableMaintained { table_name: String, properties: Report, columns: Report, privileges: Report, constraints: Report, triggers: Report }, } impl HasChanges for TableComparison { diff --git a/src/db/column.rs b/src/db/column.rs new file mode 100644 index 0000000..10d8b43 --- /dev/null +++ b/src/db/column.rs @@ -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, 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, + pub is_nullable: String, + pub data_type: String, + pub character_maximum_length: Option, + pub character_octet_length: Option, + pub numeric_precision: Option, + pub numeric_precision_radix: Option, + pub numeric_scale: Option, + pub datetime_precision: Option, + pub interval_type: Option, + pub interval_precision: Option, + pub character_set_catalog: Option, + pub character_set_schema: Option, + pub character_set_name: Option, + pub collation_catalog: Option, + pub collation_schema: Option, + pub collation_name: Option, + pub domain_catalog: Option, + pub domain_schema: Option, + pub domain_name: Option, + pub udt_catalog: Option, + pub udt_schema: Option, + pub udt_name: Option, + pub scope_catalog: Option, + pub scope_schema: Option, + pub scope_name: Option, + pub maximum_cardinality: Option, + pub dtd_identifier: Option, + pub is_self_referencing: String, + pub is_identity: String, + pub identity_generation: Option, + pub identity_start: Option, + pub identity_increment: Option, + pub identity_maximum: Option, + pub identity_minimum: Option, + pub identity_cycle: Option, + pub is_generated: String, + pub generation_expression: Option, + pub is_updatable: String, +} + diff --git a/src/db/column_privilege.rs b/src/db/column_privilege.rs new file mode 100644 index 0000000..2c4cb07 --- /dev/null +++ b/src/db/column_privilege.rs @@ -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, 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 + } +} \ No newline at end of file diff --git a/src/db/mod.rs b/src/db/mod.rs index 51a7194..48ca01e 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,538 +1,79 @@ -pub mod thing; - -use std::collections::HashMap; -use postgres::{Client, NoTls, Error}; -use crate::db::thing::{Column, Constraint, Privilege, Routine, Schema, Sequence, Table, Trigger, View}; +pub mod column; +pub mod column_privilege; +pub mod privilege; +pub mod routine; +pub mod routine_privilege; +pub mod schema; +pub mod sequence; +pub mod table; +pub mod table_constraint; +pub mod table_privilege; +pub mod table_trigger; +pub mod view; + +use sqlx::{Connection, Error, PgConnection}; +use crate::db::column::Column; +use crate::db::column_privilege::ColumnPrivilege; +use crate::db::routine::Routine; +use crate::db::routine_privilege::RoutinePrivilege; +use crate::db::table_privilege::TablePrivilege; +use crate::db::table_trigger::TableTrigger; +use crate::db::view::View; pub struct Database { - connection: Client + connection: PgConnection } impl Database { - pub fn connect(url: &str) -> Result { - let client = Client::connect(url, NoTls)?; + pub async fn connect(url: &str) -> Result { + let connection = PgConnection::connect(url).await?; Ok(Database { - connection: client + connection, }) } - pub fn columns(&mut self, schema_name: &str, table_name: &str) -> Result, Error> { - let mut columns = Vec::new(); - - let rows = self.connection.query(r#" -SELECT - column_name, - ordinal_position, - column_default, - is_nullable, - data_type, - character_maximum_length, - numeric_precision, - numeric_scale, - datetime_precision, - is_identity, - identity_generation, - is_generated, - generation_expression, - is_updatable -FROM - information_schema.columns -WHERE - table_schema = $1 AND - table_name = $2 -ORDER BY - table_name, - column_name;"#, - &[&schema_name, &table_name])?; - - for row in rows { - let column_name: String = row.get(0); - let ordinal_position: i32 = row.get(1); - let column_default: Option = row.get(2); - let is_nullable: String = row.get(3); - let data_type: String = row.get(4); - let character_maximum_length: Option = row.get(5); - let numeric_precision: Option = row.get(6); - let numeric_scale: Option = row.get(7); - let datetime_precision: Option = row.get(8); - let is_identity: String = row.get(9); - let identity_generation: Option = row.get(10); - let is_generated: String = row.get(11); - let generation_expression: Option = row.get(12); - let is_updatable: String = row.get(13); - - let column = Column { - column_name, - ordinal_position, - column_default, - is_nullable, - data_type, - character_maximum_length, - numeric_precision, - numeric_scale, - datetime_precision, - is_identity, - identity_generation, - is_generated, - generation_expression, - is_updatable, - }; - - columns.push(column.clone()); - } - - Ok(columns) + pub async fn columns(&mut self, schema_names: &[String]) -> Result, Error> { + column::columns(&mut self.connection, schema_names).await } - pub fn column_privileges(&mut self, schema_name: &str, table_name: &str, column_name: &str) -> Result, Error> { - let mut column_privileges = Vec::new(); - - let rows = self.connection.query(r#" -SELECT - grantor, - grantee, - privilege_type, - is_grantable -FROM - information_schema.column_privileges -WHERE - table_schema = $1 AND - table_name = $2 AND - column_name = $3 AND - grantor != grantee -ORDER BY - table_name, - column_name, - privilege_type, - grantor, - grantee;"#, - &[&schema_name, &table_name, &column_name])?; - - for row in rows { - let grantor: String = row.get(0); - let grantee: String = row.get(1); - let privilege_type: String = row.get(2); - let is_grantable: String = row.get(3); - - let column_privilege = Privilege { - grantor, - grantee, - privilege_type, - is_grantable, - }; - - column_privileges.push(column_privilege.clone()); - } - - Ok(column_privileges) + pub async fn column_privileges(&mut self, schema_names: &[String]) -> Result, Error> { + column_privilege::column_privileges(&mut self.connection, schema_names).await } - pub fn routines(&mut self, schema_name: &str) -> Result, Error> { - let mut routines = Vec::new(); - - let rows = self.connection.query(r#" -SELECT - r.routine_name || '(' || COALESCE(( - SELECT string_agg(COALESCE(p.parameter_name, '$' || p.ordinal_position) || ' ' || p.parameter_mode || ' ' || p.udt_schema || '.' || p.udt_name, ', ' order by p.ordinal_position) - FROM information_schema.parameters p - WHERE p.specific_name = r.specific_name - GROUP BY p.specific_name - ), '') || ')' signature, - r.routine_type, - r.data_type, - r.type_udt_name, - r.routine_body, - r.routine_definition, - r.external_name, - r.external_language, - r.is_deterministic, - r.is_null_call, - r.security_type -FROM - information_schema.routines r -WHERE - r.routine_schema = $1 -ORDER BY - signature;"#, - &[&schema_name])?; - - for row in rows { - let signature: String = row.get(0); - let routine_type: Option = row.get(1); - let data_type: Option = row.get(2); - let type_udt_name: Option = row.get(3); - let routine_body: String = row.get(4); - let routine_definition: Option = row.get(5); - let external_name: Option = row.get(6); - let external_language: String = row.get(7); - let is_deterministic: String = row.get(8); - let is_null_call: Option = row.get(9); - let security_type: String = row.get(10); - - let routine = Routine { - signature, - routine_type, - data_type, - type_udt_name, - routine_body, - routine_definition, - external_name, - external_language, - is_deterministic, - is_null_call, - security_type, - }; - - routines.push(routine); - } - - Ok(routines) + pub async fn routines(&mut self, schema_names: &[String]) -> Result, Error> { + routine::routines(&mut self.connection, schema_names).await } - - pub fn routine_privileges(&mut self, schema_name: &str) -> Result>, Error> { - let mut privilegess_by_signature = HashMap::new(); - - let rows = self.connection.query(r#" - SELECT - rp.grantor, - rp.grantee, - rp.routine_name || '(' || COALESCE(( - SELECT string_agg(COALESCE(p.parameter_name, '$' || p.ordinal_position) || ' ' || p.parameter_mode || ' ' || p.udt_schema || '.' || p.udt_name, ', ' order by p.ordinal_position) - FROM information_schema.parameters p - WHERE p.specific_name = rp.specific_name - GROUP BY p.specific_name - ), '') || ')' signature, - rp.privilege_type, - rp.is_grantable - FROM - information_schema.routine_privileges rp - WHERE - rp.routine_schema = $1 AND - rp.grantor != rp.grantee - ORDER BY - signature, - privilege_type;"#, - &[&schema_name])?; - - for row in rows { - let grantor: String = row.get(0); - let grantee: String = row.get(1); - let signature: String = row.get(2); - let privilege_type: String = row.get(3); - let is_grantable: String = row.get(4); - - let routine_privileges = privilegess_by_signature - .entry(signature) - .or_insert_with(|| Vec::new()); - - let routine_privilege = Privilege { - grantor, - grantee, - privilege_type, - is_grantable, - }; - - routine_privileges.push(routine_privilege); - } - - Ok(privilegess_by_signature) + + pub async fn routine_privileges(&mut self, schema_names: &[String]) -> Result, Error> { + routine_privilege::routine_privileges(&mut self.connection, schema_names).await } - - pub fn schema(&mut self, schema_name: &str) -> Result, Error> { - let row = self.connection.query_opt(r#" -SELECT - schema_owner -FROM - information_schema.schemata -WHERE - schema_name = $1 -ORDER BY - schema_name;"#, - &[&schema_name])?; - - match row { - None => Ok(None), - Some(row) => { - let schema_owner = row.get(0); - - let schema = Schema { - schema_name: String::from(schema_name), - schema_owner, - }; - - Ok(Some(schema)) - } - } + + pub async fn schemas(&mut self, schema_names: &[String]) -> Result, Error> { + schema::query(&mut self.connection, &schema_names).await } - pub fn sequences(&mut self, schema_name: &str) -> Result, Error> { - let mut sequences = Vec::new(); - - let rows = self.connection.query(r#" -SELECT - sequence_name, - data_type, - numeric_precision, - numeric_precision_radix, - numeric_scale, - start_value, - minimum_value, - maximum_value, - increment, - cycle_option -FROM - information_schema.sequences -WHERE - sequence_schema = $1 -ORDER BY - sequence_name;"#, - &[&schema_name])?; - - for row in rows { - let sequence_name: String = row.get(0); - let data_type: String = row.get(1); - let numeric_precision: i32 = row.get(2); - let numeric_precision_radix: i32 = row.get(3); - let numeric_scale: i32 = row.get(4); - let start_value: String = row.get(5); - let minimum_value: String = row.get(6); - let maximum_value: String = row.get(7); - let increment: String = row.get(8); - let cycle_option: String = row.get(9); - - let sequence = Sequence { - sequence_name, - data_type, - numeric_precision, - numeric_precision_radix, - numeric_scale, - start_value, - minimum_value, - maximum_value, - increment, - cycle_option, - }; - - sequences.push(sequence); - } - - Ok(sequences) + pub async fn sequences(&mut self, schema_names: &[String]) -> Result, Error> { + sequence::query(&mut self.connection, &schema_names[..]).await } - pub fn tables(&mut self, schema_name: &str) -> Result, Error> { - let mut tables = Vec::new(); - - let rows = self.connection.query(r#" -SELECT - table_name, - table_type, - is_insertable_into -FROM - information_schema.tables -WHERE - table_schema = $1 -ORDER BY - table_name;"#, - &[&schema_name])?; - - for row in rows { - let table_name : String = row.get(0); - let table_type : String = row.get(1); - let is_insertable_into : String = row.get(2); - - let table = Table { - table_name, - table_type, - is_insertable_into, - }; - - tables.push(table); - } - - Ok(tables) + pub async fn tables(&mut self, schema_names: &[String]) -> Result, Error> { + table::query(&mut self.connection, schema_names).await } - pub fn table_constraints(&mut self, schema_name: &str, table_name: &str) -> Result, Error> { - let mut table_constraints = Vec::new(); - - let rows = self.connection.query(r#" -SELECT - constraint_name, - constraint_type, - is_deferrable, - initially_deferred, - nulls_distinct -FROM - information_schema.table_constraints -WHERE - table_schema = $1 AND - table_name = $2 AND - constraint_type != 'CHECK' -ORDER BY - table_name, - constraint_name;"#, - &[&schema_name, &table_name])?; - - for row in rows { - let constraint_name: String = row.get(0); - let constraint_type: String = row.get(1); - let is_deferrable: String = row.get(2); - let initially_deferred: String = row.get(3); - let nulls_distinct: Option = row.get(4); - - let table_constraint = Constraint { - constraint_name, - constraint_type, - is_deferrable, - initially_deferred, - nulls_distinct, - }; - - table_constraints.push(table_constraint.clone()); - } - - Ok(table_constraints) + pub async fn table_constraints(&mut self, schema_names: &[String]) -> Result, Error> { + table_constraint::query(&mut self.connection, schema_names).await } - - pub fn table_privileges(&mut self, schema_name: &str, table_name: &str) -> Result, Error> { - let mut table_privileges = Vec::new(); - - let rows = self.connection.query(r#" -SELECT - grantor, - grantee, - privilege_type, - is_grantable -FROM - information_schema.table_privileges -WHERE - table_schema = $1 AND - table_name = $2 AND - grantor != grantee -ORDER BY - privilege_type, - grantor, - grantee;"#, - &[&schema_name, &table_name])?; - - for row in rows { - let grantor: String = row.get(0); - let grantee: String = row.get(1); - let privilege_type: String = row.get(2); - let is_grantable: String = row.get(3); - - let table_privilege = Privilege { - grantor, - grantee, - privilege_type, - is_grantable, - }; - - table_privileges.push(table_privilege.clone()); - } - - Ok(table_privileges) + + pub async fn table_privileges(&mut self, schema_names: &[String]) -> Result, Error> { + table_privilege::query(&mut self.connection, schema_names).await } - - pub fn triggers(&mut self, schema_name: &str, table_name: &str) -> Result, Error> { - let mut triggers = Vec::new(); - - let rows = self.connection.query(r#" -SELECT - trigger_name, - event_manipulation, - action_order, - action_condition, - action_statement, - action_orientation, - action_timing, - action_reference_old_table, - action_reference_new_table -FROM - information_schema.triggers -WHERE - trigger_schema = $1 AND - event_object_schema = $1 AND - event_object_table = $2 -ORDER BY - trigger_name, event_manipulation;"#, - &[&schema_name, &table_name])?; - - for row in rows { - let trigger_name: String = row.get(0); - let event_manipulation: String = row.get(1); - let action_order: i32 = row.get(2); - let action_condition: Option = row.get(3); - let action_statement: String = row.get(4); - let action_orientation: String = row.get(5); - let action_timing: String = row.get(6); - let action_reference_old_table: Option = row.get(7); - let action_reference_new_table: Option = row.get(8); - - let trigger = Trigger { - trigger_name, - event_manipulation, - action_order, - action_condition, - action_statement, - action_orientation, - action_timing, - action_reference_old_table, - action_reference_new_table, - }; - - triggers.push(trigger); - } - - Ok(triggers) + + pub async fn table_triggers(&mut self, schema_names: &[String]) -> Result, Error> { + table_trigger::query(&mut self.connection, schema_names).await } - pub fn views(&mut self, schema_name: &str) -> Result, Error> { - let mut views = Vec::new(); - - let rows = self.connection.query(r#" -SELECT - table_name, - view_definition, - check_option, - is_updatable, - is_insertable_into, - is_trigger_updatable, - is_trigger_deletable, - is_trigger_insertable_into -FROM - information_schema.views -WHERE - table_schema = $1 -ORDER BY - table_name;"#, - &[&schema_name])?; - - for row in rows { - let view_name: String = row.get(0); - let view_definition: Option = row.get(1); - let check_option: String = row.get(2); - let is_updatable: String = row.get(3); - let is_insertable_into: String = row.get(4); - let is_trigger_updatable: String = row.get(5); - let is_trigger_deletable: String = row.get(6); - let is_trigger_insertable_into: String = row.get(7); - - let view = View { - view_name, - view_definition, - check_option, - is_updatable, - is_insertable_into, - is_trigger_updatable, - is_trigger_deletable, - is_trigger_insertable_into, - }; - - views.push(view); - } - - Ok(views) + pub async fn views(&mut self, schema_names: &[String]) -> Result, Error> { + view::query(&mut self.connection, schema_names).await } } diff --git a/src/db/privilege.rs b/src/db/privilege.rs new file mode 100644 index 0000000..bb7bab3 --- /dev/null +++ b/src/db/privilege.rs @@ -0,0 +1,5 @@ +pub trait Privilege { + fn grantor(&self) -> &str; + fn grantee(&self) -> &str; + fn privilege_type(&self) -> &str; +} diff --git a/src/db/routine.rs b/src/db/routine.rs new file mode 100644 index 0000000..bf4e607 --- /dev/null +++ b/src/db/routine.rs @@ -0,0 +1,116 @@ +use sqlx::{Error, FromRow, PgConnection}; + +pub const QUERY: &str = r#" +SELECT + r.routine_catalog, + r.routine_schema, + r.routine_name || '(' || COALESCE(( + SELECT string_agg(COALESCE(p.parameter_name, '$' || p.ordinal_position) || ' ' || p.parameter_mode || ' ' || p.udt_schema || '.' || p.udt_name, ', ' order by p.ordinal_position) + FROM information_schema.parameters p + WHERE p.specific_name = r.specific_name + GROUP BY p.specific_name + ), '') || ')' signature, + r.routine_type, + r.module_catalog, + r.module_schema, + r.module_name, + r.udt_catalog, + r.udt_schema, + r.udt_name, + r.data_type, + r.character_maximum_length, + r.character_octet_length, + r.character_set_catalog, + r.character_set_schema, + r.character_set_name, + r.collation_catalog, + r.collation_schema, + r.collation_name, + r.numeric_precision, + r.numeric_precision_radix, + r.numeric_scale, + r.datetime_precision, + r.interval_type, + r.interval_precision, + r.type_udt_catalog, + r.type_udt_schema, + r.type_udt_name, + r.maximum_cardinality, + r.dtd_identifier, + r.routine_body, + r.routine_definition, + r.external_name, + r.external_language, + r.parameter_style, + r.is_deterministic, + r.sql_data_access, + r.is_null_call, + r.sql_path, + r.schema_level_routine, + r.max_dynamic_result_sets, + r.is_user_defined_cast, + r.is_implicitly_invocable, + r.security_type, + r.is_udt_dependent +FROM + information_schema.routines r +WHERE + r.routine_schema = ANY($1) +ORDER BY + signature;"#; + +pub async fn routines(connection: &mut PgConnection, schema_names: &[String]) -> Result, Error> { + sqlx::query_as(QUERY) + .bind(&schema_names[..]) + .fetch_all(connection).await +} + +#[derive(Debug, Clone, PartialEq, FromRow)] +pub struct Routine { + pub routine_catalog: String, + pub routine_schema: String, + pub signature: String, + pub routine_type: Option, + pub module_catalog: Option, + pub module_schema: Option, + pub module_name: Option, + pub udt_catalog: Option, + pub udt_schema: Option, + pub udt_name: Option, + pub data_type: Option, + pub character_maximum_length: Option, + pub character_octet_length: Option, + pub character_set_catalog: Option, + pub character_set_schema: Option, + pub character_set_name: Option, + pub collation_catalog: Option, + pub collation_schema: Option, + pub collation_name: Option, + pub numeric_precision: Option, + pub numeric_precision_radix: Option, + pub numeric_scale: Option, + pub datetime_precision: Option, + pub interval_type: Option, + pub interval_precision: Option, + pub type_udt_catalog: Option, + pub type_udt_schema: Option, + pub type_udt_name: Option, + pub maximum_cardinality: Option, + pub dtd_identifier: Option, + pub routine_body: String, + pub routine_definition: Option, + pub external_name: Option, + pub external_language: String, + pub parameter_style: String, + pub is_deterministic: String, + pub sql_data_access: String, + pub is_null_call: Option, + pub sql_path: Option, + pub schema_level_routine: String, + pub max_dynamic_result_sets: Option, + pub is_user_defined_cast: Option, + pub is_implicitly_invocable: Option, + pub security_type: String, + pub is_udt_dependent: String, +} + diff --git a/src/db/routine_privilege.rs b/src/db/routine_privilege.rs new file mode 100644 index 0000000..b8e872d --- /dev/null +++ b/src/db/routine_privilege.rs @@ -0,0 +1,56 @@ +use sqlx::{Error, FromRow, PgConnection}; +use crate::db::privilege::Privilege; + +const QUERY: &str = r#" +SELECT + rp.grantor, + rp.grantee, + rp.routine_catalog, + rp.routine_schema, + rp.routine_name || '(' || COALESCE(( + SELECT string_agg(COALESCE(p.parameter_name, '$' || p.ordinal_position) || ' ' || p.parameter_mode || ' ' || p.udt_schema || '.' || p.udt_name, ', ' order by p.ordinal_position) + FROM information_schema.parameters p + WHERE p.specific_name = rp.specific_name + GROUP BY p.specific_name + ), '') || ')' signature, + rp.privilege_type, + rp.is_grantable +FROM + information_schema.routine_privileges rp +WHERE + rp.routine_schema = ANY($1) AND + rp.grantor != rp.grantee +ORDER BY + signature, + privilege_type;"#; + +pub async fn routine_privileges(connection: &mut PgConnection, schema_names: &[String]) -> Result, Error> { + sqlx::query_as(QUERY) + .bind(&schema_names[..]) + .fetch_all(connection).await +} + +#[derive(Debug, Clone, PartialEq, FromRow)] +pub struct RoutinePrivilege { + pub grantor: String, + pub grantee: String, + pub routine_catalog: String, + pub routine_schema: String, + pub signature: String, + pub privilege_type: String, + pub is_grantable: String, +} + +impl Privilege for &RoutinePrivilege { + fn grantor(&self) -> &str { + &self.grantor + } + + fn grantee(&self) -> &str { + &self.grantee + } + + fn privilege_type(&self) -> &str { + &self.privilege_type + } +} \ No newline at end of file diff --git a/src/db/schema.rs b/src/db/schema.rs new file mode 100644 index 0000000..5df75b6 --- /dev/null +++ b/src/db/schema.rs @@ -0,0 +1,33 @@ +use sqlx::{Error, FromRow, PgConnection}; + +const QUERY: &str = r#" +SELECT + schema_name, + schema_owner, + default_character_set_catalog, + default_character_set_schema, + default_character_set_name, + sql_path +FROM + information_schema.schemata +WHERE + schema_name = ANY($1) +ORDER BY + schema_name;"#; + +pub async fn query(connection: &mut PgConnection, schema_names: &[String]) -> Result, Error> { + sqlx::query_as(QUERY) + .bind(&schema_names[..]) + .fetch_all(connection).await +} + +#[derive(Debug, Clone, PartialEq, FromRow)] +pub struct Schema { + pub schema_name: String, + pub schema_owner: String, + pub default_character_set_catalog: Option, + pub default_character_set_schema: Option, + pub default_character_set_name: Option, + pub sql_path: Option, +} + diff --git a/src/db/sequence.rs b/src/db/sequence.rs new file mode 100644 index 0000000..9248d8e --- /dev/null +++ b/src/db/sequence.rs @@ -0,0 +1,47 @@ +use sqlx::{Error, FromRow, PgConnection}; + +const QUERY : &str = r#" +SELECT + sequence_catalog, + sequence_schema, + sequence_name, + data_type, + numeric_precision, + numeric_precision_radix, + numeric_scale, + start_value, + minimum_value, + maximum_value, + increment, + cycle_option +FROM + information_schema.sequences +WHERE + sequence_schema = ANY($1) +ORDER BY + sequence_catalog, + sequence_schema, + sequence_name;"#; + +pub async fn query(connection: &mut PgConnection, schema_names: &[String]) -> Result, Error> { + sqlx::query_as(QUERY) + .bind(&schema_names[..]) + .fetch_all(connection).await +} + +#[derive(Debug, Clone, PartialEq, FromRow)] +pub struct Sequence { + pub sequence_catalog: String, + pub sequence_schema: String, + pub sequence_name: String, + pub data_type: String, + pub numeric_precision: i32, + pub numeric_precision_radix: i32, + pub numeric_scale: i32, + pub start_value: String, + pub minimum_value: String, + pub maximum_value: String, + pub increment: String, + pub cycle_option: String, +} + diff --git a/src/db/table.rs b/src/db/table.rs new file mode 100644 index 0000000..919b194 --- /dev/null +++ b/src/db/table.rs @@ -0,0 +1,46 @@ +use sqlx::{Error, FromRow, PgConnection}; + +const QUERY : &str = r#" +SELECT + table_catalog, + table_schema, + table_name, + table_type, + self_referencing_column_name, + reference_generation, + user_defined_type_catalog, + user_defined_type_schema, + user_defined_type_name, + is_insertable_into, + is_typed, + commit_action +FROM + information_schema.tables +WHERE + table_schema = ANY($1) +ORDER BY + table_catalog, + table_schema, + table_name;"#; + +pub async fn query(connection: &mut PgConnection, schema_names: &[String]) -> Result, Error> { + sqlx::query_as(QUERY) + .bind(&schema_names[..]) + .fetch_all(connection).await +} + +#[derive(Debug, Clone, PartialEq, FromRow)] +pub struct Table { + pub table_catalog: String, + pub table_schema: String, + pub table_name: String, + pub table_type: String, + pub self_referencing_column_name: Option, + pub reference_generation: Option, + pub user_defined_type_catalog: Option, + pub user_defined_type_schema: Option, + pub user_defined_type_name: Option, + pub is_insertable_into: String, + pub is_typed: String, + pub commit_action: Option, +} diff --git a/src/db/table_constraint.rs b/src/db/table_constraint.rs new file mode 100644 index 0000000..4ae1f40 --- /dev/null +++ b/src/db/table_constraint.rs @@ -0,0 +1,48 @@ +use sqlx::{Error, FromRow, PgConnection}; + +const QUERY: &str = r#" +SELECT + constraint_catalog, + constraint_schema, + constraint_name, + constraint_type, + table_catalog, + table_schema, + table_name, + is_deferrable, + initially_deferred, + enforced, + nulls_distinct +FROM + information_schema.table_constraints +WHERE + table_schema = ANY($1) + AND + constraint_type != 'CHECK' +ORDER BY + table_catalog, + table_schema, + table_name, + constraint_name, + constraint_type;"#; + +pub async fn query(connection: &mut PgConnection, schema_names: &[String]) -> Result, Error> { + sqlx::query_as(QUERY) + .bind(&schema_names[..]) + .fetch_all(connection).await +} + +#[derive(Debug, Clone, PartialEq, FromRow)] +pub struct TableConstraint { + pub constraint_catalog: String, + pub constraint_schema: String, + pub constraint_name: String, + pub table_catalog: String, + pub table_schema: String, + pub table_name: String, + pub constraint_type: String, + pub is_deferrable: String, + pub initially_deferred: String, + pub enforced: String, + pub nulls_distinct: Option, +} diff --git a/src/db/table_privilege.rs b/src/db/table_privilege.rs new file mode 100644 index 0000000..e191edb --- /dev/null +++ b/src/db/table_privilege.rs @@ -0,0 +1,56 @@ +use sqlx::{Error, FromRow, PgConnection}; +use crate::db::privilege::Privilege; + +const QUERY: &str = r#" +SELECT + grantor, + grantee, + table_catalog, + table_schema, + table_name, + privilege_type, + is_grantable, + with_hierarchy +FROM + information_schema.table_privileges +WHERE + table_schema = ANY($1) +ORDER BY + table_catalog, + table_schema, + table_name, + grantor, + grantee, + privilege_type;"#; + +pub async fn query(connection: &mut PgConnection, schema_names: &[String]) -> Result, Error> { + sqlx::query_as(QUERY) + .bind(&schema_names[..]) + .fetch_all(connection).await +} + +#[derive(Debug, Clone, PartialEq, FromRow)] +pub struct TablePrivilege { + pub grantor: String, + pub grantee: String, + pub table_catalog: String, + pub table_schema: String, + pub table_name: String, + pub privilege_type: String, + pub is_grantable: String, + pub with_hierarchy: String, +} + +impl Privilege for &TablePrivilege { + fn grantor(&self) -> &str { + &self.grantor + } + + fn grantee(&self) -> &str { + &self.grantee + } + + fn privilege_type(&self) -> &str { + &self.privilege_type + } +} \ No newline at end of file diff --git a/src/db/table_trigger.rs b/src/db/table_trigger.rs new file mode 100644 index 0000000..a1eb504 --- /dev/null +++ b/src/db/table_trigger.rs @@ -0,0 +1,55 @@ +use sqlx::{Error, FromRow, PgConnection}; + +const QUERY : &str = r#" +SELECT + trigger_catalog, + trigger_schema, + trigger_name, + event_manipulation, + event_object_catalog, + event_object_schema, + event_object_table, + action_order, + action_condition, + action_statement, + action_orientation, + action_timing, + action_reference_old_table, + action_reference_new_table, + action_reference_old_row, + action_reference_new_row +FROM + information_schema.triggers +WHERE + trigger_schema = ANY($1) +ORDER BY + event_object_catalog, + event_object_schema, + event_object_table, + trigger_name, + event_manipulation;"#; + +pub async fn query(connection: &mut PgConnection, schema_names: &[String]) -> Result, Error> { + sqlx::query_as(QUERY) + .bind(&schema_names[..]) + .fetch_all(connection).await +} + +#[derive(Debug, Clone, PartialEq, FromRow)] +pub struct TableTrigger { + pub trigger_catalog: String, + pub trigger_schema: String, + pub trigger_name: String, + pub event_manipulation: String, + pub event_object_catalog: String, + pub event_object_schema: String, + pub event_object_table: String, + pub action_order: i32, + pub action_condition: Option, + pub action_statement: String, + pub action_orientation: String, + pub action_timing: String, + pub action_reference_old_table: Option, + pub action_reference_new_table: Option, +} + diff --git a/src/db/thing.rs b/src/db/thing.rs deleted file mode 100644 index 92c37dc..0000000 --- a/src/db/thing.rs +++ /dev/null @@ -1,101 +0,0 @@ -#[derive(Clone, PartialEq)] -pub struct Column { - pub column_name: String, - pub ordinal_position: i32, - pub column_default: Option, - pub is_nullable: String, - pub data_type: String, - pub character_maximum_length: Option, - pub numeric_precision: Option, - pub numeric_scale: Option, - pub datetime_precision: Option, - pub is_identity: String, - pub identity_generation: Option, - pub is_generated: String, - pub generation_expression: Option, - pub is_updatable: String, -} - -#[derive(Clone, PartialEq)] -pub struct Privilege { - pub grantor: String, - pub grantee: String, - pub privilege_type: String, - pub is_grantable: String, -} - -#[derive(Clone, PartialEq)] -pub struct Routine { - pub signature: String, - pub routine_type: Option, - pub data_type: Option, - pub type_udt_name: Option, - pub routine_body: String, - pub routine_definition: Option, - pub external_name: Option, - pub external_language: String, - pub is_deterministic: String, - pub is_null_call: Option, - pub security_type: String, -} - -#[derive(Clone, PartialEq)] -pub struct Schema { - pub schema_name: String, - pub schema_owner: String, -} - -#[derive(Clone, PartialEq)] -pub struct Sequence { - pub sequence_name: String, - pub data_type: String, - pub numeric_precision: i32, - pub numeric_precision_radix: i32, - pub numeric_scale: i32, - pub start_value: String, - pub minimum_value: String, - pub maximum_value: String, - pub increment: String, - pub cycle_option: String, -} - -#[derive(Clone, PartialEq)] -pub struct Table { - pub table_name: String, - pub table_type: String, - pub is_insertable_into: String, -} - -#[derive(Clone, PartialEq)] -pub struct Constraint { - pub constraint_name: String, - pub constraint_type: String, - pub is_deferrable: String, - pub initially_deferred: String, - pub nulls_distinct: Option, -} - -#[derive(Clone, PartialEq)] -pub struct Trigger { - pub trigger_name: String, - pub event_manipulation: String, - pub action_order: i32, - pub action_condition: Option, - pub action_statement: String, - pub action_orientation: String, - pub action_timing: String, - pub action_reference_old_table: Option, - pub action_reference_new_table: Option, -} - -#[derive(Clone, PartialEq)] -pub struct View { - pub view_name: String, - pub view_definition: Option, - pub check_option: String, - pub is_updatable: String, - pub is_insertable_into: String, - pub is_trigger_updatable: String, - pub is_trigger_deletable: String, - pub is_trigger_insertable_into: String, -} diff --git a/src/db/view.rs b/src/db/view.rs new file mode 100644 index 0000000..3fc412b --- /dev/null +++ b/src/db/view.rs @@ -0,0 +1,40 @@ +use sqlx::{Error, FromRow, PgConnection}; + +const QUERY: &str = r#" +SELECT + table_catalog, + table_schema, + table_name, + view_definition, + check_option, + is_updatable, + is_insertable_into, + is_trigger_updatable, + is_trigger_deletable, + is_trigger_insertable_into +FROM + information_schema.views +WHERE + table_schema = ANY($1) +ORDER BY + table_name;"#; + +pub async fn query(connection: &mut PgConnection, schema_names: &[String]) -> Result, Error> { + sqlx::query_as(QUERY) + .bind(&schema_names[..]) + .fetch_all(connection).await +} + +#[derive(Debug, Clone, PartialEq, FromRow)] +pub struct View { + pub table_catalog: String, + pub table_schema: String, + pub table_name: String, + pub view_definition: Option, + pub check_option: String, + pub is_updatable: String, + pub is_insertable_into: String, + pub is_trigger_updatable: String, + pub is_trigger_deletable: String, + pub is_trigger_insertable_into: String, +} diff --git a/src/main.rs b/src/main.rs index cc3d54d..58bcf31 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,15 +6,16 @@ mod string; use std::process; use crate::cli::CLI; -fn main() { +#[tokio::main] +async fn main() { let cli = CLI::new(); - let result = cli.run(); + let result = cli.run().await; match result { - Ok(count) => process::exit(count), - Err(err) => { - eprintln!("{}", err); - process::exit(-1); - }, - } + Ok(count) => process::exit(count), + Err(err) => { + eprintln!("{}", err); + process::exit(-1); + }, + } } diff --git a/tests/identical-schemas-verbose/expected.txt b/tests/identical-schemas-verbose/expected.txt index abb7a04..2ed7d48 100644 --- a/tests/identical-schemas-verbose/expected.txt +++ b/tests/identical-schemas-verbose/expected.txt @@ -1,9 +1,36 @@ Schema 'test': unchanged Property 'schema_owner': unchanged at 'postgres' + Property 'default_character_set_catalog': unchanged at '' + Property 'default_character_set_schema': unchanged at '' + Property 'default_character_set_name': unchanged at '' Routine 'new_employee(name IN pg_catalog.text, role_id IN pg_catalog.int4)': unchanged Property 'routine_type': unchanged at 'PROCEDURE' + Property 'module_catalog': unchanged at '' + Property 'module_schema': unchanged at '' + Property 'module_name': unchanged at '' + Property 'udt_catalog': unchanged at '' + Property 'udt_schema': unchanged at '' + Property 'udt_name': unchanged at '' Property 'data_type': unchanged at '' + Property 'character_maximum_length': unchanged at '' + Property 'character_octet_length': unchanged at '' + Property 'character_set_catalog': unchanged at '' + Property 'character_set_schema': unchanged at '' + Property 'character_set_name': unchanged at '' + Property 'collation_catalog': unchanged at '' + Property 'collation_schema': unchanged at '' + Property 'collation_name': unchanged at '' + Property 'numeric_precision': unchanged at '' + Property 'numeric_precision_radix': unchanged at '' + Property 'numeric_scale': unchanged at '' + Property 'datetime_precision': unchanged at '' + Property 'interval_type': unchanged at '' + Property 'interval_precision': unchanged at '' + Property 'type_udt_catalog': unchanged at '' + Property 'type_udt_schema': unchanged at '' Property 'type_udt_name': unchanged at '' + Property 'maximum_cardinality': unchanged at '' + Property 'dtd_identifier': unchanged at '' Property 'routine_body': unchanged at 'SQL' Property 'routine_definition': unchanged at ' INSERT INTO employee(name, role_id) @@ -11,214 +38,675 @@ VALUES (name, role_id); ' Property 'external_name': unchanged at '' Property 'external_language': unchanged at 'SQL' + Property 'parameter_style': unchanged at 'GENERAL' Property 'is_deterministic': unchanged at 'NO' + Property 'sql_data_access': unchanged at 'MODIFIES' Property 'is_null_call': unchanged at '' + Property 'sql_path': unchanged at '' + Property 'schema_level_routine': unchanged at 'YES' + Property 'max_dynamic_result_sets': unchanged at '0' + Property 'is_user_defined_cast': unchanged at '' + Property 'is_implicitly_invocable': unchanged at '' Property 'security_type': unchanged at 'INVOKER' Privilege 'EXECUTE' (postgres->PUBLIC): unchanged Table 'department': unchanged + Property 'table_catalog': unchanged at 'postgres' + Property 'table_schema': unchanged at 'test' + Property 'table_name': unchanged at 'department' Property 'table_type': unchanged at 'BASE TABLE' + Property 'self_referencing_column_name': unchanged at '' + Property 'reference_generation': unchanged at '' + Property 'user_defined_type_catalog': unchanged at '' + Property 'user_defined_type_schema': unchanged at '' + Property 'user_defined_type_name': unchanged at '' Property 'is_insertable_into': unchanged at 'YES' + Property 'is_typed': unchanged at 'NO' + Property 'commit_action': unchanged at '' Column 'id': unchanged Property 'column_default': unchanged at '' Property 'is_nullable': unchanged at 'NO' Property 'data_type': unchanged at 'integer' Property 'character_maximum_length': unchanged at '' + Property 'character_octet_length': unchanged at '' Property 'numeric_precision': unchanged at '32' + Property 'numeric_precision_radix': unchanged at '2' Property 'numeric_scale': unchanged at '0' Property 'datetime_precision': unchanged at '' + Property 'interval_type': unchanged at '' + Property 'interval_precision': unchanged at '' + Property 'character_set_catalog': unchanged at '' + Property 'character_set_schema': unchanged at '' + Property 'character_set_name': unchanged at '' + Property 'collation_catalog': unchanged at '' + Property 'collation_schema': unchanged at '' + Property 'collation_name': unchanged at '' + Property 'domain_catalog': unchanged at '' + Property 'domain_schema': unchanged at '' + Property 'domain_name': unchanged at '' + Property 'udt_catalog': unchanged at 'postgres' + Property 'udt_schema': unchanged at 'pg_catalog' + Property 'udt_name': unchanged at 'int4' + Property 'scope_catalog': unchanged at '' + Property 'scope_schema': unchanged at '' + Property 'scope_name': unchanged at '' + Property 'maximum_cardinality': unchanged at '' + Property 'dtd_identifier': unchanged at '' + Property 'is_self_referencing': unchanged at 'NO' Property 'is_identity': unchanged at 'YES' Property 'identity_generation': unchanged at 'ALWAYS' + Property 'identity_start': unchanged at '1' + Property 'identity_increment': unchanged at '1' + Property 'identity_maximum': unchanged at '2147483647' + Property 'identity_minimum': unchanged at '1' + Property 'identity_cycle': unchanged at 'NO' Property 'is_generated': unchanged at 'NEVER' Property 'generation_expression': unchanged at '' Property 'is_updatable': unchanged at 'YES' Property 'ordinal_position': unchanged at '1' + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged Column 'name': unchanged Property 'column_default': unchanged at '' Property 'is_nullable': unchanged at 'NO' Property 'data_type': unchanged at 'text' Property 'character_maximum_length': unchanged at '' + Property 'character_octet_length': unchanged at '1073741824' Property 'numeric_precision': unchanged at '' + Property 'numeric_precision_radix': unchanged at '' Property 'numeric_scale': unchanged at '' Property 'datetime_precision': unchanged at '' + Property 'interval_type': unchanged at '' + Property 'interval_precision': unchanged at '' + Property 'character_set_catalog': unchanged at '' + Property 'character_set_schema': unchanged at '' + Property 'character_set_name': unchanged at '' + Property 'collation_catalog': unchanged at '' + Property 'collation_schema': unchanged at '' + Property 'collation_name': unchanged at '' + Property 'domain_catalog': unchanged at '' + Property 'domain_schema': unchanged at '' + Property 'domain_name': unchanged at '' + Property 'udt_catalog': unchanged at 'postgres' + Property 'udt_schema': unchanged at 'pg_catalog' + Property 'udt_name': unchanged at 'text' + Property 'scope_catalog': unchanged at '' + Property 'scope_schema': unchanged at '' + Property 'scope_name': unchanged at '' + Property 'maximum_cardinality': unchanged at '' + Property 'dtd_identifier': unchanged at '' + Property 'is_self_referencing': unchanged at 'NO' Property 'is_identity': unchanged at 'NO' Property 'identity_generation': unchanged at '' + Property 'identity_start': unchanged at '' + Property 'identity_increment': unchanged at '' + Property 'identity_maximum': unchanged at '' + Property 'identity_minimum': unchanged at '' + Property 'identity_cycle': unchanged at 'NO' Property 'is_generated': unchanged at 'NEVER' Property 'generation_expression': unchanged at '' Property 'is_updatable': unchanged at 'YES' Property 'ordinal_position': unchanged at '2' + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged + Privilege 'DELETE' (postgres->postgres): unchanged + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'TRIGGER' (postgres->postgres): unchanged + Privilege 'TRUNCATE' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged Constraint 'department_name_key': unchanged + Property 'constraint_catalog': unchanged at 'postgres' + Property 'constraint_schema': unchanged at 'test' + Property 'table_name': unchanged at 'department' Property 'constraint_type': unchanged at 'UNIQUE' Property 'is_deferrable': unchanged at 'NO' Property 'initially_deferred': unchanged at 'NO' + Property 'enforced': unchanged at 'YES' Property 'nulls_distinct': unchanged at 'YES' Constraint 'department_pkey': unchanged + Property 'constraint_catalog': unchanged at 'postgres' + Property 'constraint_schema': unchanged at 'test' + Property 'table_name': unchanged at 'department' Property 'constraint_type': unchanged at 'PRIMARY KEY' Property 'is_deferrable': unchanged at 'NO' Property 'initially_deferred': unchanged at 'NO' + Property 'enforced': unchanged at 'YES' Property 'nulls_distinct': unchanged at '' Table 'department_employee': unchanged + Property 'table_catalog': unchanged at 'postgres' + Property 'table_schema': unchanged at 'test' + Property 'table_name': unchanged at 'department_employee' Property 'table_type': unchanged at 'BASE TABLE' + Property 'self_referencing_column_name': unchanged at '' + Property 'reference_generation': unchanged at '' + Property 'user_defined_type_catalog': unchanged at '' + Property 'user_defined_type_schema': unchanged at '' + Property 'user_defined_type_name': unchanged at '' Property 'is_insertable_into': unchanged at 'YES' + Property 'is_typed': unchanged at 'NO' + Property 'commit_action': unchanged at '' Column 'department_id': unchanged Property 'column_default': unchanged at '' Property 'is_nullable': unchanged at 'NO' Property 'data_type': unchanged at 'integer' Property 'character_maximum_length': unchanged at '' + Property 'character_octet_length': unchanged at '' Property 'numeric_precision': unchanged at '32' + Property 'numeric_precision_radix': unchanged at '2' Property 'numeric_scale': unchanged at '0' Property 'datetime_precision': unchanged at '' + Property 'interval_type': unchanged at '' + Property 'interval_precision': unchanged at '' + Property 'character_set_catalog': unchanged at '' + Property 'character_set_schema': unchanged at '' + Property 'character_set_name': unchanged at '' + Property 'collation_catalog': unchanged at '' + Property 'collation_schema': unchanged at '' + Property 'collation_name': unchanged at '' + Property 'domain_catalog': unchanged at '' + Property 'domain_schema': unchanged at '' + Property 'domain_name': unchanged at '' + Property 'udt_catalog': unchanged at 'postgres' + Property 'udt_schema': unchanged at 'pg_catalog' + Property 'udt_name': unchanged at 'int4' + Property 'scope_catalog': unchanged at '' + Property 'scope_schema': unchanged at '' + Property 'scope_name': unchanged at '' + Property 'maximum_cardinality': unchanged at '' + Property 'dtd_identifier': unchanged at '' + Property 'is_self_referencing': unchanged at 'NO' Property 'is_identity': unchanged at 'NO' Property 'identity_generation': unchanged at '' + Property 'identity_start': unchanged at '' + Property 'identity_increment': unchanged at '' + Property 'identity_maximum': unchanged at '' + Property 'identity_minimum': unchanged at '' + Property 'identity_cycle': unchanged at 'NO' Property 'is_generated': unchanged at 'NEVER' Property 'generation_expression': unchanged at '' Property 'is_updatable': unchanged at 'YES' Property 'ordinal_position': unchanged at '1' + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged Column 'employee_id': unchanged Property 'column_default': unchanged at '' Property 'is_nullable': unchanged at 'NO' Property 'data_type': unchanged at 'integer' Property 'character_maximum_length': unchanged at '' + Property 'character_octet_length': unchanged at '' Property 'numeric_precision': unchanged at '32' + Property 'numeric_precision_radix': unchanged at '2' Property 'numeric_scale': unchanged at '0' Property 'datetime_precision': unchanged at '' + Property 'interval_type': unchanged at '' + Property 'interval_precision': unchanged at '' + Property 'character_set_catalog': unchanged at '' + Property 'character_set_schema': unchanged at '' + Property 'character_set_name': unchanged at '' + Property 'collation_catalog': unchanged at '' + Property 'collation_schema': unchanged at '' + Property 'collation_name': unchanged at '' + Property 'domain_catalog': unchanged at '' + Property 'domain_schema': unchanged at '' + Property 'domain_name': unchanged at '' + Property 'udt_catalog': unchanged at 'postgres' + Property 'udt_schema': unchanged at 'pg_catalog' + Property 'udt_name': unchanged at 'int4' + Property 'scope_catalog': unchanged at '' + Property 'scope_schema': unchanged at '' + Property 'scope_name': unchanged at '' + Property 'maximum_cardinality': unchanged at '' + Property 'dtd_identifier': unchanged at '' + Property 'is_self_referencing': unchanged at 'NO' Property 'is_identity': unchanged at 'NO' Property 'identity_generation': unchanged at '' + Property 'identity_start': unchanged at '' + Property 'identity_increment': unchanged at '' + Property 'identity_maximum': unchanged at '' + Property 'identity_minimum': unchanged at '' + Property 'identity_cycle': unchanged at 'NO' Property 'is_generated': unchanged at 'NEVER' Property 'generation_expression': unchanged at '' Property 'is_updatable': unchanged at 'YES' Property 'ordinal_position': unchanged at '2' + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged + Privilege 'DELETE' (postgres->postgres): unchanged + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'TRIGGER' (postgres->postgres): unchanged + Privilege 'TRUNCATE' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged Constraint 'fk_department_id': unchanged + Property 'constraint_catalog': unchanged at 'postgres' + Property 'constraint_schema': unchanged at 'test' + Property 'table_name': unchanged at 'department_employee' Property 'constraint_type': unchanged at 'FOREIGN KEY' Property 'is_deferrable': unchanged at 'NO' Property 'initially_deferred': unchanged at 'NO' + Property 'enforced': unchanged at 'YES' Property 'nulls_distinct': unchanged at '' Constraint 'fk_employee_id': unchanged + Property 'constraint_catalog': unchanged at 'postgres' + Property 'constraint_schema': unchanged at 'test' + Property 'table_name': unchanged at 'department_employee' Property 'constraint_type': unchanged at 'FOREIGN KEY' Property 'is_deferrable': unchanged at 'NO' Property 'initially_deferred': unchanged at 'NO' + Property 'enforced': unchanged at 'YES' Property 'nulls_distinct': unchanged at '' Table 'employee': unchanged + Property 'table_catalog': unchanged at 'postgres' + Property 'table_schema': unchanged at 'test' + Property 'table_name': unchanged at 'employee' Property 'table_type': unchanged at 'BASE TABLE' + Property 'self_referencing_column_name': unchanged at '' + Property 'reference_generation': unchanged at '' + Property 'user_defined_type_catalog': unchanged at '' + Property 'user_defined_type_schema': unchanged at '' + Property 'user_defined_type_name': unchanged at '' Property 'is_insertable_into': unchanged at 'YES' + Property 'is_typed': unchanged at 'NO' + Property 'commit_action': unchanged at '' Column 'id': unchanged Property 'column_default': unchanged at '' Property 'is_nullable': unchanged at 'NO' Property 'data_type': unchanged at 'integer' Property 'character_maximum_length': unchanged at '' + Property 'character_octet_length': unchanged at '' Property 'numeric_precision': unchanged at '32' + Property 'numeric_precision_radix': unchanged at '2' Property 'numeric_scale': unchanged at '0' Property 'datetime_precision': unchanged at '' + Property 'interval_type': unchanged at '' + Property 'interval_precision': unchanged at '' + Property 'character_set_catalog': unchanged at '' + Property 'character_set_schema': unchanged at '' + Property 'character_set_name': unchanged at '' + Property 'collation_catalog': unchanged at '' + Property 'collation_schema': unchanged at '' + Property 'collation_name': unchanged at '' + Property 'domain_catalog': unchanged at '' + Property 'domain_schema': unchanged at '' + Property 'domain_name': unchanged at '' + Property 'udt_catalog': unchanged at 'postgres' + Property 'udt_schema': unchanged at 'pg_catalog' + Property 'udt_name': unchanged at 'int4' + Property 'scope_catalog': unchanged at '' + Property 'scope_schema': unchanged at '' + Property 'scope_name': unchanged at '' + Property 'maximum_cardinality': unchanged at '' + Property 'dtd_identifier': unchanged at '' + Property 'is_self_referencing': unchanged at 'NO' Property 'is_identity': unchanged at 'YES' Property 'identity_generation': unchanged at 'ALWAYS' + Property 'identity_start': unchanged at '1' + Property 'identity_increment': unchanged at '1' + Property 'identity_maximum': unchanged at '2147483647' + Property 'identity_minimum': unchanged at '1' + Property 'identity_cycle': unchanged at 'NO' Property 'is_generated': unchanged at 'NEVER' Property 'generation_expression': unchanged at '' Property 'is_updatable': unchanged at 'YES' Property 'ordinal_position': unchanged at '1' + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged Column 'name': unchanged Property 'column_default': unchanged at '' Property 'is_nullable': unchanged at 'NO' Property 'data_type': unchanged at 'text' Property 'character_maximum_length': unchanged at '' + Property 'character_octet_length': unchanged at '1073741824' Property 'numeric_precision': unchanged at '' + Property 'numeric_precision_radix': unchanged at '' Property 'numeric_scale': unchanged at '' Property 'datetime_precision': unchanged at '' + Property 'interval_type': unchanged at '' + Property 'interval_precision': unchanged at '' + Property 'character_set_catalog': unchanged at '' + Property 'character_set_schema': unchanged at '' + Property 'character_set_name': unchanged at '' + Property 'collation_catalog': unchanged at '' + Property 'collation_schema': unchanged at '' + Property 'collation_name': unchanged at '' + Property 'domain_catalog': unchanged at '' + Property 'domain_schema': unchanged at '' + Property 'domain_name': unchanged at '' + Property 'udt_catalog': unchanged at 'postgres' + Property 'udt_schema': unchanged at 'pg_catalog' + Property 'udt_name': unchanged at 'text' + Property 'scope_catalog': unchanged at '' + Property 'scope_schema': unchanged at '' + Property 'scope_name': unchanged at '' + Property 'maximum_cardinality': unchanged at '' + Property 'dtd_identifier': unchanged at '' + Property 'is_self_referencing': unchanged at 'NO' Property 'is_identity': unchanged at 'NO' Property 'identity_generation': unchanged at '' + Property 'identity_start': unchanged at '' + Property 'identity_increment': unchanged at '' + Property 'identity_maximum': unchanged at '' + Property 'identity_minimum': unchanged at '' + Property 'identity_cycle': unchanged at 'NO' Property 'is_generated': unchanged at 'NEVER' Property 'generation_expression': unchanged at '' Property 'is_updatable': unchanged at 'YES' Property 'ordinal_position': unchanged at '2' + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged Column 'role_id': unchanged Property 'column_default': unchanged at '' Property 'is_nullable': unchanged at 'NO' Property 'data_type': unchanged at 'integer' Property 'character_maximum_length': unchanged at '' + Property 'character_octet_length': unchanged at '' Property 'numeric_precision': unchanged at '32' + Property 'numeric_precision_radix': unchanged at '2' Property 'numeric_scale': unchanged at '0' Property 'datetime_precision': unchanged at '' + Property 'interval_type': unchanged at '' + Property 'interval_precision': unchanged at '' + Property 'character_set_catalog': unchanged at '' + Property 'character_set_schema': unchanged at '' + Property 'character_set_name': unchanged at '' + Property 'collation_catalog': unchanged at '' + Property 'collation_schema': unchanged at '' + Property 'collation_name': unchanged at '' + Property 'domain_catalog': unchanged at '' + Property 'domain_schema': unchanged at '' + Property 'domain_name': unchanged at '' + Property 'udt_catalog': unchanged at 'postgres' + Property 'udt_schema': unchanged at 'pg_catalog' + Property 'udt_name': unchanged at 'int4' + Property 'scope_catalog': unchanged at '' + Property 'scope_schema': unchanged at '' + Property 'scope_name': unchanged at '' + Property 'maximum_cardinality': unchanged at '' + Property 'dtd_identifier': unchanged at '' + Property 'is_self_referencing': unchanged at 'NO' Property 'is_identity': unchanged at 'NO' Property 'identity_generation': unchanged at '' + Property 'identity_start': unchanged at '' + Property 'identity_increment': unchanged at '' + Property 'identity_maximum': unchanged at '' + Property 'identity_minimum': unchanged at '' + Property 'identity_cycle': unchanged at 'NO' Property 'is_generated': unchanged at 'NEVER' Property 'generation_expression': unchanged at '' Property 'is_updatable': unchanged at 'YES' Property 'ordinal_position': unchanged at '3' + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged + Privilege 'DELETE' (postgres->postgres): unchanged + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'TRIGGER' (postgres->postgres): unchanged + Privilege 'TRUNCATE' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged Constraint 'employee_pkey': unchanged + Property 'constraint_catalog': unchanged at 'postgres' + Property 'constraint_schema': unchanged at 'test' + Property 'table_name': unchanged at 'employee' Property 'constraint_type': unchanged at 'PRIMARY KEY' Property 'is_deferrable': unchanged at 'NO' Property 'initially_deferred': unchanged at 'NO' + Property 'enforced': unchanged at 'YES' Property 'nulls_distinct': unchanged at '' Table 'employee_role': unchanged + Property 'table_catalog': unchanged at 'postgres' + Property 'table_schema': unchanged at 'test' + Property 'table_name': unchanged at 'employee_role' Property 'table_type': unchanged at 'BASE TABLE' + Property 'self_referencing_column_name': unchanged at '' + Property 'reference_generation': unchanged at '' + Property 'user_defined_type_catalog': unchanged at '' + Property 'user_defined_type_schema': unchanged at '' + Property 'user_defined_type_name': unchanged at '' Property 'is_insertable_into': unchanged at 'YES' + Property 'is_typed': unchanged at 'NO' + Property 'commit_action': unchanged at '' Column 'id': unchanged Property 'column_default': unchanged at '' Property 'is_nullable': unchanged at 'NO' Property 'data_type': unchanged at 'integer' Property 'character_maximum_length': unchanged at '' + Property 'character_octet_length': unchanged at '' Property 'numeric_precision': unchanged at '32' + Property 'numeric_precision_radix': unchanged at '2' Property 'numeric_scale': unchanged at '0' Property 'datetime_precision': unchanged at '' + Property 'interval_type': unchanged at '' + Property 'interval_precision': unchanged at '' + Property 'character_set_catalog': unchanged at '' + Property 'character_set_schema': unchanged at '' + Property 'character_set_name': unchanged at '' + Property 'collation_catalog': unchanged at '' + Property 'collation_schema': unchanged at '' + Property 'collation_name': unchanged at '' + Property 'domain_catalog': unchanged at '' + Property 'domain_schema': unchanged at '' + Property 'domain_name': unchanged at '' + Property 'udt_catalog': unchanged at 'postgres' + Property 'udt_schema': unchanged at 'pg_catalog' + Property 'udt_name': unchanged at 'int4' + Property 'scope_catalog': unchanged at '' + Property 'scope_schema': unchanged at '' + Property 'scope_name': unchanged at '' + Property 'maximum_cardinality': unchanged at '' + Property 'dtd_identifier': unchanged at '' + Property 'is_self_referencing': unchanged at 'NO' Property 'is_identity': unchanged at 'YES' Property 'identity_generation': unchanged at 'ALWAYS' + Property 'identity_start': unchanged at '1' + Property 'identity_increment': unchanged at '1' + Property 'identity_maximum': unchanged at '2147483647' + Property 'identity_minimum': unchanged at '1' + Property 'identity_cycle': unchanged at 'NO' Property 'is_generated': unchanged at 'NEVER' Property 'generation_expression': unchanged at '' Property 'is_updatable': unchanged at 'YES' Property 'ordinal_position': unchanged at '1' + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged Column 'name': unchanged Property 'column_default': unchanged at '' Property 'is_nullable': unchanged at 'NO' Property 'data_type': unchanged at 'text' Property 'character_maximum_length': unchanged at '' + Property 'character_octet_length': unchanged at '1073741824' Property 'numeric_precision': unchanged at '' + Property 'numeric_precision_radix': unchanged at '' Property 'numeric_scale': unchanged at '' Property 'datetime_precision': unchanged at '' + Property 'interval_type': unchanged at '' + Property 'interval_precision': unchanged at '' + Property 'character_set_catalog': unchanged at '' + Property 'character_set_schema': unchanged at '' + Property 'character_set_name': unchanged at '' + Property 'collation_catalog': unchanged at '' + Property 'collation_schema': unchanged at '' + Property 'collation_name': unchanged at '' + Property 'domain_catalog': unchanged at '' + Property 'domain_schema': unchanged at '' + Property 'domain_name': unchanged at '' + Property 'udt_catalog': unchanged at 'postgres' + Property 'udt_schema': unchanged at 'pg_catalog' + Property 'udt_name': unchanged at 'text' + Property 'scope_catalog': unchanged at '' + Property 'scope_schema': unchanged at '' + Property 'scope_name': unchanged at '' + Property 'maximum_cardinality': unchanged at '' + Property 'dtd_identifier': unchanged at '' + Property 'is_self_referencing': unchanged at 'NO' Property 'is_identity': unchanged at 'NO' Property 'identity_generation': unchanged at '' + Property 'identity_start': unchanged at '' + Property 'identity_increment': unchanged at '' + Property 'identity_maximum': unchanged at '' + Property 'identity_minimum': unchanged at '' + Property 'identity_cycle': unchanged at 'NO' Property 'is_generated': unchanged at 'NEVER' Property 'generation_expression': unchanged at '' Property 'is_updatable': unchanged at 'YES' Property 'ordinal_position': unchanged at '2' + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged + Privilege 'DELETE' (postgres->postgres): unchanged + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'TRIGGER' (postgres->postgres): unchanged + Privilege 'TRUNCATE' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged Constraint 'employee_role_name_key': unchanged + Property 'constraint_catalog': unchanged at 'postgres' + Property 'constraint_schema': unchanged at 'test' + Property 'table_name': unchanged at 'employee_role' Property 'constraint_type': unchanged at 'UNIQUE' Property 'is_deferrable': unchanged at 'NO' Property 'initially_deferred': unchanged at 'NO' + Property 'enforced': unchanged at 'YES' Property 'nulls_distinct': unchanged at 'YES' Constraint 'employee_role_pkey': unchanged + Property 'constraint_catalog': unchanged at 'postgres' + Property 'constraint_schema': unchanged at 'test' + Property 'table_name': unchanged at 'employee_role' Property 'constraint_type': unchanged at 'PRIMARY KEY' Property 'is_deferrable': unchanged at 'NO' Property 'initially_deferred': unchanged at 'NO' + Property 'enforced': unchanged at 'YES' Property 'nulls_distinct': unchanged at '' Table 'leads': unchanged + Property 'table_catalog': unchanged at 'postgres' + Property 'table_schema': unchanged at 'test' + Property 'table_name': unchanged at 'leads' Property 'table_type': unchanged at 'VIEW' + Property 'self_referencing_column_name': unchanged at '' + Property 'reference_generation': unchanged at '' + Property 'user_defined_type_catalog': unchanged at '' + Property 'user_defined_type_schema': unchanged at '' + Property 'user_defined_type_name': unchanged at '' Property 'is_insertable_into': unchanged at 'NO' + Property 'is_typed': unchanged at 'NO' + Property 'commit_action': unchanged at '' Column 'id': unchanged Property 'column_default': unchanged at '' Property 'is_nullable': unchanged at 'YES' Property 'data_type': unchanged at 'integer' Property 'character_maximum_length': unchanged at '' + Property 'character_octet_length': unchanged at '' Property 'numeric_precision': unchanged at '32' + Property 'numeric_precision_radix': unchanged at '2' Property 'numeric_scale': unchanged at '0' Property 'datetime_precision': unchanged at '' + Property 'interval_type': unchanged at '' + Property 'interval_precision': unchanged at '' + Property 'character_set_catalog': unchanged at '' + Property 'character_set_schema': unchanged at '' + Property 'character_set_name': unchanged at '' + Property 'collation_catalog': unchanged at '' + Property 'collation_schema': unchanged at '' + Property 'collation_name': unchanged at '' + Property 'domain_catalog': unchanged at '' + Property 'domain_schema': unchanged at '' + Property 'domain_name': unchanged at '' + Property 'udt_catalog': unchanged at 'postgres' + Property 'udt_schema': unchanged at 'pg_catalog' + Property 'udt_name': unchanged at 'int4' + Property 'scope_catalog': unchanged at '' + Property 'scope_schema': unchanged at '' + Property 'scope_name': unchanged at '' + Property 'maximum_cardinality': unchanged at '' + Property 'dtd_identifier': unchanged at '' + Property 'is_self_referencing': unchanged at 'NO' Property 'is_identity': unchanged at 'NO' Property 'identity_generation': unchanged at '' + Property 'identity_start': unchanged at '' + Property 'identity_increment': unchanged at '' + Property 'identity_maximum': unchanged at '' + Property 'identity_minimum': unchanged at '' + Property 'identity_cycle': unchanged at 'NO' Property 'is_generated': unchanged at 'NEVER' Property 'generation_expression': unchanged at '' Property 'is_updatable': unchanged at 'NO' Property 'ordinal_position': unchanged at '1' + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged Column 'name': unchanged Property 'column_default': unchanged at '' Property 'is_nullable': unchanged at 'YES' Property 'data_type': unchanged at 'text' Property 'character_maximum_length': unchanged at '' + Property 'character_octet_length': unchanged at '1073741824' Property 'numeric_precision': unchanged at '' + Property 'numeric_precision_radix': unchanged at '' Property 'numeric_scale': unchanged at '' Property 'datetime_precision': unchanged at '' + Property 'interval_type': unchanged at '' + Property 'interval_precision': unchanged at '' + Property 'character_set_catalog': unchanged at '' + Property 'character_set_schema': unchanged at '' + Property 'character_set_name': unchanged at '' + Property 'collation_catalog': unchanged at '' + Property 'collation_schema': unchanged at '' + Property 'collation_name': unchanged at '' + Property 'domain_catalog': unchanged at '' + Property 'domain_schema': unchanged at '' + Property 'domain_name': unchanged at '' + Property 'udt_catalog': unchanged at 'postgres' + Property 'udt_schema': unchanged at 'pg_catalog' + Property 'udt_name': unchanged at 'text' + Property 'scope_catalog': unchanged at '' + Property 'scope_schema': unchanged at '' + Property 'scope_name': unchanged at '' + Property 'maximum_cardinality': unchanged at '' + Property 'dtd_identifier': unchanged at '' + Property 'is_self_referencing': unchanged at 'NO' Property 'is_identity': unchanged at 'NO' Property 'identity_generation': unchanged at '' + Property 'identity_start': unchanged at '' + Property 'identity_increment': unchanged at '' + Property 'identity_maximum': unchanged at '' + Property 'identity_minimum': unchanged at '' + Property 'identity_cycle': unchanged at 'NO' Property 'is_generated': unchanged at 'NEVER' Property 'generation_expression': unchanged at '' Property 'is_updatable': unchanged at 'NO' Property 'ordinal_position': unchanged at '2' + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged + Privilege 'DELETE' (postgres->postgres): unchanged + Privilege 'INSERT' (postgres->postgres): unchanged + Privilege 'REFERENCES' (postgres->postgres): unchanged + Privilege 'SELECT' (postgres->postgres): unchanged + Privilege 'TRIGGER' (postgres->postgres): unchanged + Privilege 'TRUNCATE' (postgres->postgres): unchanged + Privilege 'UPDATE' (postgres->postgres): unchanged View 'leads': unchanged Property 'view_definition': unchanged at ' SELECT e.id, e.name diff --git a/tests/main/expected.txt b/tests/main/expected.txt index d02f6ac..3536d55 100644 --- a/tests/main/expected.txt +++ b/tests/main/expected.txt @@ -41,8 +41,11 @@ VALUES (a, b); Column 'b': Property 'is_nullable': changed from 'NO' to 'YES' Property 'data_type': changed from 'text' to 'integer' + Property 'character_octet_length': changed from '1073741824' to '' Property 'numeric_precision': changed from '' to '32' + Property 'numeric_precision_radix': changed from '' to '2' Property 'numeric_scale': changed from '' to '0' + Property 'udt_name': changed from 'text' to 'int4' Table 'column_ordering_changed': Column 'a': Property 'ordinal_position': changed from '1' to '2' @@ -67,6 +70,10 @@ VALUES (a, b); Property 'is_nullable': changed from 'NO' to 'YES' Property 'is_identity': changed from 'YES' to 'NO' Property 'identity_generation': changed from 'ALWAYS' to '' + Property 'identity_start': changed from '1' to '' + Property 'identity_increment': changed from '1' to '' + Property 'identity_maximum': changed from '2147483647' to '' + Property 'identity_minimum': changed from '1' to '' Property 'is_updatable': changed from 'YES' to 'NO' Column 'b': Property 'is_nullable': changed from 'NO' to 'YES' @@ -133,5 +140,4 @@ VALUES (a, b); id AS name, 7 AS seven FROM test.view_table - WHERE (a > 10) - ORDER BY id;' + WHERE (a > 10);' diff --git a/tests/main/right.sql b/tests/main/right.sql index be8aa1b..e816e62 100644 --- a/tests/main/right.sql +++ b/tests/main/right.sql @@ -328,8 +328,7 @@ AS SELECT id, a CREATE VIEW view_changed (id, name) AS SELECT a, id, 7 as seven FROM view_table - WHERE a > 10 - ORDER BY id; + WHERE a > 10; -- view removed