diff --git a/src/error.rs b/src/error.rs index adca04d..45b6197 100644 --- a/src/error.rs +++ b/src/error.rs @@ -77,6 +77,9 @@ pub enum Error { /// Error available for the implementors of the `ToSql` trait. ToSqlConversionFailure(Box), + + /// Error when the SQL is not a `SELECT`, is not read-only. + InvalidQuery, } impl From for Error { @@ -132,6 +135,7 @@ impl fmt::Display for Error { #[cfg(feature = "functions")] Error::UserFunctionError(ref err) => err.fmt(f), Error::ToSqlConversionFailure(ref err) => err.fmt(f), + Error::InvalidQuery => write!(f, "Query is not read-only"), } } } @@ -160,6 +164,7 @@ impl error::Error for Error { #[cfg(feature = "functions")] Error::UserFunctionError(ref err) => err.description(), Error::ToSqlConversionFailure(ref err) => err.description(), + Error::InvalidQuery => "query is not read-only", } } @@ -178,7 +183,8 @@ impl error::Error for Error { Error::InvalidColumnName(_) | Error::InvalidColumnType(_, _) | Error::InvalidPath(_) | - Error::StatementChangedRows(_) => None, + Error::StatementChangedRows(_) | + Error::InvalidQuery => None, #[cfg(feature = "functions")] Error::InvalidFunctionParameterType(_, _) => None, diff --git a/src/raw_statement.rs b/src/raw_statement.rs index 5b99b8c..f4deda1 100644 --- a/src/raw_statement.rs +++ b/src/raw_statement.rs @@ -83,6 +83,11 @@ impl RawStatement { self.0 = ptr::null_mut(); r } + + #[cfg(feature = "bundled")] + pub fn readonly(&self) -> bool { + unsafe { ffi::sqlite3_stmt_readonly(self.0) != 0 } + } } impl Drop for RawStatement { diff --git a/src/statement.rs b/src/statement.rs index 5545617..8d3b753 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -154,6 +154,7 @@ impl<'conn> Statement<'conn> { /// /// Will return `Err` if binding parameters fails. pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> Result> { + try!(self.check_readonly()); try!(self.bind_parameters(params)); Ok(Rows::new(self)) } @@ -181,6 +182,7 @@ impl<'conn> Statement<'conn> { /// /// Will return `Err` if binding parameters fails. pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> Result> { + try!(self.check_readonly()); try!(self.bind_parameters_named(params)); Ok(Rows::new(self)) } @@ -465,6 +467,19 @@ impl<'conn> Statement<'conn> { mem::swap(&mut stmt, &mut self.stmt); self.conn.decode_result(stmt.finalize()) } + + #[cfg(not(feature = "bundled"))] + fn check_readonly(&self) -> Result<()> { + Ok(()) + } + + #[cfg(feature = "bundled")] + fn check_readonly(&self) -> Result<()> { + if !self.stmt.readonly() { + return Err(Error::InvalidQuery); + } + Ok(()) + } } impl<'conn> Into for Statement<'conn> {