Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,19 @@ pub(crate) struct Args {
pub(crate) verbose: u8,

#[arg(short = 't', long = "table")]
#[arg(help = "Table or views to query. Can be used multiple times.")]
#[arg(action=ArgAction::Set)]
pub(crate) table: Option<Vec<String>>,
#[arg(help = "Table or view to query. Can be used multiple times")]
#[arg(action=ArgAction::Append)]
pub(crate) table: Vec<String>,

#[arg(short = 's', long = "sql")]
#[arg(help = "SQL query to run. Can be used multiple times")]
#[arg(action=ArgAction::Append)]
pub(crate) query: Vec<String>,

#[arg(short = 'i', long = "ignore")]
#[arg(help = "Ignore non-readonly queries")]
#[arg(action=ArgAction::SetTrue)]
pub(crate) ignore_non_readonly: bool,

#[arg(help = "Pattern to match every cell with")]
pub(crate) pattern: String,
Expand Down
36 changes: 14 additions & 22 deletions src/cell_to_string.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
use std::fmt::Display;

use log::warn;
use sqlx::sqlite::SqliteValueRef;
use sqlx::Decode;
use sqlx::Sqlite;
use sqlx::Type;
use sqlx::TypeInfo;
use sqlx::ValueRef;

// REVIEW: better way convert an error to a string?
fn errr_format(value: impl Display) -> String {
format!("{value}")
}

pub(crate) fn sqlite_cell_to_string(value_ref: SqliteValueRef) -> Result<Option<String>, String> {
// TODO: add an option to override types in some extent

if value_ref.is_null() {
return Ok(None);
}
Expand All @@ -24,41 +13,45 @@ pub(crate) fn sqlite_cell_to_string(value_ref: SqliteValueRef) -> Result<Option<

// TEXT
if <String as Type<Sqlite>>::compatible(&type_info) {
let value = <String as Decode<Sqlite>>::decode(value_ref).map_err(errr_format)?;
let value =
<String as Decode<Sqlite>>::decode(value_ref).map_err(|value| value.to_string())?;
return Ok(Some(value));
}

// // INTEGER, INT4
if <i64 as Type<Sqlite>>::compatible(&type_info) {
let value = <i64 as Decode<Sqlite>>::decode(value_ref).map_err(errr_format)?;
let value =
<i64 as Decode<Sqlite>>::decode(value_ref).map_err(|value| value.to_string())?;
return Ok(Some(format!("{value}")));
}
// REAL
if <f64 as Type<Sqlite>>::compatible(&type_info) {
let value = <f64 as Decode<Sqlite>>::decode(value_ref).map_err(errr_format)?;
let value =
<f64 as Decode<Sqlite>>::decode(value_ref).map_err(|value| value.to_string())?;
return Ok(Some(format!("{value}")));
}
// BOOL?
if <bool as Type<Sqlite>>::compatible(&type_info) {
let value = <bool as Decode<Sqlite>>::decode(value_ref).map_err(errr_format)?;
let value =
<bool as Decode<Sqlite>>::decode(value_ref).map_err(|value| value.to_string())?;
return Ok(Some(format!("{value}")));
}
// DateTime
if <chrono::DateTime<chrono::Local> as Type<Sqlite>>::compatible(&type_info) {
let value = <chrono::DateTime<chrono::Local> as Decode<Sqlite>>::decode(value_ref)
.map_err(errr_format)?;
.map_err(|value| value.to_string())?;
return Ok(Some(value.to_rfc3339()));
}
// Date
if <chrono::NaiveDate as Type<Sqlite>>::compatible(&type_info) {
let value =
<chrono::NaiveDate as Decode<Sqlite>>::decode(value_ref).map_err(errr_format)?;
let value = <chrono::NaiveDate as Decode<Sqlite>>::decode(value_ref)
.map_err(|value| value.to_string())?;
return Ok(Some(value.format("%Y-%m-%d").to_string()));
}
// Time
if <chrono::NaiveTime as Type<Sqlite>>::compatible(&type_info) {
let value =
<chrono::NaiveTime as Decode<Sqlite>>::decode(value_ref).map_err(errr_format)?;
let value = <chrono::NaiveTime as Decode<Sqlite>>::decode(value_ref)
.map_err(|value| value.to_string())?;
return Ok(Some(value.format("%H:%M:%S").to_string()));
}

Expand All @@ -73,6 +66,5 @@ pub(crate) fn sqlite_cell_to_string(value_ref: SqliteValueRef) -> Result<Option<
return Ok(None);
}

warn!("Unknown cell type: {}", type_info.name());
Ok(None)
Err("Unknown type".into())
}
72 changes: 72 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use log::Level as LogLevel;
use sqlparser::parser::ParserError;

pub(crate) type Level = LogLevel;

pub(crate) enum SQLError {
Regex(regex::Error),
QueryError(QueryError),
ParseError(ParserError),
SqlX((String, sqlx::Error)),
ConvertCell((String, String)),
Io((String, std::io::Error)),
}

pub(crate) enum QueryError {
ReadOnlyQueryAllowed,
}

impl SQLError {
/// Report and return error code if needed
pub fn report(&self, level: Level) -> i32 {
match self {
SQLError::Regex(error) => {
log::log!(level, "Regex error: {error}");

64
}
SQLError::QueryError(query_error) => {
match query_error {
QueryError::ReadOnlyQueryAllowed => {
log::log!(level, "Only readonly query is allowed");
}
}

65
}
SQLError::ParseError(error) => {
log::log!(level, "Unable to parse SQL: {error}");

66
}
SQLError::Io((context, error)) => {
let context = format_context(context);
log::log!(level, "IO error{context}: {error}");

70
}
SQLError::SqlX((context, error)) => {
let context = format_context(context);
log::log!(level, "SQL error{context}: {error}");

74
}
SQLError::ConvertCell((context, error)) => {
let context = format_context(context);

log::log!(level, "Cell conversion error{context}: {error}");

73
}
}
}
}

#[inline]
fn format_context(context: &String) -> String {
if context.is_empty() {
String::new()
} else {
format!(" ({context})")
}
}
Loading