Skip to content

Commit

Permalink
row count returns Option
Browse files Browse the repository at this point in the history
  • Loading branch information
pacman82 committed Aug 25, 2022
1 parent 7635f89 commit 6e8d1a7
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 28 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 0.48.0

* Both `Preallocated::row_count` and `Prepared::row_count` now return `Option<usize>` instead of `isize`.

## 0.47.0

* Support getting number of rows affected by `INSERT`, `UPDATE` or `DELETE`.
Expand Down
2 changes: 1 addition & 1 deletion odbc-api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "odbc-api"
version = "0.47.0"
version = "0.48.0"
authors = ["Markus Klein"]
edition = "2021"
license = "MIT"
Expand Down
43 changes: 32 additions & 11 deletions odbc-api/src/preallocated.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
execute::{execute_columns, execute_tables, execute_with_parameters},
handles::{SqlText, Statement, StatementImpl},
handles::{AsStatementRef, SqlText, Statement, StatementImpl, StatementRef},
CursorImpl, Error, ParameterCollectionRef,
};

Expand Down Expand Up @@ -154,28 +154,49 @@ impl<'o> Preallocated<'o> {
)
}

/// Number of rows affected by the last `INSERT`, `UPDATE` or `DELETE` statment. May return `-1`
/// if row count is not available. Some drivers may also allow to use this to determine how many
/// rows have been fetched using `SELECT`. Most drivers however only know how many rows have
/// been fetched after they have been fetched.
///
/// Number of rows affected by the last `INSERT`, `UPDATE` or `DELETE` statment. May return
/// `None` if row count is not available. Some drivers may also allow to use this to determine
/// how many rows have been fetched using `SELECT`. Most drivers however only know how many rows
/// have been fetched after they have been fetched.
///
/// ```
/// use odbc_api::{Connection, Error};
///
///
/// /// Make everyone rich and return how many colleagues are happy now.
/// fn raise_minimum_salary(conn: &Connection<'_>, new_min_salary: i32) -> Result<isize, Error> {
/// fn raise_minimum_salary(
/// conn: &Connection<'_>,
/// new_min_salary: i32
/// ) -> Result<usize, Error> {
/// // We won't use conn.execute directly, because we need a handle to ask about the number
/// // of changed rows. So let's allocate the statement explicitly.
/// let mut stmt = conn.preallocate()?;
/// stmt.execute(
/// "UPDATE Employees SET salary = ? WHERE salary < ?",
/// (&new_min_salary, &new_min_salary),
/// )?;
/// let number_of_updated_rows = stmt.row_count()?;
/// let number_of_updated_rows = stmt
/// .row_count()?
/// .expect("For UPDATE statements row count must always be available.");
/// Ok(number_of_updated_rows)
/// }
/// ```
pub fn row_count(&mut self) -> Result<isize, Error> {
self.statement.row_count().into_result(&self.statement)
pub fn row_count(&mut self) -> Result<Option<usize>, Error> {
self.statement
.row_count()
.into_result(&self.statement)
.map(|count| {
// ODBC returns -1 in case a row count is not available
if count == -1 {
None
} else {
Some(count.try_into().unwrap())
}
})
}
}

impl<'o> AsStatementRef for Preallocated<'o> {
fn as_stmt_ref(&mut self) -> StatementRef<'_> {
self.statement.as_stmt_ref()
}
}
30 changes: 19 additions & 11 deletions odbc-api/src/prepared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,35 +219,43 @@ where
unsafe { ColumnarBulkInserter::new(stmt, parameter_buffers) }
}

/// Number of rows affected by the last `INSERT`, `UPDATE` or `DELETE` statment. May return `-1`
/// if row count is not available. Some drivers may also allow to use this to determine how many
/// rows have been fetched using `SELECT`. Most drivers however only know how many rows have
/// been fetched after they have been fetched.
///
/// Number of rows affected by the last `INSERT`, `UPDATE` or `DELETE` statment. May return
/// `None` if row count is not available. Some drivers may also allow to use this to determine
/// how many rows have been fetched using `SELECT`. Most drivers however only know how many rows
/// have been fetched after they have been fetched.
///
/// ```
/// use odbc_api::{Connection, Error, IntoParameter};
///
///
/// /// Deletes all comments for every user in the slice. Returns the number of deleted
/// /// comments.
/// pub fn delete_all_comments_from(
/// users: &[&str],
/// conn: Connection<'_>,
/// ) -> Result<isize, Error>
/// ) -> Result<usize, Error>
/// {
/// // Store prepared query for fast repeated execution.
/// let mut prepared = conn.prepare("DELETE FROM Comments WHERE user=?")?;
/// let mut total_deleted_comments = 0;
/// for user in users {
/// prepared.execute(&user.into_parameter())?;
/// total_deleted_comments += prepared.row_count()?;
/// total_deleted_comments += prepared
/// .row_count()?
/// .expect("Row count must always be available for DELETE statements.");
/// }
/// Ok(total_deleted_comments)
/// }
/// ```
pub fn row_count(&mut self) -> Result<isize, Error> {
pub fn row_count(&mut self) -> Result<Option<usize>, Error> {
let stmt = self.statement.as_stmt_ref();
let number_of_affected_rows = stmt.row_count().into_result(&stmt)?;
Ok(number_of_affected_rows)
stmt.row_count().into_result(&stmt).map(|count| {
// ODBC returns -1 in case a row count is not available
if count == -1 {
None
} else {
Some(count.try_into().unwrap())
}
})
}
}

Expand Down
46 changes: 43 additions & 3 deletions odbc-api/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3318,14 +3318,14 @@ fn row_count_one_shot_query(profile: &Profile) {
let row_count = preallocated.row_count().unwrap();

// Then
assert_eq!(2, row_count);
assert_eq!(Some(2), row_count);
}

/// Fire an insert statement adding two rows and verify that the count of changed rows is 2.
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn row_count_prepared_query(profile: &Profile) {
fn row_count_prepared_insert(profile: &Profile) {
// Given
let table_name = table_name!();
let (conn, _table) = profile.given(&table_name, &["INTEGER"]).unwrap();
Expand All @@ -3337,7 +3337,47 @@ fn row_count_prepared_query(profile: &Profile) {
let row_count = prepared.row_count().unwrap();

// Then
assert_eq!(2, row_count);
assert_eq!(Some(2), row_count);
}

#[test_case(MSSQL, None; "Microsoft SQL Server")]
#[test_case(MARIADB, Some(0); "Maria DB")]
#[test_case(SQLITE_3, Some(0); "SQLite 3")]
fn row_count_create_table_preallocated(profile: &Profile, expectation: Option<usize>) {
// Given a name for a table which does not exist
let table_name = table_name!();
let conn = profile.connection().unwrap();
conn.execute(&format!("DROP TABLE IF EXISTS {table_name};"), ()).unwrap();

// When
let mut preallocated = conn.preallocate().unwrap();
preallocated
.execute(&format!("CREATE TABLE {table_name} (a INTEGER);"), ())
.unwrap();
let row_count = preallocated.row_count().unwrap();

// Then
assert_eq!(expectation, row_count);
}


/// Fire an insert statement adding two rows and verify that the count of changed rows is 2.
#[test_case(MSSQL, Some(0); "Microsoft SQL Server")]
#[test_case(MARIADB, Some(0); "Maria DB")]
#[test_case(SQLITE_3, Some(0); "SQLite 3")]
fn row_count_create_table_prepared(profile: &Profile, expectation: Option<usize>) {
// Given a name for a table which does not exist
let table_name = table_name!();
let conn = profile.connection().unwrap();
conn.execute(&format!("DROP TABLE IF EXISTS {table_name};"), ()).unwrap();

// When
let mut prepared = conn.prepare(&format!("CREATE TABLE {table_name} (a INTEGER);")).unwrap();
prepared.execute(()).unwrap();
let row_count = prepared.row_count().unwrap();

// Then
assert_eq!(expectation, row_count);
}

#[test_case(MSSQL; "Microsoft SQL Server")]
Expand Down
2 changes: 1 addition & 1 deletion odbcsv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ readme = "Readme.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
odbc-api = { version = "0.47.0", path = "../odbc-api" }
odbc-api = { version = "0.48.0", path = "../odbc-api" }
csv = "1.1.6"
anyhow = "1.0.62"
stderrlog = "0.5.3"
Expand Down

0 comments on commit 6e8d1a7

Please sign in to comment.