kubo / rust-oracle

Oracle driver for Rust
178 stars 43 forks source link

`*mut oracle::binding::binding::dpiStmt` cannot be sent between threads safely #82

Closed krstn420 closed 3 months ago

krstn420 commented 3 months ago

Hi Kubo,

thanks again for your hard work. I created this issue, as #81 was recently closed. I followed your suggestions to use cache: HashMap<String, Mutex<ResultSet<'static, Row>>> and stmt.into_result_set::<Row>(&[]) instead of stmt.query(&[]). But for me the same issue still exists:

error[E0277]: `*mut oracle::binding::binding::dpiStmt` cannot be sent between threads safely
   --> src\main.rs:35:17
    |
35  | fn fetch(cache: State<'_, MutexCacheManager>, cache_key: String,) -> Result<String, String> {
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*mut oracle::binding::binding::dpiStmt` cannot be sent between threads safely
    |
    = help: within `ResultSet<'static, Row>`, the trait `Send` is not implemented for `*mut oracle::binding::binding::dpiStmt`
note: required because it appears within the type `Stmt`
   --> C:\Users\xxx\.cargo\git\checkouts\rust-oracle-858dd8ce065f6a17\e98d639\src\statement.rs:388:19
    |
388 | pub(crate) struct Stmt {
    |                   ^^^^
note: required because it appears within the type `StmtHolder<'static>`
   --> C:\Users\xxx\.cargo\git\checkouts\rust-oracle-858dd8ce065f6a17\e98d639\src\row.rs:106:6
    |
106 | enum StmtHolder<'a> {
    |      ^^^^^^^^^^
note: required because it appears within the type `ResultSet<'static, Row>`
   --> C:\Users\xxx\.cargo\git\checkouts\rust-oracle-858dd8ce065f6a17\e98d639\src\row.rs:132:12
    |
132 | pub struct ResultSet<'a, T>
    |            ^^^^^^^^^
    = note: required for `std::sync::Mutex<ResultSet<'static, Row>>` to implement `Send`
    = note: required because it appears within the type `(String, Mutex<ResultSet<'static, Row>>)`
    = note: required for `hashbrown::raw::RawTable<(String, std::sync::Mutex<ResultSet<'static, Row>>)>` to implement `Send`
note: required because it appears within the type `HashMap<String, Mutex<ResultSet<'static, Row>>, RandomState>`
   --> /rust/deps\hashbrown-0.14.2\src\map.rs:190:12
note: required because it appears within the type `HashMap<String, Mutex<ResultSet<'static, Row>>>`
   --> C:\Users\xxx\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\std\src\collections\hash\map.rs:216:12
    |
216 | pub struct HashMap<K, V, S = RandomState> {
    |            ^^^^^^^
note: required because it appears within the type `CacheManager`
   --> src\main.rs:12:12
    |
12  | pub struct CacheManager {
    |            ^^^^^^^^^^^^
    = note: required for `std::sync::Mutex<CacheManager>` to implement `Send`
note: required because it appears within the type `MutexCacheManager`
   --> src\main.rs:17:12
    |
17  | pub struct MutexCacheManager(pub Mutex<CacheManager>);
    |            ^^^^^^^^^^^^^^^^^
note: required by a bound in `State`
   --> C:\Users\xxx\.cargo\registry\src\index.crates.io-6f17d22bba15001f\tauri-1.6.1\src\state.rs:14:25
    |
14  | pub struct State<'r, T: Send + Sync + 'static>(&'r T);
    |                         ^^^^ required by this bound in `State`

My reduced example follows:

//problem: `*mut oracle::binding::binding::dpiStmt` cannot be sent between threads safely

// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use std::{collections::HashMap, sync::Mutex};

use oracle::{Connection, ResultSet, Row};
use tauri::State;

#[derive(Default, Debug)]
pub struct CacheManager {
    cache: HashMap<String, Mutex<ResultSet<'static, Row>>>
}

#[derive(Default)]
pub struct MutexCacheManager(pub Mutex<CacheManager>);

/* 
impl CacheManager {
    pub fn write_cache(&mut self, key: &str, data: ResultSet<'static, Row>) {
        let key = key.to_string();
        let value = data;
        println!("write cache for {}", key);
        self.cache.insert(key, value);
    }
    pub fn read_cache(&self, key: &str) -> &ResultSet<'static, Row> {
        let key = key.to_string();
        println!("read cache for {}", key);
        self.cache.get(&key).unwrap()
    }
} */

#[tauri::command]
fn fetch(cache: State<'_, MutexCacheManager>, cache_key: String,) -> Result<String, String> {
    let max_elements = 50;

    let connection_string = format!(
        "(description= (retry_count=1)(retry_delay=1)(address=(protocol=tcps)(port={})(host={}))(connect_data=(service_name={}))(security=(ssl_server_dn_match=yes)))",
        1522,
        "adb.eu-frankfurt-1.oraclecloud.com",
        "xxx"
    );
    let user: String = "xxx".to_string();
    let pass: String = "xxx".to_string();

    match Connection::connect(user, pass, connection_string) {
        Ok(conn) => {
            let query: String = "select * from all_objects".to_string();

            let mut stmt = conn
                .statement(&query)
                .fetch_array_size(max_elements)
                .build()
                .unwrap();
            let mut rows = stmt.into_result_set::<Row>(&[]).unwrap();
            println!("print first 50 to screen:");
            let mut x = 0;
            for row_result in rows.by_ref().take(max_elements) {
                println!("{:?}", row_result.as_ref().unwrap().sql_values());
            }
            println!("{} The rest should be put in the cache", x);

            //let mut cache_binding = cache.0.write().unwrap();
            //cache_binding.write_cache(&cache_key, &rows);

            println!("done");
            Ok(cache_key)
        }
        Err(err) => Err(err.to_string()),
    }
}

fn main() {
    println!("Tauri Starting...");

    tauri::Builder
        ::default()
        .manage(MutexCacheManager(Default::default()))
        .invoke_handler(tauri::generate_handler![fetch])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
kubo commented 3 months ago
   --> C:\Users\xxx\.cargo\git\checkouts\rust-oracle-858dd8ce065f6a17\e98d639\src\statement.rs:388:19

e98d639 looks a git commit ID. It isn't the latest in the repository.

Could you run cargo update oracle in order to update Cargo.lock? See https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html

krstn420 commented 3 months ago

Ah, sorry, i thought "cargo clean" was enough. Thank you, for your help, my tests with new commit ID where fine.