sqlx/sqlx-core/src/sqlite/arguments.rs
Ryan Leckey 8d188c5f1a feat: expose column information on Row
- add database-specific Column types: MySqlColumn, PgColumn, etc.
 - add Row::columns() -> &[DB::Column]
 - add Row::column(I) and Row::try_column(I)
2020-07-05 03:48:36 -07:00

110 lines
3.1 KiB
Rust

use std::borrow::Cow;
use atoi::atoi;
use libsqlite3_sys::SQLITE_OK;
use crate::arguments::Arguments;
use crate::encode::{Encode, IsNull};
use crate::error::Error;
use crate::sqlite::statement::{SqliteStatement, StatementHandle};
use crate::sqlite::Sqlite;
#[derive(Debug, Clone)]
pub enum SqliteArgumentValue<'q> {
Null,
Text(Cow<'q, str>),
Blob(Cow<'q, [u8]>),
Double(f64),
Int(i32),
Int64(i64),
}
#[derive(Default)]
pub struct SqliteArguments<'q> {
pub(crate) values: Vec<SqliteArgumentValue<'q>>,
}
impl<'q> SqliteArguments<'q> {
pub(crate) fn add<T>(&mut self, value: T)
where
T: Encode<'q, Sqlite>,
{
if let IsNull::Yes = value.encode(&mut self.values) {
self.values.push(SqliteArgumentValue::Null);
}
}
}
impl<'q> Arguments<'q> for SqliteArguments<'q> {
type Database = Sqlite;
fn reserve(&mut self, len: usize, _size_hint: usize) {
self.values.reserve(len);
}
fn add<T>(&mut self, value: T)
where
T: Encode<'q, Self::Database>,
{
self.add(value)
}
}
impl SqliteArguments<'_> {
pub(super) fn bind(&self, statement: &SqliteStatement) -> Result<(), Error> {
let mut arg_i = 0;
for handle in &statement.handles {
let cnt = handle.bind_parameter_count();
for param_i in 1..=cnt {
// figure out the index of this bind parameter into our argument tuple
let n: usize = if let Some(name) = handle.bind_parameter_name(param_i) {
if name.starts_with('?') {
// parameter should have the form ?NNN
atoi(name[1..].as_bytes()).expect("parameter of the form ?NNN")
} else {
return Err(err_protocol!("unsupported SQL parameter format: {}", name));
}
} else {
arg_i += 1;
arg_i
};
if n > self.values.len() {
// SQLite treats unbound variables as NULL
// we reproduce this here
// If you are reading this and think this should be an error, open an issue and we can
// discuss configuring this somehow
// Note that the query macros have a different way of enforcing
// argument arity
break;
}
self.values[n - 1].bind(handle, param_i)?;
}
}
Ok(())
}
}
impl SqliteArgumentValue<'_> {
fn bind(&self, handle: &StatementHandle, i: usize) -> Result<(), Error> {
use SqliteArgumentValue::*;
let status = match self {
Text(v) => handle.bind_text(i, v),
Blob(v) => handle.bind_blob(i, v),
Int(v) => handle.bind_int(i, *v),
Int64(v) => handle.bind_int64(i, *v),
Double(v) => handle.bind_double(i, *v),
Null => handle.bind_null(i),
};
if status != SQLITE_OK {
return Err(handle.last_error().into());
}
Ok(())
}
}