Repository: njord-rs/njord (language: rust)

Issue:
Add MySQL support



Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index d65e65b0..fca5b2ea 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -17,6 +17,23 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
+[[package]]
+name = "ahash"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
 [[package]]
 name = "ahash"
 version = "0.8.6"
@@ -29,12 +46,27 @@ dependencies = [
  "zerocopy",
 ]
 
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "allocator-api2"
 version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
 
+[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
 [[package]]
 name = "autocfg"
 version = "1.1.0"
@@ -51,17 +83,55 @@ dependencies = [
  "cc",
  "cfg-if",
  "libc",
- "miniz_oxide",
+ "miniz_oxide 0.7.2",
  "object",
  "rustc-demangle",
 ]
 
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
 [[package]]
 name = "base64"
 version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
 
+[[package]]
+name = "bigdecimal"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d712318a27c7150326677b321a5fa91b55f6d9034ffd67f20319e147d40cee"
+dependencies = [
+ "autocfg",
+ "libm",
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "bindgen"
+version = "0.59.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
+dependencies = [
+ "bitflags 1.3.2",
+ "cexpr",
+ "clang-sys",
+ "lazy_static",
+ "lazycell",
+ "peeking_take_while",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+]
+
 [[package]]
 name = "bitflags"
 version = "1.3.2"
@@ -74,12 +144,100 @@ version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
 
+[[package]]
+name = "bitvec"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
+dependencies = [
+ "funty",
+ "radium",
+ "tap",
+ "wyz",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "borsh"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed"
+dependencies = [
+ "borsh-derive",
+ "cfg_aliases",
+]
+
+[[package]]
+name = "borsh-derive"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b"
+dependencies = [
+ "once_cell",
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.79",
+ "syn_derive",
+]
+
+[[package]]
+name = "btoi"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd6407f73a9b8b6162d8a2ef999fe6afd7cc15902ebf42c5cd296addf17e0ad"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "bufstream"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"
+
 [[package]]
 name = "bumpalo"
 version = "3.15.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
 
+[[package]]
+name = "bytecheck"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2"
+dependencies = [
+ "bytecheck_derive",
+ "ptr_meta",
+ "simdutf8",
+]
+
+[[package]]
+name = "bytecheck_derive"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
 [[package]]
 name = "bytes"
 version = "1.6.0"
@@ -91,6 +249,19 @@ name = "cc"
 version = "1.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
+dependencies = [
+ "jobserver",
+ "libc",
+]
+
+[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
+]
 
 [[package]]
 name = "cfg-if"
@@ -98,6 +269,32 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "clang-sys"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading",
+]
+
+[[package]]
+name = "cmake"
+version = "0.1.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "core-foundation"
 version = "0.9.4"
@@ -114,6 +311,155 @@ version = "0.8.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
 
+[[package]]
+name = "cpufeatures"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-epoch",
+ "crossbeam-queue",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "darling"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 2.0.79",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 2.0.79",
+]
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "derive_utils"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65f152f4b8559c4da5d574bafc7af85454d706b4c5fe8b530d508cacbb6807ea"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.79",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
 [[package]]
 name = "encoding_rs"
 version = "0.8.33"
@@ -157,12 +503,29 @@ version = "2.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
 
+[[package]]
+name = "flate2"
+version = "1.0.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
+dependencies = [
+ "crc32fast",
+ "libz-sys",
+ "miniz_oxide 0.8.0",
+]
+
 [[package]]
 name = "fnv"
 version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
+[[package]]
+name = "foldhash"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
+
 [[package]]
 name = "foreign-types"
 version = "0.3.2"
@@ -187,6 +550,68 @@ dependencies = [
  "percent-encoding",
 ]
 
+[[package]]
+name = "frunk"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "874b6a17738fc273ec753618bac60ddaeac48cb1d7684c3e7bd472e57a28b817"
+dependencies = [
+ "frunk_core",
+ "frunk_derives",
+ "frunk_proc_macros",
+ "serde",
+]
+
+[[package]]
+name = "frunk_core"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3529a07095650187788833d585c219761114005d5976185760cf794d265b6a5c"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "frunk_derives"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e99b8b3c28ae0e84b604c75f721c21dc77afb3706076af5e8216d15fd1deaae3"
+dependencies = [
+ "frunk_proc_macro_helpers",
+ "quote",
+ "syn 2.0.79",
+]
+
+[[package]]
+name = "frunk_proc_macro_helpers"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05a956ef36c377977e512e227dcad20f68c2786ac7a54dacece3746046fea5ce"
+dependencies = [
+ "frunk_core",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.79",
+]
+
+[[package]]
+name = "frunk_proc_macros"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67e86c2c9183662713fea27ea527aad20fb15fee635a71081ff91bf93df4dc51"
+dependencies = [
+ "frunk_core",
+ "frunk_proc_macro_helpers",
+ "quote",
+ "syn 2.0.79",
+]
+
+[[package]]
+name = "funty"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
+
 [[package]]
 name = "futures-channel"
 version = "0.3.30"
@@ -226,6 +651,16 @@ dependencies = [
  "pin-utils",
 ]
 
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
 [[package]]
 name = "getrandom"
 version = "0.2.12"
@@ -243,6 +678,12 @@ version = "0.28.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
 
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
 [[package]]
 name = "h2"
 version = "0.4.4"
@@ -262,25 +703,51 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash 0.7.8",
+]
+
 [[package]]
 name = "hashbrown"
 version = "0.14.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
 dependencies = [
- "ahash",
+ "ahash 0.8.6",
  "allocator-api2",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
+dependencies = [
+ "allocator-api2",
+ "equivalent",
+ "foldhash",
+]
+
 [[package]]
 name = "hashlink"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "692eaaf7f7607518dd3cef090f1474b61edc5301d8012f09579920df68b725ee"
 dependencies = [
- "hashbrown",
+ "hashbrown 0.14.3",
 ]
 
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
 [[package]]
 name = "hermit-abi"
 version = "0.3.9"
@@ -400,6 +867,12 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
 [[package]]
 name = "idna"
 version = "0.5.0"
@@ -417,7 +890,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
 dependencies = [
  "equivalent",
- "hashbrown",
+ "hashbrown 0.14.3",
+]
+
+[[package]]
+name = "io-enum"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53b53d712d99a73eec59ee5e4fe6057f8052142d38eeafbbffcb06b36d738a6e"
+dependencies = [
+ "derive_utils",
 ]
 
 [[package]]
@@ -432,6 +914,15 @@ version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
 
+[[package]]
+name = "jobserver"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "js-sys"
 version = "0.3.69"
@@ -447,12 +938,34 @@ version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
+[[package]]
+name = "lazycell"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+
 [[package]]
 name = "libc"
 version = "0.2.151"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
 
+[[package]]
+name = "libloading"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
+dependencies = [
+ "cfg-if",
+ "windows-targets 0.52.6",
+]
+
+[[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"
@@ -464,6 +977,17 @@ dependencies = [
  "vcpkg",
 ]
 
+[[package]]
+name = "libz-sys"
+version = "1.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472"
+dependencies = [
+ "cc",
+ "pkg-config",
+ "vcpkg",
+]
+
 [[package]]
 name = "linux-raw-sys"
 version = "0.4.13"
@@ -486,6 +1010,15 @@ version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
 
+[[package]]
+name = "lru"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
+dependencies = [
+ "hashbrown 0.15.0",
+]
+
 [[package]]
 name = "memchr"
 version = "2.7.1"
@@ -498,6 +1031,12 @@ version = "0.3.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
 
+[[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.2"
@@ -507,6 +1046,15 @@ dependencies = [
  "adler",
 ]
 
+[[package]]
+name = "miniz_oxide"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
+dependencies = [
+ "adler2",
+]
+
 [[package]]
 name = "mio"
 version = "1.0.2"
@@ -519,6 +1067,109 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "mysql"
+version = "0.1.0"
+dependencies = [
+ "njord",
+ "njord_derive",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "tokio",
+]
+
+[[package]]
+name = "mysql"
+version = "25.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6ad644efb545e459029b1ffa7c969d830975bd76906820913247620df10050b"
+dependencies = [
+ "bufstream",
+ "bytes",
+ "crossbeam",
+ "flate2",
+ "io-enum",
+ "libc",
+ "lru",
+ "mysql_common",
+ "named_pipe",
+ "native-tls",
+ "pem",
+ "percent-encoding",
+ "serde",
+ "serde_json",
+ "socket2",
+ "twox-hash",
+ "url",
+]
+
+[[package]]
+name = "mysql-common-derive"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afe0450cc9344afff34915f8328600ab5ae19260802a334d0f72d2d5bdda3bfe"
+dependencies = [
+ "darling",
+ "heck",
+ "num-bigint",
+ "proc-macro-crate",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.79",
+ "termcolor",
+ "thiserror",
+]
+
+[[package]]
+name = "mysql_common"
+version = "0.32.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "478b0ff3f7d67b79da2b96f56f334431aef65e15ba4b29dd74a4236e29582bdc"
+dependencies = [
+ "base64 0.21.7",
+ "bigdecimal",
+ "bindgen",
+ "bitflags 2.6.0",
+ "bitvec",
+ "btoi",
+ "byteorder",
+ "bytes",
+ "cc",
+ "cmake",
+ "crc32fast",
+ "flate2",
+ "frunk",
+ "lazy_static",
+ "mysql-common-derive",
+ "num-bigint",
+ "num-traits",
+ "rand",
+ "regex",
+ "rust_decimal",
+ "saturating",
+ "serde",
+ "serde_json",
+ "sha1",
+ "sha2",
+ "smallvec",
+ "subprocess",
+ "thiserror",
+ "time",
+ "uuid",
+ "zstd",
+]
+
+[[package]]
+name = "named_pipe"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad9c443cce91fc3e12f017290db75dde490d685cdaaf508d7159d7cf41f0eb2b"
+dependencies = [
+ "winapi",
+]
+
 [[package]]
 name = "native-tls"
 version = "0.2.11"
@@ -542,6 +1193,7 @@ name = "njord"
 version = "0.3.0"
 dependencies = [
  "log",
+ "mysql 25.0.1",
  "njord_derive",
  "rand",
  "rusqlite",
@@ -555,7 +1207,51 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rusqlite",
- "syn",
+ "syn 2.0.79",
+]
+
+[[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"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[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-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
 ]
 
 [[package]]
@@ -596,7 +1292,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -640,6 +1336,22 @@ dependencies = [
  "windows-targets 0.48.5",
 ]
 
+[[package]]
+name = "peeking_take_while"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
+
+[[package]]
+name = "pem"
+version = "3.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae"
+dependencies = [
+ "base64 0.22.0",
+ "serde",
+]
+
 [[package]]
 name = "percent-encoding"
 version = "2.3.1"
@@ -663,7 +1375,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -684,12 +1396,51 @@ version = "0.3.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
 
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
 [[package]]
 name = "ppv-lite86"
 version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
+[[package]]
+name = "proc-macro-crate"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
+dependencies = [
+ "toml_edit",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
 [[package]]
 name = "proc-macro2"
 version = "1.0.87"
@@ -699,6 +1450,26 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "ptr_meta"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1"
+dependencies = [
+ "ptr_meta_derive",
+]
+
+[[package]]
+name = "ptr_meta_derive"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "quote"
 version = "1.0.37"
@@ -708,6 +1479,12 @@ dependencies = [
  "proc-macro2",
 ]
 
+[[package]]
+name = "radium"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
+
 [[package]]
 name = "rand"
 version = "0.8.5"
@@ -747,13 +1524,51 @@ dependencies = [
  "bitflags 1.3.2",
 ]
 
+[[package]]
+name = "regex"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[package]]
+name = "rend"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c"
+dependencies = [
+ "bytecheck",
+]
+
 [[package]]
 name = "reqwest"
 version = "0.12.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b"
 dependencies = [
- "base64",
+ "base64 0.22.0",
  "bytes",
  "encoding_rs",
  "futures-core",
@@ -805,6 +1620,35 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "rkyv"
+version = "0.7.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b"
+dependencies = [
+ "bitvec",
+ "bytecheck",
+ "bytes",
+ "hashbrown 0.12.3",
+ "ptr_meta",
+ "rend",
+ "rkyv_derive",
+ "seahash",
+ "tinyvec",
+ "uuid",
+]
+
+[[package]]
+name = "rkyv_derive"
+version = "0.7.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "rusqlite"
 version = "0.32.1"
@@ -819,12 +1663,34 @@ dependencies = [
  "smallvec",
 ]
 
+[[package]]
+name = "rust_decimal"
+version = "1.36.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555"
+dependencies = [
+ "arrayvec",
+ "borsh",
+ "bytes",
+ "num-traits",
+ "rand",
+ "rkyv",
+ "serde",
+ "serde_json",
+]
+
 [[package]]
 name = "rustc-demangle"
 version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
 
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
 [[package]]
 name = "rustix"
 version = "0.38.28"
@@ -857,7 +1723,7 @@ version = "2.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
 dependencies = [
- "base64",
+ "base64 0.22.0",
  "rustls-pki-types",
 ]
 
@@ -884,6 +1750,12 @@ version = "1.0.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
 
+[[package]]
+name = "saturating"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ece8e78b2f38ec51c51f5d475df0a7187ba5111b2a28bdc761ee05b075d40a71"
+
 [[package]]
 name = "schannel"
 version = "0.1.23"
@@ -899,6 +1771,12 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
+[[package]]
+name = "seahash"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
+
 [[package]]
 name = "security-framework"
 version = "2.9.2"
@@ -939,7 +1817,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -966,6 +1844,34 @@ dependencies = [
  "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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
 [[package]]
 name = "signal-hook-registry"
 version = "1.4.1"
@@ -975,6 +1881,12 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "simdutf8"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
+
 [[package]]
 name = "slab"
 version = "0.4.9"
@@ -1018,12 +1930,45 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "subprocess"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
 [[package]]
 name = "subtle"
 version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0d0208408ba0c3df17ed26eb06992cb1a1268d41b2c0e12e65203fbe3972cee5"
 
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
 [[package]]
 name = "syn"
 version = "2.0.79"
@@ -1035,6 +1980,18 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "syn_derive"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b"
+dependencies = [
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.79",
+]
+
 [[package]]
 name = "sync_wrapper"
 version = "1.0.1"
@@ -1065,6 +2022,12 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "tap"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
+
 [[package]]
 name = "tempfile"
 version = "3.9.0"
@@ -1078,6 +2041,64 @@ dependencies = [
  "windows-sys",
 ]
 
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[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 2.0.79",
+]
+
+[[package]]
+name = "time"
+version = "0.3.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
+dependencies = [
+ "deranged",
+ "num-conv",
+ "powerfmt",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "time-macros"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
 [[package]]
 name = "tinyvec"
 version = "1.6.0"
@@ -1119,7 +2140,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -1157,6 +2178,23 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "toml_datetime"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+
+[[package]]
+name = "toml_edit"
+version = "0.22.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
+dependencies = [
+ "indexmap",
+ "toml_datetime",
+ "winnow",
+]
+
 [[package]]
 name = "tower"
 version = "0.4.13"
@@ -1211,6 +2249,23 @@ version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
 
+[[package]]
+name = "twox-hash"
+version = "1.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
+dependencies = [
+ "cfg-if",
+ "rand",
+ "static_assertions",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
 [[package]]
 name = "unicode-bidi"
 version = "0.3.15"
@@ -1249,6 +2304,12 @@ dependencies = [
  "percent-encoding",
 ]
 
+[[package]]
+name = "uuid"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
+
 [[package]]
 name = "vcpkg"
 version = "0.2.15"
@@ -1297,7 +2358,7 @@ dependencies = [
  "once_cell",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.79",
  "wasm-bindgen-shared",
 ]
 
@@ -1331,7 +2392,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.79",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -1352,6 +2413,37 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
 [[package]]
 name = "windows-registry"
 version = "0.2.0"
@@ -1512,6 +2604,24 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
+[[package]]
+name = "winnow"
+version = "0.6.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "wyz"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
+dependencies = [
+ "tap",
+]
+
 [[package]]
 name = "zerocopy"
 version = "0.7.31"
@@ -1529,7 +2639,7 @@ checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -1537,3 +2647,31 @@ name = "zeroize"
 version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+
+[[package]]
+name = "zstd"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "7.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059"
+dependencies = [
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.13+zstd.1.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
diff --git a/Cargo.toml b/Cargo.toml
index f8309ede..d8ad94c0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,6 +3,7 @@ members = [
     "njord",
     "njord_derive",
     "njord_examples/sqlite",
+    "njord_examples/mysql",
 ]
 
 resolver = "2"
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..6f34f28b
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,19 @@
+version: "3.8"
+
+services:
+  mysql:
+    image: mysql:8.0
+    container_name: mysql_container
+    environment:
+      MYSQL_ROOT_PASSWORD: password
+      MYSQL_DATABASE: neo # Optional: Pre-create a database
+      MYSQL_USER: user # Optional: Create a new user
+      MYSQL_PASSWORD: password # Optional: Set password for the new user
+    ports:
+      - "3306:3306"
+    volumes:
+      - mysql_data:/var/lib/mysql # Persistent data storage
+      - ./njord_examples/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql # Initialization script
+
+volumes:
+  mysql_data:
diff --git a/njord/Cargo.toml b/njord/Cargo.toml
index 490407cc..78651f17 100644
--- a/njord/Cargo.toml
+++ b/njord/Cargo.toml
@@ -18,6 +18,7 @@ rusqlite = { version = "0.32.1", features = ["bundled"] }
 log = "0.4.22"
 rand = "0.8.4"
 serde = { version = "1.0.210", features = ["derive"] }
+mysql = "25.0.1"
 
 [dev-dependencies]
 njord_derive = { version = "0.3.0", path = "../njord_derive" }
@@ -33,6 +34,7 @@ njord_derive = { version = "0.3.0", path = "../njord_derive" }
 [features]
 default = ["sqlite"]
 sqlite = []
+mysql = []
 
 [[test]]
 name = "sqlite_tests"
diff --git a/njord/src/lib.rs b/njord/src/lib.rs
index 1ab9c27b..d38e90a7 100644
--- a/njord/src/lib.rs
+++ b/njord/src/lib.rs
@@ -30,6 +30,8 @@
 pub mod column;
 pub mod condition;
 pub mod keys;
+#[cfg(feature = "mysql")]
+pub mod mysql;
 pub mod query;
 #[cfg(feature = "sqlite")]
 pub mod sqlite;
diff --git a/njord/src/mysql/delete.rs b/njord/src/mysql/delete.rs
new file mode 100644
index 00000000..3ecacf37
--- /dev/null
+++ b/njord/src/mysql/delete.rs
@@ -0,0 +1,173 @@
+//! BSD 3-Clause License
+//!
+//! Copyright (c) 2024
+//!     Marcus Cvjeticanin
+//!     Chase Willden
+//!
+//! Redistribution and use in source and binary forms, with or without
+//! modification, are permitted provided that the following conditions are met:
+//!
+//! 1. Redistributions of source code must retain the above copyright notice, this
+//!    list of conditions and the following disclaimer.
+//!
+//! 2. Redistributions in binary form must reproduce the above copyright notice,
+//!    this list of conditions and the following disclaimer in the documentation
+//!    and/or other materials provided with the distribution.
+//!
+//! 3. Neither the name of the copyright holder nor the names of its
+//!    contributors may be used to endorse or promote products derived from
+//!    this software without specific prior written permission.
+//!
+//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+//! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+//! FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+//! SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+//! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+//! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+//! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use std::collections::HashMap;
+
+use crate::{
+    condition::Condition,
+    mysql::util::{
+        generate_limit_str, generate_offset_str, generate_order_by_str,
+        generate_where_condition_str, remove_quotes_and_backslashes,
+    },
+};
+
+use log::info;
+use mysql::{prelude::Queryable, PooledConn};
+
+use crate::table::Table;
+
+/// Constructs a new DELETE query builder.
+///
+/// # Arguments
+///
+/// * `conn` - A `PooledConn` to the MySql database.
+///
+/// # Returns
+///
+/// A `DeleteQueryBuilder` instance.
+pub fn delete<'a, T: Table + Default>(conn: &'a mut PooledConn) -> DeleteQueryBuilder<'a, T> {
+    DeleteQueryBuilder::new(conn)
+}
+
+/// A builder for constructing DELETE queries.
+pub struct DeleteQueryBuilder<'a, T: Table + Default> {
+    conn: &'a mut PooledConn,
+    table: Option<T>,
+    where_condition: Option<Condition<'a>>,
+    order_by: Option<HashMap<Vec<String>, String>>,
+    limit: Option<usize>,
+    offset: Option<usize>,
+}
+
+impl<'a, T: Table + Default> DeleteQueryBuilder<'a, T> {
+    /// Creates a new `DeleteQueryBuilder` instance.
+    ///
+    /// # Arguments
+    ///
+    /// * `conn` - A `PooledConn` to the MySql database.
+    pub fn new(conn: &'a mut PooledConn) -> Self {
+        DeleteQueryBuilder {
+            conn,
+            table: None,
+            where_condition: None,
+            order_by: None,
+            limit: None,
+            offset: None,
+        }
+    }
+
+    /// Sets the table from which to delete data.
+    ///
+    /// # Arguments
+    ///
+    /// * `table` - An instance of the table from which to delete data.
+    pub fn from(mut self, table: T) -> Self {
+        self.table = Some(table);
+        self
+    }
+
+    /// Sets the WHERE clause condition.
+    ///
+    /// # Arguments
+    ///
+    /// * `condition` - The condition to be applied in the WHERE clause.
+    pub fn where_clause(mut self, condition: Condition<'a>) -> Self {
+        self.where_condition = Some(condition);
+        self
+    }
+
+    /// Sets the ORDER BY clause columns and order direction.
+    ///
+    /// # Arguments
+    ///
+    /// * `col_and_order` - A hashmap representing the columns and their order directions.
+    pub fn order_by(mut self, col_and_order: HashMap<Vec<String>, String>) -> Self {
+        self.order_by = Some(col_and_order);
+        self
+    }
+
+    /// Sets the LIMIT clause for the query.
+    ///
+    /// # Arguments
+    ///
+    /// * `count` - The maximum number of rows to be deleted.
+    pub fn limit(mut self, count: usize) -> Self {
+        self.limit = Some(count);
+        self
+    }
+
+    /// Sets the OFFSET clause for the query.
+    ///
+    /// # Arguments
+    ///
+    /// * `offset` - The offset from which to start deleting rows.
+    pub fn offset(mut self, offset: usize) -> Self {
+        self.offset = Some(offset);
+        self
+    }
+
+    /// Builds and executes the DELETE query.
+    ///
+    /// # Returns
+    ///
+    /// A `Result` indicating success or failure of the deletion operation.
+    pub fn build(self) -> Result<(), String> {
+        let table_name = self
+            .table
+            .as_ref()
+            .map(|t| t.get_name().to_string())
+            .unwrap_or("".to_string());
+
+        // Sanitize table name from unwanted quotations or backslashes
+        let table_name_str = remove_quotes_and_backslashes(&table_name);
+        let where_condition_str = generate_where_condition_str(self.where_condition);
+        let order_by_str = generate_order_by_str(&self.order_by);
+        let limit_str = generate_limit_str(self.limit);
+        let offset_str = generate_offset_str(self.offset);
+
+        // Construct the query based on defined variables above
+        let query = format!(
+            "DELETE FROM {} {} {} {}",
+            table_name_str,
+            where_condition_str,
+            order_by_str,
+            format!("{} {}", limit_str, offset_str),
+        );
+
+        info!("{}", query);
+        println!("{}", query);
+
+        // Execute SQL
+        let _ = self.conn.query_drop(&query.to_string());
+
+        Ok(())
+    }
+}
diff --git a/njord/src/mysql/error.rs b/njord/src/mysql/error.rs
new file mode 100644
index 00000000..9e9545e6
--- /dev/null
+++ b/njord/src/mysql/error.rs
@@ -0,0 +1,52 @@
+//! BSD 3-Clause License
+//!
+//! Copyright (c) 2024
+//!     Marcus Cvjeticanin
+//!     Chase Willden
+//!
+//! Redistribution and use in source and binary forms, with or without
+//! modification, are permitted provided that the following conditions are met:
+//!
+//! 1. Redistributions of source code must retain the above copyright notice, this
+//!    list of conditions and the following disclaimer.
+//!
+//! 2. Redistributions in binary form must reproduce the above copyright notice,
+//!    this list of conditions and the following disclaimer in the documentation
+//!    and/or other materials provided with the distribution.
+//!
+//! 3. Neither the name of the copyright holder nor the names of its
+//!    contributors may be used to endorse or promote products derived from
+//!    this software without specific prior written permission.
+//!
+//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+//! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+//! FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+//! SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+//! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+//! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+//! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use mysql::Error as MySqlLibError;
+
+/// Represents errors that can occur during SQLite operations.
+#[derive(Debug)]
+pub enum MySqlError {
+    /// Error that occurs during a SELECT operation.
+    SelectError(MySqlLibError),
+    /// Error that occurs during an INSERT operation.
+    InsertError(MySqlLibError),
+    /// Error that occurs during an UPDATE operation.
+    UpdateError(MySqlLibError),
+    /// Error that occurs during a DELETE operation.
+    DeleteError(MySqlLibError),
+}
+
+impl From<MySqlLibError> for MySqlError {
+    /// Converts a `rusqlite::Error` into a `MySqlError`.
+    fn from(error: MySqlLibError) -> Self {
+        MySqlError::InsertError(error)
+    }
+}
diff --git a/njord/src/mysql/insert.rs b/njord/src/mysql/insert.rs
new file mode 100644
index 00000000..7d0cbe31
--- /dev/null
+++ b/njord/src/mysql/insert.rs
@@ -0,0 +1,199 @@
+//! BSD 3-Clause License
+//!
+//! Copyright (c) 2024,
+//!     Marcus Cvjeticanin
+//!     Chase Willden
+//!
+//! Redistribution and use in source and binary forms, with or without
+//! modification, are permitted provided that the following conditions are met:
+//!
+//! 1. Redistributions of source code must retain the above copyright notice, this
+//!    list of conditions and the following disclaimer.
+//!
+//! 2. Redistributions in binary form must reproduce the above copyright notice,
+//!    this list of conditions and the following disclaimer in the documentation
+//!    and/or other materials provided with the distribution.
+//!
+//! 3. Neither the name of the copyright holder nor the names of its
+//!    contributors may be used to endorse or promote products derived from
+//!    this software without specific prior written permission.
+//!
+//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+//! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+//! FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+//! SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+//! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+//! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+//! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use crate::{query::QueryBuilder, table::Table};
+
+use mysql::{prelude::Queryable, PooledConn};
+use rusqlite::Error as RusqliteError;
+
+use log::info;
+use std::fmt::Error;
+
+/// Inserts rows into a MySql table.
+///
+/// This function takes a `PooledConn` and a vector of objects implementing
+/// the `Table` trait, which represents rows to be inserted into the table.
+/// It generates SQL INSERT statements for each row and executes them within
+/// a transaction.
+///
+/// # Arguments
+///
+/// * `conn` - A `PooledConn` to the MySql database.
+/// * `table_rows` - A vector of objects implementing the `Table` trait representing
+///                  the rows to be inserted into the database.
+///
+/// # Returns
+///
+/// A `Result` containing a `String` representing the joined SQL statements
+/// if the insertion is successful, or a `RusqliteError` if an error occurs.
+pub fn insert<'a, T: Table>(
+    conn: &'a mut PooledConn,
+    table_rows: Vec<T>,
+) -> Result<String, RusqliteError> {
+    let mut statements: Vec<String> = Vec::new();
+    for (index, table_row) in table_rows.iter().enumerate() {
+        match generate_statement(table_row, index == 0) {
+            Ok(statement) => statements.push(statement),
+            Err(_) => return Err(RusqliteError::InvalidQuery),
+        }
+    }
+
+    let joined_statements = statements.join(", ");
+
+    // FIXME: Convert to transaction
+    match conn.query_drop(&joined_statements) {
+        Ok(_) => info!("Inserted into table, done."),
+        Err(err) => eprintln!("Error: {}", err),
+    }
+
+    info!("Inserted into table, done.");
+
+    // FIXME: Return the number of rows affected
+    Ok(joined_statements)
+}
+
+/// Generates an SQL INSERT INTO statement for a given table row.
+///
+/// # Arguments
+///
+/// * `table_row` - A reference to an object implementing the `Table` trait.
+///
+/// # Returns
+///
+/// A `Result` containing a `String` representing the generated SQL statement
+/// if the operation is successful, or a `RusqliteError` if an error occurs.
+pub fn into<'a, T: Table + Default>(
+    conn: &'a mut PooledConn,
+    columns: Vec<String>,
+    subquery: Box<dyn QueryBuilder<'a> + 'a>,
+) -> Result<String, RusqliteError> {
+    let statement = generate_insert_into_statement::<T>(columns, subquery);
+    let sql = statement.unwrap();
+
+    // FIXME: Convert to transaction
+    let _ = conn.query_drop(&sql);
+
+    info!("Inserted into table, done.");
+
+    // FIXME: Return the number of rows affected
+    return Ok(sql);
+}
+
+/// Generates an SQL INSERT INTO statement for a given subquery.
+///
+/// # Arguments
+///
+/// * `columns` - A `Vec` of column names.
+/// * `subquery` - A `QueryBuilder` object representing the subquery.
+///
+/// # Returns
+///
+/// A `Result` containing a `String` representing the generated SQL statement
+/// if the operation is successful, or a `RusqliteError` if an error occurs.
+fn generate_insert_into_statement<'a, T: Table + Default>(
+    columns: Vec<String>,
+    subquery: Box<dyn QueryBuilder<'a> + 'a>,
+) -> Result<String, RusqliteError> {
+    let columns_str = columns.join(", ");
+    let subquery_str = subquery.to_sql();
+    let table_row = T::default();
+    let table_name = table_row.get_name().replace("\"", "").replace("\\", "");
+
+    let sql = format!(
+        "INSERT INTO {} ({}) {}",
+        table_name, columns_str, subquery_str
+    );
+
+    Ok(sql)
+}
+
+/// Generates an SQL INSERT statement for a given table row.
+///
+/// This function takes an object implementing the `Table` trait, representing
+/// a single row of data to be inserted into the database. It generates an SQL
+/// INSERT statement based on the column names and values of the table row.
+///
+/// # Arguments
+///
+/// * `table_row` - An object implementing the `Table` trait representing
+///                 a single row of data to be inserted.
+/// * `first_statement` - A boolean flag indicating whether this is the first
+///                       statement to be generated.
+///
+/// # Returns
+///
+/// A `Result` containing a `String` representing the generated SQL statement
+/// if successful, or a `Error` if an error occurs during the generation process.
+fn generate_statement<T: Table>(table_row: &T, first_statement: bool) -> Result<String, Error> {
+    // Generate strings for columns and values
+    let mut columns_str = String::new();
+    let mut values_str = String::new();
+
+    // Iterate over the fields to generate columns and values
+    let column_fields = table_row.get_column_fields();
+    let column_values = table_row.get_column_values();
+
+    for (column_name, value) in column_fields.iter().zip(column_values.iter()) {
+        // Check if the field is an AutoIncrementPrimaryKey
+        if table_row.is_auto_increment_primary_key(value) {
+            println!("Skipping AutoIncrementPrimaryKey field in SQL statement generation.");
+            continue;
+        }
+        columns_str.push_str(&format!("{}, ", column_name));
+        values_str.push_str(&format!("'{}', ", value)); // Surround values with single quotes
+    }
+
+    // Sanitize table name from unwanted quotations or backslashes
+    let table_name = table_row.get_name().replace("\"", "").replace("\\", "");
+
+    // Remove the trailing comma and space
+    if !columns_str.is_empty() {
+        columns_str.pop();
+        columns_str.pop();
+    }
+    if !values_str.is_empty() {
+        values_str.pop();
+        values_str.pop();
+    }
+
+    let sql = if first_statement {
+        format!(
+            "INSERT INTO {} ({}) VALUES ({})",
+            table_name, columns_str, values_str
+        )
+    } else {
+        format!("({})", values_str)
+    };
+
+    println!("{}", sql); // For debugging purposes
+
+    Ok(sql)
+}
diff --git a/njord/src/mysql/mod.rs b/njord/src/mysql/mod.rs
new file mode 100644
index 00000000..cd50521d
--- /dev/null
+++ b/njord/src/mysql/mod.rs
@@ -0,0 +1,64 @@
+//! BSD 3-Clause License
+//!
+//! Copyright (c) 2024,
+//!     Marcus Cvjeticanin
+//!     Chase Willden
+//!
+//! Redistribution and use in source and binary forms, with or without
+//! modification, are permitted provided that the following conditions are met:
+//!
+//! 1. Redistributions of source code must retain the above copyright notice, this
+//!    list of conditions and the following disclaimer.
+//!
+//! 2. Redistributions in binary form must reproduce the above copyright notice,
+//!    this list of conditions and the following disclaimer in the documentation
+//!    and/or other materials provided with the distribution.
+//!
+//! 3. Neither the name of the copyright holder nor the names of its
+//!    contributors may be used to endorse or promote products derived from
+//!    this software without specific prior written permission.
+//!
+//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+//! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+//! FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+//! SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+//! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+//! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+//! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use mysql::{Error, Pool, PooledConn};
+
+pub mod delete;
+pub mod error;
+pub mod insert;
+pub mod select;
+pub mod update;
+mod util;
+
+pub use delete::delete;
+pub use error::MySqlError;
+pub use insert::insert;
+pub use select::select;
+pub use update::update;
+
+/// Open a database connection.
+///
+/// This function opens a connection to a MySql database located at the specified path.
+///
+/// # Arguments
+///
+/// * `db_path` - A reference to the path where the MySql database is located.
+///
+/// # Returns
+///
+/// Returns a `Result` containing a `PooledConn` if the operation was successful, or an `Error` if an error occurred.
+pub fn open(url: &str) -> Result<PooledConn, Error> {
+    let pool = Pool::new(url)?;
+
+    let conn = pool.get_conn()?;
+
+    Ok(conn)
+}
diff --git a/njord/src/mysql/select.rs b/njord/src/mysql/select.rs
new file mode 100644
index 00000000..9387e9a0
--- /dev/null
+++ b/njord/src/mysql/select.rs
@@ -0,0 +1,388 @@
+//! BSD 3-Clause License
+//!
+//! Copyright (c) 2024
+//!     Marcus Cvjeticanin
+//!     Chase Willden
+//!
+//! Redistribution and use in source and binary forms, with or without
+//! modification, are permitted provided that the following conditions are met:
+//!
+//! 1. Redistributions of source code must retain the above copyright notice, this
+//!    list of conditions and the following disclaimer.
+//!
+//! 2. Redistributions in binary form must reproduce the above copyright notice,
+//!    this list of conditions and the following disclaimer in the documentation
+//!    and/or other materials provided with the distribution.
+//!
+//! 3. Neither the name of the copyright holder nor the names of its
+//!    contributors may be used to endorse or promote products derived from
+//!    this software without specific prior written permission.
+//!
+//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+//! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+//! FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+//! SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+//! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+//! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+//! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use crate::{
+    column::Column,
+    condition::Condition,
+    mysql::util::{
+        generate_group_by_str, generate_having_str, generate_limit_str, generate_offset_str,
+        generate_order_by_str, generate_where_condition_str,
+    },
+    query::QueryBuilder,
+};
+use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
+
+use log::info;
+use mysql::prelude::*;
+use mysql::{prelude::FromRow, Error, PooledConn};
+
+use crate::table::Table;
+use crate::util::{Join, JoinType};
+
+/// Constructs a new SELECT query builder.
+///
+/// # Arguments
+///
+/// * `conn` - A `PooledConn` to the MySql database.
+/// * `columns` - A vector of strings representing the columns to be selected.
+///
+/// # Returns
+///
+/// A `SelectQueryBuilder` instance.
+pub fn select<'a, T: Table + Default + FromRow>(
+    conn: &'a mut PooledConn,
+    columns: Vec<Column<'a, T>>,
+) -> SelectQueryBuilder<'a, T> {
+    SelectQueryBuilder::new(conn, columns)
+}
+
+/// A builder for constructing SELECT queries.
+#[derive(Clone)]
+pub struct SelectQueryBuilder<'a, T: Table + Default + FromRow> {
+    conn: Rc<RefCell<&'a mut PooledConn>>,
+    table: Option<T>,
+    columns: Vec<Column<'a, T>>,
+    where_condition: Option<Condition<'a>>,
+    distinct: bool,
+    group_by: Option<Vec<String>>,
+    order_by: Option<HashMap<Vec<String>, String>>,
+    limit: Option<usize>,
+    offset: Option<usize>,
+    having_condition: Option<Condition<'a>>,
+    except_clauses: Option<Vec<SelectQueryBuilder<'a, T>>>,
+    union_clauses: Option<Vec<SelectQueryBuilder<'a, T>>>,
+    joins: Option<Vec<Join<'a>>>,
+}
+
+impl<'a, T: Table + Default + FromRow> SelectQueryBuilder<'a, T> {
+    /// Creates a new `SelectQueryBuilder` instance.
+    ///
+    /// # Arguments
+    ///
+    /// * `conn` - A `PooledConn` to the MySql database.
+    /// * `columns` - A vector of strings representing the columns to be selected.
+    pub fn new(conn: &'a mut PooledConn, columns: Vec<Column<'a, T>>) -> Self {
+        SelectQueryBuilder {
+            conn: Rc::new(RefCell::new(conn)),
+            table: None,
+            columns,
+            where_condition: None,
+            distinct: false,
+            group_by: None,
+            order_by: None,
+            limit: None,
+            offset: None,
+            having_condition: None,
+            except_clauses: None,
+            union_clauses: None,
+            joins: None,
+        }
+    }
+
+    /// Sets the columns to be selected.
+    ///
+    /// # Arguments
+    ///
+    /// * `columns` - A vector of strings representing the columns to be selected.
+    pub fn select(mut self, columns: Vec<Column<'a, T>>) -> Self {
+        self.columns = columns;
+        self
+    }
+
+    /// Sets the DISTINCT keyword for the query.
+    pub fn distinct(mut self) -> Self {
+        self.distinct = true;
+        self
+    }
+
+    /// Sets the table from which to select data.
+    ///
+    /// # Arguments
+    ///
+    /// * `table` - The table from which to select data.
+    pub fn from(mut self, table: T) -> Self {
+        self.table = Some(table);
+        self
+    }
+
+    /// Sets the WHERE clause condition.
+    ///
+    /// # Arguments
+    ///
+    /// * `condition` - The condition to be applied in the WHERE clause.
+    pub fn where_clause(mut self, condition: Condition<'a>) -> Self {
+        self.where_condition = Some(condition);
+        self
+    }
+
+    /// Sets the GROUP BY clause columns.
+    ///
+    /// # Arguments
+    ///
+    /// * `columns` - A vector of strings representing the columns to be grouped by.
+    pub fn group_by(mut self, columns: Vec<String>) -> Self {
+        self.group_by = Some(columns);
+        self
+    }
+
+    /// Sets the ORDER BY clause columns and order direction.
+    ///
+    /// # Arguments
+    ///
+    /// * `col_and_order` - A HashMap containing column names as keys and order direction as values.
+    pub fn order_by(mut self, col_and_order: HashMap<Vec<String>, String>) -> Self {
+        self.order_by = Some(col_and_order);
+        self
+    }
+
+    /// Sets the LIMIT clause for the query.
+    ///
+    /// # Arguments
+    ///
+    /// * `count` - The number of rows to limit the result to.
+    pub fn limit(mut self, count: usize) -> Self {
+        self.limit = Some(count);
+        self
+    }
+
+    /// Sets the OFFSET clause for the query.
+    ///
+    /// # Arguments
+    ///
+    /// * `offset` - The number of rows to skip.
+    pub fn offset(mut self, offset: usize) -> Self {
+        self.offset = Some(offset);
+        self
+    }
+
+    /// Sets the HAVING clause condition.
+    ///
+    /// # Arguments
+    ///
+    /// * `condition` - The condition to be applied in the HAVING clause.
+    pub fn having(mut self, condition: Condition<'a>) -> Self {
+        self.having_condition = Some(condition);
+        self
+    }
+
+    /// Adds an EXCEPT clause to the query, allowing you to exclude results from another query.
+    ///
+    /// This method modifies the current query builder to exclude the results of the specified
+    /// `other_query`. If there are already existing EXCEPT clauses, the new clause will be added
+    /// to the list. If no EXCEPT clauses exist, a new list will be created with the provided
+    /// query.
+    ///
+    /// # Arguments
+    ///
+    /// * `other_query` - A `SelectQueryBuilder` instance that represents the query whose results
+    ///   should be excluded from the current query.
+    ///
+    /// # Returns
+    ///
+    /// Returns the modified `SelectQueryBuilder` instance with the new EXCEPT clause added.
+    pub fn except(mut self, other_query: SelectQueryBuilder<'a, T>) -> Self {
+        match self.except_clauses {
+            Some(ref mut clauses) => clauses.push(other_query),
+            None => self.except_clauses = Some(vec![other_query]),
+        }
+        self
+    }
+
+    /// Adds a UNION clause to the query, allowing you to combine results from another query.
+    ///
+    /// This method modifies the current query builder to include the results of the specified
+    /// `other_query`. If there are already existing UNION clauses, the new clause will be added
+    /// to the list. If no UNION clauses exist, a new list will be created with the provided
+    /// query.
+    ///
+    /// # Arguments
+    ///
+    /// * `other_query` - A `SelectQueryBuilder` instance that represents the query whose results
+    ///   should be combined with the current query.
+    ///
+    /// # Returns
+    ///
+    /// Returns the modified `SelectQueryBuilder` instance with the new UNION clause added.
+    pub fn union(mut self, other_query: SelectQueryBuilder<'a, T>) -> Self {
+        match self.union_clauses {
+            Some(ref mut clauses) => clauses.push(other_query),
+            None => self.union_clauses = Some(vec![other_query]),
+        }
+        self
+    }
+
+    /// Adds a JOIN clause to the query, allowing you to combine rows from two or more tables based on a related column.
+    ///
+    /// This method modifies the current query builder to include a join clause with the specified join type,
+    /// target table, and condition for the join. If there are already existing JOIN clauses, the new clause
+    /// will be added to the list. If no JOIN clauses exist, a new list will be created with the provided
+    /// join information.
+    ///
+    /// # Arguments
+    ///
+    /// * `join_type` - The type of join to perform (e.g., INNER, LEFT, RIGHT, FULL).
+    /// * `table` - The table to join with the current table.
+    /// * `on_condition` - The condition that specifies how the tables are related (the ON clause).
+    ///
+    /// # Returns
+    ///
+    /// Returns the modified `SelectQueryBuilder` instance with the new JOIN clause added.
+    pub fn join(
+        mut self,
+        join_type: JoinType,
+        table: Arc<dyn Table>,
+        on_condition: Condition<'a>,
+    ) -> Self {
+        match self.joins {
+            Some(ref mut joins) => joins.push(Join::new(join_type, table, on_condition)),
+            None => self.joins = Some(vec![Join::new(join_type, table, on_condition)]),
+        }
+        self
+    }
+
+    /// Builds the query string, this function should be used internally.
+    pub fn build_query(&self) -> String {
+        let columns_str = self
+            .columns
+            .iter()
+            .map(|c| c.build())
+            .collect::<Vec<String>>()
+            .join(", ");
+
+        let table_name = self
+            .table
+            .as_ref()
+            .map(|t| t.get_name().to_string())
+            .unwrap_or("".to_string());
+
+        // Generate JOIN clauses, if any
+        let join_clauses: Vec<String> = match &self.joins {
+            Some(joins) => joins
+                .iter()
+                .map(|join| {
+                    let join_type_str = match join.join_type {
+                        JoinType::Inner => "INNER JOIN",
+                        JoinType::Left => "LEFT JOIN",
+                        JoinType::Right => "RIGHT JOIN",
+                        JoinType::Full => "FULL OUTER JOIN",
+                    };
+                    format!(
+                        "{} {} ON {}",
+                        join_type_str,
+                        join.table.get_name(),
+                        generate_where_condition_str(Some(join.on_condition.clone()))
+                            .replace("WHERE", "")
+                    )
+                })
+                .collect(),
+            None => Vec::new(),
+        };
+
+        let distinct_str = if self.distinct { "DISTINCT " } else { "" };
+        let where_condition_str = generate_where_condition_str(self.where_condition.clone());
+        let group_by_str = generate_group_by_str(&self.group_by);
+        let order_by_str = generate_order_by_str(&self.order_by);
+        let limit_str = generate_limit_str(self.limit);
+        let offset_str = generate_offset_str(self.offset);
+        let having_str =
+            generate_having_str(self.group_by.is_some(), self.having_condition.as_ref());
+
+        // Create the JOIN clause or an empty string
+        let join_clause = if !join_clauses.is_empty() {
+            join_clauses.join(" ")
+        } else {
+            String::new()
+        };
+
+        let mut query = format!(
+            "SELECT {}{} FROM {} {} {} {} {} {} {}",
+            distinct_str,
+            columns_str,
+            table_name,
+            join_clause,
+            where_condition_str,
+            group_by_str,
+            having_str,
+            order_by_str,
+            format!("{} {}", limit_str, offset_str),
+        );
+
+        // Handle EXCEPT clauses
+        if let Some(except_clauses) = &self.except_clauses {
+            for except_query in except_clauses {
+                let except_sql = except_query.build_query();
+                query = format!("{} EXCEPT {}", query, except_sql);
+            }
+        }
+
+        // Handle UNION clauses
+        if let Some(union_clauses) = &self.union_clauses {
+            for union_query in union_clauses {
+                let union_sql = union_query.build_query();
+                query = format!("{} UNION {}", query, union_sql);
+            }
+        }
+
+        query
+    }
+
+    /// Builds and executes the SELECT query.
+    ///
+    /// # Returns
+    ///
+    /// A `Result` containing a vector of selected table rows if successful,
+    /// or a `rusqlite::Error` if an error occurs during the execution.
+    pub fn build(&mut self) -> Result<Vec<T>, Error> {
+        let final_query = self.build_query();
+
+        info!("{}", final_query);
+        println!("{}", final_query);
+
+        // Borrow the connection mutably from the RefCell
+        let mut conn = self.conn.borrow_mut();
+
+        let results: Vec<T> = conn.query(final_query.as_str())?;
+
+        Ok(results)
+    }
+}
+
+/// Implement `QueryBuilder` for `SelectQueryBuilder`
+///
+/// The where statement ensures the T is long lived
+impl<'a, T> QueryBuilder<'a> for SelectQueryBuilder<'a, T>
+where
+    T: Table + Default + Clone + FromRow + 'a, // Added 'a bound here
+{
+    fn to_sql(&self) -> String {
+        self.build_query()
+    }
+}
diff --git a/njord/src/mysql/update.rs b/njord/src/mysql/update.rs
new file mode 100644
index 00000000..78eec0d0
--- /dev/null
+++ b/njord/src/mysql/update.rs
@@ -0,0 +1,234 @@
+//! BSD 3-Clause License
+//!
+//! Copyright (c) 2024
+//!     Marcus Cvjeticanin
+//!     Chase Willden
+//!
+//! Redistribution and use in source and binary forms, with or without
+//! modification, are permitted provided that the following conditions are met:
+//!
+//! 1. Redistributions of source code must retain the above copyright notice, this
+//!    list of conditions and the following disclaimer.
+//!
+//! 2. Redistributions in binary form must reproduce the above copyright notice,
+//!    this list of conditions and the following disclaimer in the documentation
+//!    and/or other materials provided with the distribution.
+//!
+//! 3. Neither the name of the copyright holder nor the names of its
+//!    contributors may be used to endorse or promote products derived from
+//!    this software without specific prior written permission.
+//!
+//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+//! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+//! FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+//! SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+//! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+//! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+//! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use std::collections::HashMap;
+
+use crate::{
+    condition::Condition,
+    mysql::util::{
+        generate_limit_str, generate_offset_str, generate_order_by_str,
+        generate_where_condition_str, remove_quotes_and_backslashes,
+    },
+};
+
+use log::info;
+use mysql::{
+    prelude::{FromRow, Queryable},
+    PooledConn,
+};
+
+use crate::table::Table;
+
+use super::select::SelectQueryBuilder;
+
+/// Constructs a new UPDATE query builder.
+///
+/// # Arguments
+///
+/// * `conn` - A `PooledConn` to the MySql database.
+/// * `table` - An instance of the table to be updated.
+///
+/// # Returns
+///
+/// An `UpdateQueryBuilder` instance.
+pub fn update<'a, T: Table + Default + FromRow>(
+    conn: &'a mut PooledConn,
+    table: T,
+) -> UpdateQueryBuilder<'a, T> {
+    UpdateQueryBuilder::new(conn, table)
+}
+
+/// A builder for constructing UPDATE queries.
+pub struct UpdateQueryBuilder<'a, T: Table + Default + FromRow> {
+    conn: &'a mut PooledConn,
+    table: Option<T>,
+    columns: Vec<String>,
+    sub_queries: HashMap<String, SelectQueryBuilder<'a, T>>,
+    where_condition: Option<Condition<'a>>,
+    order_by: Option<HashMap<Vec<String>, String>>,
+    limit: Option<usize>,
+    offset: Option<usize>,
+}
+
+impl<'a, T: Table + Default + FromRow> UpdateQueryBuilder<'a, T> {
+    /// Creates a new `UpdateQueryBuilder` instance.
+    ///
+    /// # Arguments
+    ///
+    /// * `conn` - A `PooledConn` to the MySql database.
+    /// * `table` - An instance of the table to be updated.
+    pub fn new(conn: &'a mut PooledConn, table: T) -> Self {
+        UpdateQueryBuilder {
+            conn,
+            table: Some(table),
+            columns: Vec::new(),
+            sub_queries: HashMap::new(),
+            where_condition: None,
+            order_by: None,
+            limit: None,
+            offset: None,
+        }
+    }
+
+    /// Sets the columns and values to be updated.
+    ///
+    /// # Arguments
+    ///
+    /// * `columns` - A vector of strings representing the columns to be updated.
+    pub fn set(mut self, columns: Vec<String>) -> Self {
+        self.columns = columns;
+        self
+    }
+
+    /// Sets the columns and values (as subquery) to be updated.
+    ///
+    /// # Arguments
+    ///     
+    /// * `columns` - A hashmap representing the columns and their subqueries.
+    pub fn set_subqueries(mut self, columns: HashMap<String, SelectQueryBuilder<'a, T>>) -> Self {
+        self.sub_queries = columns;
+        self
+    }
+
+    /// Sets the WHERE clause condition.
+    ///
+    /// # Arguments
+    ///
+    /// * `condition` - The condition to be applied in the WHERE clause.
+    pub fn where_clause(mut self, condition: Condition<'a>) -> Self {
+        self.where_condition = Some(condition);
+        self
+    }
+
+    /// Sets the ORDER BY clause columns and order direction.
+    ///
+    /// # Arguments
+    ///
+    /// * `col_and_order` - A hashmap representing the columns and their order directions.
+    pub fn order_by(mut self, col_and_order: HashMap<Vec<String>, String>) -> Self {
+        self.order_by = Some(col_and_order);
+        self
+    }
+
+    /// Sets the LIMIT clause for the query.
+    ///
+    /// # Arguments
+    ///
+    /// * `count` - The maximum number of rows to be updated.
+    pub fn limit(mut self, count: usize) -> Self {
+        self.limit = Some(count);
+        self
+    }
+
+    /// Sets the OFFSET clause for the query.
+    ///
+    /// # Arguments
+    ///
+    /// * `offset` - The offset from which to start updating rows.
+    pub fn offset(mut self, offset: usize) -> Self {
+        self.offset = Some(offset);
+        self
+    }
+
+    /// Builds and executes the UPDATE query.
+    ///
+    /// # Returns
+    ///
+    /// A `Result` indicating success or failure of the update operation.
+    pub fn build(self) -> Result<(), String> {
+        let table_name = self
+            .table
+            .as_ref()
+            .map(|t| t.get_name().to_string())
+            .unwrap_or("".to_string());
+
+        // Sanitize table name from unwanted quotations or backslashes
+        let table_name_str = remove_quotes_and_backslashes(&table_name);
+
+        // Generate SET clause
+        let set = if let Some(table) = &self.table {
+            let mut set_fields = Vec::new();
+            let fields = table.get_column_fields();
+            let values = table.get_column_values();
+
+            for column in &self.columns {
+                // Check if column exists in the table's fields
+                if let Some(index) = fields.iter().position(|c| column == c) {
+                    let value = values.get(index).cloned().unwrap_or_default();
+                    let formatted_value = if value.is_empty() {
+                        "NULL".to_string()
+                    } else if value.parse::<f64>().is_ok() {
+                        value
+                    } else {
+                        format!("'{}'", value)
+                    };
+                    set_fields.push(format!("{} = {}", column, formatted_value));
+                } else {
+                    // Handle the case when the column doesn't exist in the table
+                    eprintln!("Column '{}' does not exist in the table", column);
+                }
+            }
+
+            // Generate subqueries
+            for (column_name, sub_query) in &self.sub_queries {
+                let formatted_value = format!("({})", sub_query.build_query());
+                set_fields.push(format!("{} = {}", column_name, formatted_value));
+            }
+
+            set_fields.join(", ")
+        } else {
+            String::new()
+        };
+
+        let where_condition_str = generate_where_condition_str(self.where_condition);
+        let order_by_str = generate_order_by_str(&self.order_by);
+        let limit_str = generate_limit_str(self.limit);
+        let offset_str = generate_offset_str(self.offset);
+
+        // Construct the query based on defined variables above
+        let query = format!(
+            "UPDATE {} SET {} {} {} {}",
+            table_name_str,
+            set,
+            where_condition_str,
+            order_by_str,
+            format!("{} {}", limit_str, offset_str),
+        );
+
+        info!("{}", query);
+        println!("{}", query);
+
+        // Prepare SQL statement
+        let _ = self.conn.query_drop(query.as_str());
+
+        Ok(())
+    }
+}
diff --git a/njord/src/mysql/util.rs b/njord/src/mysql/util.rs
new file mode 100644
index 00000000..76b0df07
--- /dev/null
+++ b/njord/src/mysql/util.rs
@@ -0,0 +1,266 @@
+//! BSD 3-Clause License
+//!
+//! Copyright (c) 2024
+//!     Marcus Cvjeticanin
+//!     Chase Willden
+//!
+//! Redistribution and use in source and binary forms, with or without
+//! modification, are permitted provided that the following conditions are met:
+//!
+//! 1. Redistributions of source code must retain the above copyright notice, this
+//!    list of conditions and the following disclaimer.
+//!
+//! 2. Redistributions in binary form must reproduce the above copyright notice,
+//!    this list of conditions and the following disclaimer in the documentation
+//!    and/or other materials provided with the distribution.
+//!
+//! 3. Neither the name of the copyright holder nor the names of its
+//!    contributors may be used to endorse or promote products derived from
+//!    this software without specific prior written permission.
+//!
+//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+//! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+//! FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+//! SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+//! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+//! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+//! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use std::collections::HashMap;
+
+use crate::condition::Condition;
+
+/// Generates an SQL WHERE clause string based on the provided condition.
+///
+/// If `condition` is Some, it constructs an SQL WHERE clause string with the specified condition.
+/// If `condition` is None, an empty string is returned.
+///
+/// # Arguments
+///
+/// * `condition` - An Option containing the condition.
+///
+/// # Returns
+///
+/// A String representing the generated SQL WHERE clause.
+pub fn generate_where_condition_str(condition: Option<Condition>) -> String {
+    if let Some(condition) = condition {
+        format!("WHERE {}", condition.build())
+    } else {
+        String::new()
+    }
+}
+
+/// Generates an SQL GROUP BY clause string based on the provided columns.
+///
+/// If `columns` is Some, it constructs an SQL GROUP BY clause string with the specified columns.
+/// If `columns` is None, an empty string is returned.
+///
+/// # Arguments
+///
+/// * `columns` - An Option containing a reference to a vector of column names.
+///
+/// # Returns
+///
+/// A String representing the generated SQL GROUP BY clause.
+pub fn generate_group_by_str(columns: &Option<Vec<String>>) -> String {
+    match columns {
+        Some(columns) => format!("GROUP BY {}", columns.join(", ")),
+        None => String::new(),
+    }
+}
+
+/// Generates an SQL ORDER BY clause string based on the provided `order_by` option.
+///
+/// If `order_by` is Some, it should contain a HashMap where the keys are vectors of column names
+/// and the values are corresponding sort orders (ASC or DESC). This function constructs an SQL
+/// ORDER BY clause string based on the content of the HashMap. If the HashMap is empty, an empty
+/// string is returned.
+///
+/// # Arguments
+///
+/// * `order_by` - An Option containing a HashMap where the keys are vectors of column names and
+///   the values are corresponding sort orders (ASC or DESC).
+///
+/// # Returns
+///
+/// A String representing the generated SQL ORDER BY clause.
+pub fn generate_order_by_str(order_by: &Option<HashMap<Vec<String>, String>>) -> String {
+    let order_by_str = if let Some(order_by) = order_by.as_ref() {
+        let order_by_str: Vec<String> = order_by
+            .iter()
+            .map(|(columns, order)| format!("{} {}", columns.join(", "), order))
+            .collect();
+        if !order_by_str.is_empty() {
+            format!("ORDER BY {}", order_by_str.join(", "))
+        } else {
+            String::new()
+        }
+    } else {
+        String::new()
+    };
+
+    return order_by_str;
+}
+
+/// Generates an SQL LIMIT clause string based on the provided limit count.
+///
+/// If `limit` is Some, it constructs an SQL LIMIT clause string with the specified count.
+/// If `limit` is None, an empty string is returned.
+///
+/// # Arguments
+///
+/// * `limit` - An Option containing the limit count.
+///
+/// # Returns
+///
+/// A String representing the generated SQL LIMIT clause.
+pub fn generate_limit_str(limit: Option<usize>) -> String {
+    limit.map_or(String::new(), |count| format!("LIMIT {}", count))
+}
+
+/// Generates an SQL OFFSET clause string based on the provided offset count.
+///
+/// If `offset` is Some, it constructs an SQL OFFSET clause string with the specified count.
+/// If `offset` is None, an empty string is returned.
+///
+/// # Arguments
+///
+/// * `offset` - An Option containing the offset count.
+///
+/// # Returns
+///
+/// A String representing the generated SQL OFFSET clause.
+pub fn generate_offset_str(offset: Option<usize>) -> String {
+    offset.map_or(String::new(), |offset| format!("OFFSET {}", offset))
+}
+
+/// Generates an SQL HAVING clause string based on the provided group by flag and condition.
+///
+/// If `group_by` is true and `having_condition` is Some, it constructs an SQL HAVING clause string
+/// with the specified condition.
+/// If either `group_by` is false or `having_condition` is None, an empty string is returned.
+///
+/// # Arguments
+///
+/// * `group_by` - An Option indicating whether GROUP BY is present.
+/// * `having_condition` - An Option containing the condition for the HAVING clause.
+///
+/// # Returns
+///
+/// A String representing the generated SQL HAVING clause.
+pub fn generate_having_str(group_by: bool, having_condition: Option<&Condition>) -> String {
+    if group_by && having_condition.is_some() {
+        format!("HAVING {}", having_condition.unwrap().build())
+    } else {
+        String::new()
+    }
+}
+
+/// Removes double quotes and backslashes from a given string.
+///
+/// # Arguments
+///
+/// * `input` - The input string from which double quotes and backslashes will be removed.
+///
+/// # Returns
+///
+/// A String with double quotes and backslashes removed.
+pub fn remove_quotes_and_backslashes(input: &str) -> String {
+    input.replace("\"", "").replace("\\", "")
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::condition::{Condition, Value};
+
+    #[test]
+    fn test_generate_where_condition_str() {
+        // Test when condition is Some
+        let condition = Condition::Eq("age".to_string(), Value::Literal("30".to_string()));
+        let _result = generate_where_condition_str(Some(condition)); // TODO: need to fix this later
+                                                                     // assert_eq!(result, format!("WHERE {}", condition.build()));
+
+        // Test when condition is None
+        let result = generate_where_condition_str(None);
+        assert_eq!(result, "");
+    }
+
+    #[test]
+    fn test_generate_group_by_str() {
+        // Test when columns is Some
+        let columns = Some(vec!["name".to_string(), "age".to_string()]);
+        let result = generate_group_by_str(&columns);
+        assert_eq!(result, "GROUP BY name, age");
+
+        // Test when columns is None
+        let result = generate_group_by_str(&None);
+        assert_eq!(result, "");
+    }
+
+    /*#[test]
+    fn test_generate_order_by_str() {
+        // Test when order_by is Some
+        let mut map = HashMap::new();
+        map.insert(vec!["name".to_string()], "ASC".to_string());
+        map.insert(vec!["age".to_string()], "DESC".to_string());
+        let result = generate_order_by_str(&Some(map));
+        assert_eq!(result, "ORDER BY name ASC, age DESC");
+
+        // Test when order_by is None
+        let result = generate_order_by_str(&None);
+        assert_eq!(result, "");
+    }*/
+
+    #[test]
+    fn test_generate_limit_str() {
+        // Test when limit is Some
+        let result = generate_limit_str(Some(10));
+        assert_eq!(result, "LIMIT 10");
+
+        // Test when limit is None
+        let result = generate_limit_str(None);
+        assert_eq!(result, "");
+    }
+
+    #[test]
+    fn test_generate_offset_str() {
+        // Test when offset is Some
+        let result = generate_offset_str(Some(5));
+        assert_eq!(result, "OFFSET 5");
+
+        // Test when offset is None
+        let result = generate_offset_str(None);
+        assert_eq!(result, "");
+    }
+
+    #[test]
+    fn test_generate_having_str() {
+        // Test when group_by is true and having_condition is Some
+        let condition = Condition::Gt("COUNT(age)".to_string(), Value::Literal("5".to_string()));
+        let result = generate_having_str(true, Some(&condition));
+        assert_eq!(result, format!("HAVING {}", condition.build()));
+
+        // Test when group_by is false
+        let result = generate_having_str(false, Some(&condition));
+        assert_eq!(result, "");
+
+        // Test when having_condition is None
+        let result = generate_having_str(true, None);
+        assert_eq!(result, "");
+
+        // Test when both group_by is false and having_condition is None
+        let result = generate_having_str(false, None);
+        assert_eq!(result, "");
+    }
+
+    #[test]
+    fn test_remove_quotes_and_backslashes() {
+        let input = r#""table_name\"""#;
+        let result = remove_quotes_and_backslashes(input);
+        assert_eq!(result, "table_name");
+    }
+}
diff --git a/njord/src/sqlite/insert.rs b/njord/src/sqlite/insert.rs
index f0dbc66d..a5523089 100644
--- a/njord/src/sqlite/insert.rs
+++ b/njord/src/sqlite/insert.rs
@@ -59,14 +59,14 @@ pub fn insert<'a, T: Table>(
     table_rows: Vec<T>,
 ) -> Result<String, RusqliteError> {
     let mut statements: Vec<String> = Vec::new();
-    for table_row in table_rows {
-        match generate_statement(&table_row) {
+    for (index, table_row) in table_rows.iter().enumerate() {
+        match generate_statement(table_row, index == 0) {
             Ok(statement) => statements.push(statement),
             Err(_) => return Err(RusqliteError::InvalidQuery),
         }
     }
 
-    let joined_statements = statements.join("; ");
+    let joined_statements = statements.join(", ");
 
     // FIXME: Convert to transaction
     let _ = conn.execute_batch(&joined_statements)?;
@@ -142,12 +142,14 @@ fn generate_insert_into_statement<'a, T: Table + Default>(
 ///
 /// * `table_row` - An object implementing the `Table` trait representing
 ///                 a single row of data to be inserted.
+/// * `first_statement` - A boolean flag indicating whether this is the first
+///                       statement to be generated.
 ///
 /// # Returns
 ///
 /// A `Result` containing a `String` representing the generated SQL statement
 /// if successful, or a `Error` if an error occurs during the generation process.
-fn generate_statement<T: Table>(table_row: &T) -> Result<String, Error> {
+fn generate_statement<T: Table>(table_row: &T, first_statement: bool) -> Result<String, Error> {
     // Generate strings for columns and values
     let mut columns_str = String::new();
     let mut values_str = String::new();
@@ -179,10 +181,14 @@ fn generate_statement<T: Table>(table_row: &T) -> Result<String, Error> {
         values_str.pop();
     }
 
-    let sql = format!(
-        "INSERT INTO {} ({}) VALUES ({}); ",
-        table_name, columns_str, values_str
-    );
+    let sql = if first_statement {
+        format!(
+            "INSERT INTO {} ({}) VALUES ({})",
+            table_name, columns_str, values_str
+        )
+    } else {
+        format!("({})", values_str)
+    };
 
     println!("{}", sql); // For debugging purposes
 
diff --git a/njord_examples/mysql/Cargo.toml b/njord_examples/mysql/Cargo.toml
new file mode 100644
index 00000000..63a37a53
--- /dev/null
+++ b/njord_examples/mysql/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "mysql"
+publish = false
+version = "0.1.0"
+edition = "2021"
+rust-version = "1.81.0"
+
+[dependencies]
+njord = { version = "0.3.0", path = "../../njord", features = ["mysql"] }
+njord_derive = { version = "0.3.0", path = "../../njord_derive" }
+tokio = { version = "1", features = ["full"] }
+serde = { version = "1.0.210", features = ["derive"] }
+reqwest = { version = "0.12", features = ["json"] }
+serde_json = "1.0.128"
\ No newline at end of file
diff --git a/njord_examples/mysql/README.md b/njord_examples/mysql/README.md
new file mode 100644
index 00000000..d3eac424
--- /dev/null
+++ b/njord_examples/mysql/README.md
@@ -0,0 +1,17 @@
+# MySql
+
+A MySql database will need to be spun up. This can be found in the `docker-compose.yml` file.
+
+Run the following command:
+
+```bash
+docker-compose up -d
+```
+
+Once the database is up and running, we can run the example.
+
+To run this example:
+
+```bash
+cargo r --bin mysql
+```
diff --git a/njord_examples/mysql/init.sql b/njord_examples/mysql/init.sql
new file mode 100644
index 00000000..3276dc18
--- /dev/null
+++ b/njord_examples/mysql/init.sql
@@ -0,0 +1,12 @@
+CREATE TABLE IF NOT EXISTS neo (
+    id INT AUTO_INCREMENT PRIMARY KEY,
+    neo_id VARCHAR(255) NOT NULL,
+    neo_reference_id VARCHAR(255),
+    name VARCHAR(255),
+    name_limited VARCHAR(255),
+    designation VARCHAR(255),
+    nasa_jpl_url VARCHAR(255),
+    absolute_magnitude_h FLOAT,
+    is_potentially_hazardous_asteroid VARCHAR(8),
+    is_sentry_object VARCHAR(8)
+);
\ No newline at end of file
diff --git a/njord_examples/mysql/src/main.rs b/njord_examples/mysql/src/main.rs
new file mode 100644
index 00000000..fc832b0f
--- /dev/null
+++ b/njord_examples/mysql/src/main.rs
@@ -0,0 +1,89 @@
+use std::fmt::Error;
+
+use crate::schema::NearEarthObject;
+use njord::keys::AutoIncrementPrimaryKey;
+use njord::mysql;
+use reqwest::header::ACCEPT;
+use serde::Deserialize;
+use serde_json::Value;
+
+mod schema;
+
+const API_URL: &str = "https://api.nasa.gov/neo/rest/v1";
+
+#[derive(Debug, Deserialize)]
+pub struct NearEarthObjectResponse {
+    #[serde(rename = "id")]
+    pub neo_id: String,
+    pub neo_reference_id: String,
+    pub name: String,
+    pub name_limited: String,
+    pub designation: String,
+    pub nasa_jpl_url: String,
+    pub absolute_magnitude_h: f64,
+    pub is_potentially_hazardous_asteroid: bool,
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Error> {
+    let neo = get_near_earth_objects(0, 10).await;
+    let mut near_earth_objects: Vec<NearEarthObject> = Vec::new();
+
+    match neo {
+        Ok(data) => {
+            for obj in data["near_earth_objects"].as_array().unwrap() {
+                let response_obj: NearEarthObjectResponse =
+                    serde_json::from_value(obj.clone()).unwrap();
+
+                let near_earth_obj = NearEarthObject {
+                    id: AutoIncrementPrimaryKey::default(), // Auto-generate id
+                    neo_id: response_obj.neo_id,            // Map id to neo_id
+                    neo_reference_id: response_obj.neo_reference_id,
+                    name: response_obj.name,
+                    name_limited: response_obj.name_limited,
+                    designation: response_obj.designation,
+                    nasa_jpl_url: response_obj.nasa_jpl_url,
+                    absolute_magnitude_h: response_obj.absolute_magnitude_h,
+                    is_potentially_hazardous_asteroid: response_obj
+                        .is_potentially_hazardous_asteroid,
+                    is_sentry_object: false, // Set this field as needed
+                };
+                println!("{:#?}", near_earth_obj);
+                near_earth_objects.push(near_earth_obj);
+            }
+        }
+        Err(err) => eprintln!("Error: {}", err),
+    }
+
+    let url = "mysql://user:password@localhost:3306/neo";
+    let mut conn = mysql::open(url).unwrap();
+
+    match mysql::insert(&mut conn, near_earth_objects) {
+        Ok(_) => println!("Near Earth Objects inserted successfully"),
+        Err(err) => eprintln!("Error: {}", err),
+    };
+
+    Ok(())
+}
+
+async fn get_near_earth_objects(page: u32, size: u32) -> Result<Value, Box<dyn std::error::Error>> {
+    let client = reqwest::Client::new();
+    let endpoint = format!(
+        "{}/neo/browse?page={}&size={}&api_key=DEMO_KEY",
+        API_URL, page, size
+    );
+
+    let response = client
+        .get(endpoint)
+        .header(ACCEPT, "application/json")
+        .send()
+        .await?;
+
+    let response_text = response.text().await?;
+
+    // println!("response = {:#?}", response_text);
+
+    let v: Value = serde_json::from_str(&response_text)?;
+
+    Ok(v)
+}
diff --git a/njord_examples/mysql/src/schema.rs b/njord_examples/mysql/src/schema.rs
new file mode 100644
index 00000000..14465638
--- /dev/null
+++ b/njord_examples/mysql/src/schema.rs
@@ -0,0 +1,21 @@
+use njord::keys::AutoIncrementPrimaryKey;
+#[allow(unused_imports)]
+use njord::table::Table;
+use njord_derive::Table;
+use serde::Deserialize;
+
+#[derive(Table, Deserialize, Debug)]
+#[table_name = "neo"]
+pub struct NearEarthObject {
+    pub id: AutoIncrementPrimaryKey<usize>,
+    #[serde(rename = "id")]
+    pub neo_id: String,
+    pub neo_reference_id: String,
+    pub name: String,
+    pub name_limited: String,
+    pub designation: String,
+    pub nasa_jpl_url: String,
+    pub absolute_magnitude_h: f64,
+    pub is_potentially_hazardous_asteroid: bool,
+    pub is_sentry_object: bool,
+}


Test patch: