Skip to content
This repository was archived by the owner on Oct 27, 2023. It is now read-only.

Commit e471471

Browse files
committed
Initial commit
0 parents  commit e471471

File tree

4 files changed

+180
-0
lines changed

4 files changed

+180
-0
lines changed

.gitignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Created by https://www.toptal.com/developers/gitignore/api/rust
2+
# Edit at https://www.toptal.com/developers/gitignore?templates=rust
3+
4+
### Rust ###
5+
# Generated by Cargo
6+
# will have compiled files and executables
7+
debug/
8+
target/
9+
10+
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
11+
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
12+
Cargo.lock
13+
14+
# These are backup files generated by rustfmt
15+
**/*.rs.bk
16+
17+
# MSVC Windows builds of rustc generate these, which store debugging information
18+
*.pdb
19+
20+
# End of https://www.toptal.com/developers/gitignore/api/rust
21+
samples/

.vscode/launch.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"type": "lldb",
9+
"request": "launch",
10+
"name": "Debug executable 'quick-db-migration-tool'",
11+
"cargo": {
12+
"args": [
13+
"build",
14+
"--bin=quick-db-migration-tool",
15+
"--package=quick-db-migration-tool"
16+
],
17+
"filter": {
18+
"name": "quick-db-migration-tool",
19+
"kind": "bin"
20+
}
21+
},
22+
"args": [],
23+
"cwd": "${workspaceFolder}"
24+
},
25+
{
26+
"type": "lldb",
27+
"request": "launch",
28+
"name": "Debug unit tests in executable 'quick-db-migration-tool'",
29+
"cargo": {
30+
"args": [
31+
"test",
32+
"--no-run",
33+
"--bin=quick-db-migration-tool",
34+
"--package=quick-db-migration-tool"
35+
],
36+
"filter": {
37+
"name": "quick-db-migration-tool",
38+
"kind": "bin"
39+
}
40+
},
41+
"args": [
42+
"--input",
43+
"./samples/json.sqlite",
44+
"--output",
45+
"../test.sqlite"
46+
],
47+
"cwd": "${workspaceFolder}"
48+
}
49+
]
50+
}

Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "quick-db-migration-tool"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
clap = { version = "3.2.16", features = ["derive"] }
10+
serde_json = "1.0.82"
11+
sqlite = "0.27.0"

src/main.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use clap::Parser;
2+
use serde_json::Value;
3+
use sqlite::Connection;
4+
use std::path::Path;
5+
6+
#[derive(Parser, Debug)]
7+
#[clap(author, version, about, long_about = None)]
8+
struct Args {
9+
/// Sqlite file to migrate
10+
#[clap(short, long, value_parser)]
11+
input: String,
12+
13+
/// Sqlite output file
14+
#[clap(short, long, value_parser)]
15+
output: String,
16+
}
17+
18+
fn get_tables(connection: &Connection) -> Vec<String> {
19+
let cursor = connection
20+
.prepare("SELECT name FROM sqlite_schema WHERE type = ? AND name NOT LIKE ?")
21+
.unwrap()
22+
.bind(1, "table")
23+
.unwrap()
24+
.bind(2, "sqlite_%")
25+
.unwrap()
26+
.into_cursor();
27+
28+
cursor.map(|row| row.unwrap().get::<String, _>(0)).collect()
29+
}
30+
31+
fn process_table(connection: &Connection, conn_out: &Connection, table: String) {
32+
println!("Processing {} table", table);
33+
34+
conn_out
35+
.execute(format!("CREATE TABLE '{}' (ID TEXT, json TEXT)", table))
36+
.unwrap();
37+
38+
let mut cursor = connection
39+
.prepare(format!("SELECT ID, json FROM '{}'", table))
40+
.unwrap()
41+
.into_cursor();
42+
43+
while let Some(Ok(row)) = cursor.next() {
44+
let id = row.get::<String, _>("ID");
45+
let json = process_row(&id, row.get("json"));
46+
conn_out
47+
.execute(format!(
48+
"INSERT INTO '{}' (ID, json)
49+
VALUES ('{}', '{}'); ",
50+
table, &id, json
51+
))
52+
.unwrap();
53+
}
54+
}
55+
56+
fn process_row(id: &str, json: String) -> String {
57+
println!("Processing row with key {}", id);
58+
59+
// Parsing json until it matches
60+
let tmp_val = serde_json::from_str::<Value>(&json).unwrap();
61+
let mut tmp = tmp_val.as_str().unwrap().to_owned();
62+
loop {
63+
let current_val = serde_json::from_str::<Value>(&tmp).unwrap();
64+
if current_val.as_str().is_none() {
65+
break;
66+
}
67+
68+
tmp = current_val.as_str().unwrap().to_string();
69+
}
70+
71+
tmp
72+
}
73+
74+
fn main() {
75+
let args = Args::parse();
76+
if args.input == args.output {
77+
panic!("Can't have the same output for input");
78+
}
79+
80+
println!("Loading {} sqlite file", args.input);
81+
82+
let connection = sqlite::open(args.input).expect("Couldn't find input sqlite file");
83+
println!("Sqlite loaded");
84+
println!("Creating output sqlite file");
85+
if Path::new(&args.output).exists() {
86+
panic!("Output file already exist");
87+
}
88+
89+
let conn_output = sqlite::open(args.output).expect("Couldn't create output sqlite file");
90+
println!("Getting tables");
91+
92+
let tables = get_tables(&connection);
93+
println!("Found tables {:?}", tables);
94+
95+
for table in tables {
96+
process_table(&connection, &conn_output, table);
97+
}
98+
}

0 commit comments

Comments
 (0)