2019-06-07 14:57:11 +00:00
|
|
|
use super::*;
|
2020-06-06 10:57:43 +00:00
|
|
|
use anyhow::Result;
|
2019-06-07 14:57:11 +00:00
|
|
|
use lazy_static::lazy_static;
|
2019-06-18 10:14:09 +00:00
|
|
|
use log::*;
|
2019-06-07 18:12:24 +00:00
|
|
|
use rusqlite::types::ValueRef;
|
2019-06-07 14:57:11 +00:00
|
|
|
use rusqlite::*;
|
|
|
|
use std::convert::TryInto;
|
|
|
|
|
|
|
|
static EXTENSIONS: &[&str] = &["db", "db3", "sqlite", "sqlite3"];
|
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
static ref METADATA: AdapterMeta = AdapterMeta {
|
|
|
|
name: "sqlite".to_owned(),
|
|
|
|
version: 1,
|
2019-06-07 22:04:48 +00:00
|
|
|
description:
|
|
|
|
"Uses sqlite bindings to convert sqlite databases into a simple plain text format"
|
|
|
|
.to_owned(),
|
2019-06-16 10:19:01 +00:00
|
|
|
recurses: false, // set to true if we decide to make sqlite blobs searchable (gz blob in db is kinda common I think)
|
2019-06-11 11:34:04 +00:00
|
|
|
fast_matchers: EXTENSIONS
|
2019-06-07 14:57:11 +00:00
|
|
|
.iter()
|
2019-06-11 11:34:04 +00:00
|
|
|
.map(|s| FastMatcher::FileExtension(s.to_string()))
|
2019-06-07 14:57:11 +00:00
|
|
|
.collect(),
|
2019-06-11 11:34:04 +00:00
|
|
|
slow_matchers: Some(vec![SlowMatcher::MimeType(
|
|
|
|
"application/x-sqlite3".to_owned()
|
|
|
|
)])
|
2019-06-07 14:57:11 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct SqliteAdapter;
|
|
|
|
|
|
|
|
impl SqliteAdapter {
|
|
|
|
pub fn new() -> SqliteAdapter {
|
|
|
|
SqliteAdapter
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl GetMetadata for SqliteAdapter {
|
|
|
|
fn metadata(&self) -> &AdapterMeta {
|
|
|
|
&METADATA
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn format_blob(b: ValueRef) -> String {
|
|
|
|
use ValueRef::*;
|
|
|
|
match b {
|
|
|
|
Null => "NULL".to_owned(),
|
|
|
|
Integer(i) => format!("{}", i),
|
|
|
|
Real(i) => format!("{}", i),
|
2019-07-27 19:08:50 +00:00
|
|
|
Text(i) => format!("'{}'", String::from_utf8_lossy(i).replace("'", "''")),
|
2019-06-07 14:57:11 +00:00
|
|
|
Blob(b) => format!(
|
|
|
|
"[blob {}B]",
|
|
|
|
size_format::SizeFormatterSI::new(
|
|
|
|
// can't be larger than 2GB anyways
|
|
|
|
b.len().try_into().unwrap()
|
|
|
|
)
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FileAdapter for SqliteAdapter {
|
2020-06-06 10:57:43 +00:00
|
|
|
fn adapt(&self, ai: AdaptInfo, _detection_reason: &SlowMatcher) -> Result<()> {
|
2019-06-07 14:57:11 +00:00
|
|
|
let AdaptInfo {
|
|
|
|
is_real_file,
|
|
|
|
filepath_hint,
|
|
|
|
oup,
|
|
|
|
line_prefix,
|
|
|
|
..
|
|
|
|
} = ai;
|
|
|
|
if !is_real_file {
|
2019-06-13 14:26:03 +00:00
|
|
|
// db is in an archive
|
2019-06-07 14:57:11 +00:00
|
|
|
// todo: read to memory and then use that blob if size < max
|
|
|
|
writeln!(oup, "{}[rga: skipping sqlite in archive]", line_prefix,)?;
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
let inp_fname = filepath_hint;
|
|
|
|
|
|
|
|
let conn = Connection::open_with_flags(inp_fname, OpenFlags::SQLITE_OPEN_READ_ONLY)?;
|
|
|
|
let tables: Vec<String> = conn
|
|
|
|
.prepare("select name from sqlite_master where type='table'")?
|
|
|
|
.query_map(NO_PARAMS, |r| r.get::<_, String>(0))?
|
|
|
|
.filter_map(|e| e.ok())
|
|
|
|
.collect();
|
2019-06-18 10:14:09 +00:00
|
|
|
debug!("db has {} tables", tables.len());
|
2019-06-07 14:57:11 +00:00
|
|
|
for table in tables {
|
|
|
|
// can't use query param at that position
|
|
|
|
let mut sel = conn.prepare(&format!(
|
|
|
|
"select * from {}",
|
|
|
|
rusqlite::vtab::escape_double_quote(&table)
|
|
|
|
))?;
|
|
|
|
let mut z = sel.query(NO_PARAMS)?;
|
|
|
|
let col_names: Vec<String> = z
|
|
|
|
.column_names()
|
|
|
|
.ok_or_else(|| format_err!("no column names"))?
|
|
|
|
.into_iter()
|
|
|
|
.map(|e| e.to_owned())
|
|
|
|
.collect();
|
|
|
|
// writeln!(oup, "{}: {}", table, cols.join(", "))?;
|
|
|
|
|
|
|
|
// kind of shitty (lossy) output. maybe output real csv or something?
|
|
|
|
while let Some(row) = z.next()? {
|
|
|
|
writeln!(
|
|
|
|
oup,
|
2019-06-12 15:23:30 +00:00
|
|
|
"{}{}: {}",
|
|
|
|
line_prefix,
|
2019-06-07 14:57:11 +00:00
|
|
|
table,
|
|
|
|
col_names
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, e)| format!("{}={}", e, format_blob(row.get_raw(i))))
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
.join(", ")
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|