diff --git a/src/addins/mssql/Cargo.lock b/src/addins/mssql/Cargo.lock index 4ffb49d120..9249f2d72f 100644 --- a/src/addins/mssql/Cargo.lock +++ b/src/addins/mssql/Cargo.lock @@ -281,6 +281,26 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "dashmap" +version = "7.0.0-rc2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a1e35a65fe0538a60167f0ada6e195ad5d477f6ddae273943596d4a1a5730b" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "equivalent", + "hashbrown 0.15.2", + "lock_api", + "parking_lot_core", +] + [[package]] name = "dateparser" version = "0.2.1" @@ -813,6 +833,7 @@ dependencies = [ "addin1c", "base64", "chrono", + "dashmap", "dateparser", "serde_json", "thread-id", @@ -1535,6 +1556,7 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ + "getrandom 0.3.3", "js-sys", "wasm-bindgen", ] diff --git a/src/addins/mssql/Cargo.toml b/src/addins/mssql/Cargo.toml index bad5b2e3cd..9b588ed104 100644 --- a/src/addins/mssql/Cargo.toml +++ b/src/addins/mssql/Cargo.toml @@ -21,6 +21,7 @@ tokio-util = { version = "0.7", features = ["compat"] } serde_json = "1.0" chrono = "0.4" base64 = "0.21" -uuid = "1.17.0" +uuid = { version = "1.17.0", features = ["v4"] } dateparser = "0.2.1" -thread-id = "5.0.0" \ No newline at end of file +thread-id = "5.0.0" +dashmap = "7.0.0-rc2" \ No newline at end of file diff --git a/src/addins/mssql/dependencies.log b/src/addins/mssql/dependencies.log index 39cf90c8e8..78e3bc3b67 100644 --- a/src/addins/mssql/dependencies.log +++ b/src/addins/mssql/dependencies.log @@ -1,13 +1,13 @@ "MAIN ---" - linux-vdso.so.1 (0x00007fffb1fbc000) - libssl.so.3 => /lib64/libssl.so.3 (0x00007d48d3200000) - libcrypto.so.3 => /lib64/libcrypto.so.3 (0x00007d48d2a00000) - libm.so.6 => /lib64/libm.so.6 (0x00007d48d2600000) - libpthread.so.0 => /lib64/libpthread.so.0 (0x00007d48d2200000) - libc.so.6 => /lib64/libc.so.6 (0x00007d48d1e00000) - libdl.so.2 => /lib64/libdl.so.2 (0x00007d48d1a00000) - /lib64/ld-linux-x86-64.so.2 (0x00007d48d3800000) - libz.so.1 => /lib64/libz.so.1 (0x00007d48d1600000) + linux-vdso.so.1 (0x00007fff79bfd000) + libssl.so.3 => /lib64/libssl.so.3 (0x0000793932800000) + libcrypto.so.3 => /lib64/libcrypto.so.3 (0x0000793932000000) + libm.so.6 => /lib64/libm.so.6 (0x0000793931c00000) + libpthread.so.0 => /lib64/libpthread.so.0 (0x0000793931800000) + libc.so.6 => /lib64/libc.so.6 (0x0000793931400000) + libdl.so.2 => /lib64/libdl.so.2 (0x0000793931000000) + /lib64/ld-linux-x86-64.so.2 (0x0000793932e00000) + libz.so.1 => /lib64/libz.so.1 (0x0000793930c00000) GLIBC_2.2.5 GLIBC_2.12 GLIBC_2.3 diff --git a/src/addins/mssql/src/component/backend.rs b/src/addins/mssql/src/component/backend.rs index db48a937ff..f53c531352 100644 --- a/src/addins/mssql/src/component/backend.rs +++ b/src/addins/mssql/src/component/backend.rs @@ -29,9 +29,9 @@ enum BackendCommand { }, Execute { query: String, - params_json: String, + params_json: Vec, force_result: bool, - response: Sender, + response: Sender>, String>>, }, Shutdown, } @@ -95,10 +95,10 @@ impl MSSQLBackend { BackendCommand::Execute { query, params_json, force_result, response } => { let result = if let Some(client) = &mut client { rt.block_on(async { - Self::execute_query_internal(client, &query, ¶ms_json, force_result).await + Self::execute_query_internal(client, &query, params_json, force_result).await }) } else { - json!({"result": false, "error": "Not connected"}).to_string() + Err("Not connected".to_string()) }; let _ = response.send(result); @@ -116,6 +116,7 @@ impl MSSQLBackend { } pub fn connect(&self, conn_str: String, use_tls: bool, accept_invalid_certs: bool, ca_cert_path: String) -> String { + let (response_tx, response_rx) = mpsc::channel(); let sending = self.tx.send(BackendCommand::Connect { conn_str, @@ -133,9 +134,9 @@ impl MSSQLBackend { response_rx.recv().unwrap_or_else(|e| format!("Response receiver error: {}", e.to_string())) } - pub fn execute_query(&self, query: String, params_json: String, force_result: bool) -> String { + pub fn execute_query(&self, query: String, params_json: Vec, force_result: bool) -> Result>, String> { - let (response_tx, response_rx) = mpsc::channel(); + let (response_tx, response_rx) = mpsc::channel::>, String>>(); let sending = self.tx.send(BackendCommand::Execute { query, @@ -144,30 +145,30 @@ impl MSSQLBackend { response: response_tx, }); - match sending{ - Err(e) => {return format!("Sending error: {}", e.to_string())} + match sending { + Err(e) => { + let error = format!("Sending error: {}", e.to_string()); + return Err(error) + } _ => {} - }; + } - response_rx.recv().unwrap_or_else(|e| format!("Response receiver error: {}", e.to_string())) + response_rx.recv().unwrap_or_else(|e| { + let error = format!("Response receiver error: {}", e.to_string()); + Err(error) + }) } async fn execute_query_internal( client: &mut Client>, query: &str, - params_json: &str, + params_json: Vec, force_result: bool, - ) -> String { - let mut parsed_params: Value = match serde_json::from_str(params_json) { - Ok(params) => params, - Err(e) => return Self::format_json_error(&e.to_string()), - }; + ) -> Result>, String> { - let params_array = match parsed_params.as_array_mut() { - Some(array) => Self::process_mssql_params(array), - None => return Self::format_json_error("Parameters must be a JSON array"), - }; + let mut params = params_json; + let params_array = Self::process_mssql_params(&mut params); let normalized_query = query.trim_start().to_uppercase(); let params_refs: Vec<&dyn ToSql> = params_array.iter().map(|b| b.as_ref()).collect(); @@ -177,11 +178,11 @@ impl MSSQLBackend { Ok(stream) => { let rows = match stream.into_results().await { Ok(rows) => rows.into_iter().flatten().collect(), - Err(e) => return Self::format_json_error(&e.to_string()), + Err(e) => return Err(e.to_string()), }; - Self::rows_to_json_array(rows) + Ok(Some(Self::rows_to_json_array(rows))) }, - Err(e) => Self::format_json_error(&e.to_string()), + Err(e) => Err(e.to_string()), } } else if normalized_query == "BEGIN TRAN" || normalized_query == "COMMIT;" @@ -189,27 +190,20 @@ impl MSSQLBackend { || normalized_query == "BEGIN TRANSACTION"{ match client.simple_query(query).await { - Ok(_) => json!({"result": true}).to_string(), - Err(e) => Self::format_json_error(&e.to_string()), + Ok(_) => Ok(None), + Err(e) => Err(e.to_string()), } } else { match client.execute(query, ¶ms_refs).await { - Ok(_) => json!({"result": true}).to_string(), - Err(e) => Self::format_json_error(&e.to_string()), + Ok(_) => Ok(None), + Err(e) => Err(e.to_string()), } } } - fn format_json_error(error: &str) -> String { - json!({ - "result": false, - "error": error - }).to_string() - } - - fn rows_to_json_array(rows: Vec) -> String { + fn rows_to_json_array(rows: Vec) -> Vec { let mut json_array = Vec::new(); for row in rows { @@ -222,7 +216,7 @@ impl MSSQLBackend { json_array.push(Value::Object(json_obj)); } - json!({ "result": true, "data": json_array }).to_string() + json_array } fn from_sql_to_json(row: &Row, index: usize, column: &Column) -> Value { diff --git a/src/addins/mssql/src/component/dataset.rs b/src/addins/mssql/src/component/dataset.rs new file mode 100644 index 0000000000..abbed3ddc4 --- /dev/null +++ b/src/addins/mssql/src/component/dataset.rs @@ -0,0 +1,84 @@ +use dashmap::DashMap; +use serde_json::Value; +use std::sync::Arc; +use uuid::Uuid; + +pub struct Datasets { + data: Arc>, +} + +#[derive(Debug, Clone)] +pub struct QueryData { + pub results: Vec, + pub params: Vec, + pub text: String, + pub force_result: bool, +} + +impl Datasets { + pub fn new() -> Self { + Self { + data: Arc::new(DashMap::new()), + } + } + + pub fn init_query(&self) -> String { + let key = Uuid::new_v4().to_string(); + let query = QueryData::new(); + self.data.insert(key.clone(), query); + key + } + + pub fn get_query(&self, key: &str) -> Option { + self.data.get(key).map(|guard| guard.value().clone()) + } + + pub fn set_results(&self, key: &str, values: Vec) { + if let Some(mut entry) = self.data.get_mut(key) { + entry.results = values; + } + } + + pub fn add_param(&self, key: &str, value: Value) { + if let Some(mut entry) = self.data.get_mut(key) { + entry.params.push(value); + } + } + + pub fn set_text(&self, key: &str, text: &str) { + if let Some(mut entry) = self.data.get_mut(key) { + entry.text = text.to_string(); + } + } + + pub fn set_force_result(&self, key: &str, enabled: bool) { + if let Some(mut entry) = self.data.get_mut(key) { + entry.force_result = enabled; + } + } + + pub fn len(&self, key: &str) -> Option { + self.data.get(key).map(|entry| entry.results.len()) + } + + pub fn remove(&self, key: &str) { + self.data.remove(key); + } + + pub fn get_row(&self, key: &str, index: usize) -> Option { + let entry = self.data.get(key)?; + let value = entry.results.get(index)?; + serde_json::to_string(value).ok() + } +} + +impl QueryData { + fn new() -> Self { + Self { + results: Vec::new(), + params: Vec::new(), + text: String::new(), + force_result: false, + } + } +} \ No newline at end of file diff --git a/src/addins/mssql/src/component/mod.rs b/src/addins/mssql/src/component/mod.rs index 61d12304bb..96872bbd8f 100644 --- a/src/addins/mssql/src/component/mod.rs +++ b/src/addins/mssql/src/component/mod.rs @@ -1,24 +1,36 @@ mod backend; +mod dataset; use addin1c::{name, Variant}; use crate::core::getset; -use serde_json::json; +use serde_json::{json, Value}; use std::sync::{Arc, Mutex}; +use crate::component::dataset::Datasets; // МЕТОДЫ КОМПОНЕНТЫ pub const METHODS: &[&[u16]] = &[ name!("Connect"), name!("Close"), name!("Execute"), - name!("SetTLS") + name!("SetTLS"), + name!("GetQueryResultRow"), + name!("GetQueryResultLength"), + name!("RemoveQuery"), + name!("InitQuery"), + name!("AddQueryParam"), ]; pub fn get_params_amount(num: usize) -> usize { match num { 0 => 0, 1 => 0, - 2 => 3, + 2 => 1, 3 => 3, + 4 => 2, + 5 => 1, + 6 => 1, + 7 => 2, + 8 => 2, _ => 0, } } @@ -28,16 +40,49 @@ pub fn cal_func(obj: &mut AddIn, num: usize, params: &mut [Variant]) -> Box Box::new(obj.initialize()), 1 => Box::new(obj.close_connection()), 2 => { - let query = params[0].get_string().unwrap_or_default(); - let params_json = params[1].get_string().unwrap_or_default(); - let force_result = params[2].get_bool().unwrap_or(false); - Box::new(obj.execute_query(query, params_json, force_result)) + let key = params[0].get_string().unwrap_or_default(); + Box::new(obj.execute_query(&key)) }, 3 => { let use_tls = params[0].get_bool().unwrap_or(false); let accept_invalid_certs = params[1].get_bool().unwrap_or(false); let ca_cert_path = params[2].get_string().unwrap_or_default(); Box::new(obj.set_tls(use_tls, accept_invalid_certs, &ca_cert_path)) + }, + 4 => { + let key = params[0].get_string().unwrap_or("".to_string()); + let index = params[1].get_i32().unwrap_or(0); + + Box::new(obj.datasets.get_row(&key, index as usize).unwrap_or_else(|| "".to_string())) + }, + 5 => { + let key = params[0].get_string().unwrap_or("".to_string()); + + match obj.datasets.len(&key){ + Some(len) => Box::new(len as i32), + None => Box::new(json!( + {"result": false, "error": format!("Dataset {} not found", key)} + ).to_string()), + } + }, + 6 => { + let key = params[0].get_string().unwrap_or("".to_string()); + obj.datasets.remove(&key); + Box::new(json!({"result": true}).to_string()) + }, + 7 => { + + let text = params[0].get_string().unwrap_or("".to_string()); + let force = params[1].get_bool().unwrap_or(false); + + Box::new(obj.init_query(&text, force)) + }, + 8 => { + let key = params[0].get_string().unwrap_or("".to_string()); + let param = params[1].get_string().unwrap_or("".to_string()); + + Box::new(obj.add_query_param(&key, param)) + } _ => Box::new(false), } @@ -55,6 +100,7 @@ pub struct AddIn { use_tls: bool, accept_invalid_certs: bool, ca_cert_path: String, + datasets: Datasets, } impl AddIn { @@ -66,6 +112,7 @@ impl AddIn { use_tls: false, accept_invalid_certs: false, ca_cert_path: String::new(), + datasets: Datasets::new(), } } @@ -96,14 +143,62 @@ impl AddIn { result } + pub fn init_query(&mut self, text: &str, force_result: bool) -> String { + + let key = self.datasets.init_query(); + self.datasets.set_text(&key, text); + self.datasets.set_force_result(&key, force_result); + + key + } + + pub fn add_query_param(&mut self, key: &str, param: String) -> String { + + let value: Value = match serde_json::from_str(¶m) { + Ok(param) => param, + Err(e) => return Self::error(&e.to_string()), + }; + + self.datasets.add_param(key, value); + json!({"result": true}).to_string() + + } + pub fn close_connection(&mut self) -> String { self.initialized = false; json!({"result": true}).to_string() } - pub fn execute_query(&self, query: String, params_json: String, force_result: bool) -> String { + pub fn execute_query(&self, key: &str) -> String { match self.backend.lock(){ - Ok(backend) => {backend.execute_query(query, params_json, force_result)}, + Ok(backend) => { + + let query = match self.datasets.get_query(key){ + Some(q) => q, + None => return Self::error(format!("No query found by key: {}", key).as_str()), + }; + + let params = query.params; + let text = query.text; + let force_result = query.force_result; + + let backend_result = backend.execute_query(text, params, force_result); + + match backend_result { + Ok(result) => { + match result { + Some(data) => { + + self.datasets.set_results(&key, data); + json!({"result": true, "data": true}).to_string() + + }, + None => json!({"result": true, "data": false}).to_string() + } + }, + Err(e) => Self::error(&e.to_string()), + } + }, Err(e) => Self::error(&e.to_string()), } } @@ -127,7 +222,6 @@ impl AddIn { }).to_string() } - // SERVICE METHODS pub fn get_field_ptr(&self, index: usize) -> *const dyn getset::ValueType { match index { 0 => &self.connection_string as &dyn getset::ValueType as *const _, diff --git a/src/addins/postgres/Cargo.lock b/src/addins/postgres/Cargo.lock index d6d6939cb2..53e6892e00 100644 --- a/src/addins/postgres/Cargo.lock +++ b/src/addins/postgres/Cargo.lock @@ -182,6 +182,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-common" version = "0.1.6" @@ -192,6 +198,20 @@ dependencies = [ "typenum", ] +[[package]] +name = "dashmap" +version = "7.0.0-rc2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a1e35a65fe0538a60167f0ada6e195ad5d477f6ddae273943596d4a1a5730b" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "equivalent", + "hashbrown", + "lock_api", + "parking_lot_core", +] + [[package]] name = "dateparser" version = "0.2.1" @@ -215,6 +235,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.10" @@ -334,6 +360,12 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" + [[package]] name = "hmac" version = "0.12.1" @@ -545,6 +577,7 @@ dependencies = [ "addin1c", "base64", "chrono", + "dashmap", "dateparser", "native-tls", "postgres", @@ -1091,6 +1124,9 @@ name = "uuid" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +dependencies = [ + "getrandom", +] [[package]] name = "vcpkg" diff --git a/src/addins/postgres/Cargo.toml b/src/addins/postgres/Cargo.toml index a891713372..7b913532b6 100644 --- a/src/addins/postgres/Cargo.toml +++ b/src/addins/postgres/Cargo.toml @@ -19,9 +19,10 @@ postgres = { version = "0.19.9", features = ["with-serde_json-1", "with-chrono-0 serde_json = "1.0" base64 = "0.22.1" chrono = "0.4.39" -uuid = "1.12.1" +uuid = { version = "1.12.1", features = ["v4"] } postgres-native-tls = "0.5" native-tls = "0.2.14" dateparser = "0.2.1" +dashmap = "7.0.0-rc2" diff --git a/src/addins/postgres/dependencies.log b/src/addins/postgres/dependencies.log index 70e6ddc350..30aea80872 100644 --- a/src/addins/postgres/dependencies.log +++ b/src/addins/postgres/dependencies.log @@ -1,12 +1,12 @@ "MAIN ---" - linux-vdso.so.1 (0x00007ffeea6d2000) - libssl.so.3 => /lib64/libssl.so.3 (0x00007c2606a00000) - libcrypto.so.3 => /lib64/libcrypto.so.3 (0x00007c2606200000) - libpthread.so.0 => /lib64/libpthread.so.0 (0x00007c2605e00000) - libc.so.6 => /lib64/libc.so.6 (0x00007c2605a00000) - libdl.so.2 => /lib64/libdl.so.2 (0x00007c2605600000) - /lib64/ld-linux-x86-64.so.2 (0x00007c2607000000) - libz.so.1 => /lib64/libz.so.1 (0x00007c2605200000) + linux-vdso.so.1 (0x00007ffd042e7000) + libssl.so.3 => /lib64/libssl.so.3 (0x0000720407a00000) + libcrypto.so.3 => /lib64/libcrypto.so.3 (0x0000720407200000) + libpthread.so.0 => /lib64/libpthread.so.0 (0x0000720406e00000) + libc.so.6 => /lib64/libc.so.6 (0x0000720406a00000) + libdl.so.2 => /lib64/libdl.so.2 (0x0000720406600000) + /lib64/ld-linux-x86-64.so.2 (0x0000720408000000) + libz.so.1 => /lib64/libz.so.1 (0x0000720406200000) GLIBC_2.2.5 GLIBC_2.12 GLIBC_2.3 diff --git a/src/addins/postgres/src/component/dataset.rs b/src/addins/postgres/src/component/dataset.rs new file mode 100644 index 0000000000..abbed3ddc4 --- /dev/null +++ b/src/addins/postgres/src/component/dataset.rs @@ -0,0 +1,84 @@ +use dashmap::DashMap; +use serde_json::Value; +use std::sync::Arc; +use uuid::Uuid; + +pub struct Datasets { + data: Arc>, +} + +#[derive(Debug, Clone)] +pub struct QueryData { + pub results: Vec, + pub params: Vec, + pub text: String, + pub force_result: bool, +} + +impl Datasets { + pub fn new() -> Self { + Self { + data: Arc::new(DashMap::new()), + } + } + + pub fn init_query(&self) -> String { + let key = Uuid::new_v4().to_string(); + let query = QueryData::new(); + self.data.insert(key.clone(), query); + key + } + + pub fn get_query(&self, key: &str) -> Option { + self.data.get(key).map(|guard| guard.value().clone()) + } + + pub fn set_results(&self, key: &str, values: Vec) { + if let Some(mut entry) = self.data.get_mut(key) { + entry.results = values; + } + } + + pub fn add_param(&self, key: &str, value: Value) { + if let Some(mut entry) = self.data.get_mut(key) { + entry.params.push(value); + } + } + + pub fn set_text(&self, key: &str, text: &str) { + if let Some(mut entry) = self.data.get_mut(key) { + entry.text = text.to_string(); + } + } + + pub fn set_force_result(&self, key: &str, enabled: bool) { + if let Some(mut entry) = self.data.get_mut(key) { + entry.force_result = enabled; + } + } + + pub fn len(&self, key: &str) -> Option { + self.data.get(key).map(|entry| entry.results.len()) + } + + pub fn remove(&self, key: &str) { + self.data.remove(key); + } + + pub fn get_row(&self, key: &str, index: usize) -> Option { + let entry = self.data.get(key)?; + let value = entry.results.get(index)?; + serde_json::to_string(value).ok() + } +} + +impl QueryData { + fn new() -> Self { + Self { + results: Vec::new(), + params: Vec::new(), + text: String::new(), + force_result: false, + } + } +} \ No newline at end of file diff --git a/src/addins/postgres/src/component/methods.rs b/src/addins/postgres/src/component/methods.rs index 531ec0fc6c..6c5aae88c2 100644 --- a/src/addins/postgres/src/component/methods.rs +++ b/src/addins/postgres/src/component/methods.rs @@ -8,12 +8,28 @@ use chrono::{NaiveDate, NaiveTime, FixedOffset, NaiveDateTime, DateTime}; use uuid::Uuid; use dateparser::parse; -pub fn execute_query( - add_in: &mut AddIn, - query: String, - params_json: String, - force_result: bool, -) -> Vec { +pub fn init_query(add_in: &mut AddIn, text: &str, force_result: bool) -> String { + + let key = add_in.datasets.init_query(); + add_in.datasets.set_text(&key, text); + add_in.datasets.set_force_result(&key, force_result); + + key +} + +pub fn add_query_param(add_in: &mut AddIn, key: &str, param: String) -> String { + + let value: Value = match serde_json::from_str(¶m) { + Ok(param) => param, + Err(e) => return format_json_error(&e.to_string()), + }; + + add_in.datasets.add_param(key, value); + json!({"result": true}).to_string() + +} + +pub fn execute_query(add_in: &mut AddIn, key: &str) -> String { let client_arc = match add_in.get_connection() { Some(c) => c, @@ -25,12 +41,15 @@ pub fn execute_query( Err(_) => return format_json_error("Cannot acquire client lock"), }; - // Парсинг JSON параметров - let params: Vec = match serde_json::from_str(¶ms_json) { - Ok(params) => params, - Err(e) => return format_json_error(&e.to_string()), + let query = match add_in.datasets.get_query(key){ + Some(q) => q, + None => return format_json_error(format!("No query found by key: {}", key).as_str()), }; + let params = query.params; + let text = query.text; + let force_result = query.force_result; + let params_ref = match process_params(¶ms){ Ok(params) => params, Err(e) => return format_json_error(&e.to_string()), @@ -38,22 +57,25 @@ pub fn execute_query( let params_unboxed: Vec<_> = params_ref.iter().map(AsRef::as_ref).collect(); - if query.trim_start().to_uppercase().starts_with("SELECT") || force_result { - match client.query(&query, ¶ms_unboxed) { + if text.trim_start().to_uppercase().starts_with("SELECT") || force_result { + match client.query(&text, ¶ms_unboxed) { Ok(rows) => { - rows_to_json(rows).into_bytes() + + let processed_rows = rows_to_json(rows); + add_in.datasets.set_results(key, processed_rows); + json!({"result": true, "data": true}).to_string() + } Err(e) => format_json_error(&e.to_string()), } } else { - match client.execute(&query, ¶ms_unboxed.as_slice()) { - Ok(_) => json!({"result": true}).to_string().into_bytes(), + match client.execute(&text, ¶ms_unboxed.as_slice()) { + Ok(_) => json!({"result": true, "data": false}).to_string(), Err(e) => format_json_error(&e.to_string()), } } } -/// Конвертирует JSON-параметры в Postgres-совместимые типы fn process_params(params: &Vec) -> Result>, String> { let mut result = Vec::new(); for param in params { @@ -212,7 +234,7 @@ fn process_object(object: &Map) -> Result, } } -fn rows_to_json(rows: Vec) -> String { +fn rows_to_json(rows: Vec) -> Vec { let mut result = Vec::new(); for row in rows { @@ -230,8 +252,7 @@ fn rows_to_json(rows: Vec) -> String { result.push(Value::Object(row_map)); }; - - json!({ "result": true, "data": result }).to_string() + result } fn process_sql_value(column_name: &str, column_type: &str, row: &postgres::Row) -> Result { @@ -326,12 +347,12 @@ fn process_sql_value(column_name: &str, column_type: &str, row: &postgres::Row) Ok(value) } -fn format_json_error(error: &str) -> Vec { +fn format_json_error(error: &str) -> String { json!({ "result": false, "error": error }) - .to_string().into_bytes() + .to_string() } fn parse_date(input: &str) -> Result { diff --git a/src/addins/postgres/src/component/mod.rs b/src/addins/postgres/src/component/mod.rs index 0a787b63d2..7d0b95ac5e 100644 --- a/src/addins/postgres/src/component/mod.rs +++ b/src/addins/postgres/src/component/mod.rs @@ -1,4 +1,5 @@ mod methods; +mod dataset; use addin1c::{name, Variant}; use crate::core::getset; @@ -9,6 +10,7 @@ use native_tls::{TlsConnector, Certificate}; use std::fs::File; use std::io::Read; use std::sync::{Arc, Mutex}; +use crate::component::dataset::Datasets; // МЕТОДЫ КОМПОНЕНТЫ ------------------------------------------------------------------------------- // Синонимы @@ -16,8 +18,12 @@ pub const METHODS: &[&[u16]] = &[ name!("Connect"), name!("Close"), name!("Execute"), - name!("SetTLS") - + name!("SetTLS"), + name!("GetQueryResultRow"), + name!("GetQueryResultLength"), + name!("RemoveQuery"), + name!("InitQuery"), + name!("AddQueryParam"), ]; // Число параметров функций компоненты @@ -25,8 +31,13 @@ pub fn get_params_amount(num: usize) -> usize { match num { 0 => 0, 1 => 0, - 2 => 3, + 2 => 1, 3 => 3, + 4 => 2, + 5 => 1, + 6 => 1, + 7 => 2, + 8 => 2, _ => 0, } } @@ -41,11 +52,8 @@ pub fn cal_func(obj: &mut AddIn, num: usize, params: &mut [Variant]) -> Box Box::new(obj.close_connection()), 2 => { - let query = String::from_utf8_lossy(params[0].get_blob().unwrap_or(&[])).to_string(); - let params_json = String::from_utf8_lossy(params[1].get_blob().unwrap_or(&[])).to_string(); - let force_result = params[2].get_bool().unwrap_or(false); - - Box::new(methods::execute_query(obj, query, params_json, force_result)) + let key = params[0].get_string().unwrap_or("".to_string()); + Box::new(methods::execute_query(obj, &key)) }, 3 => { @@ -55,6 +63,41 @@ pub fn cal_func(obj: &mut AddIn, num: usize, params: &mut [Variant]) -> Box { + let key = params[0].get_string().unwrap_or("".to_string()); + let index = params[1].get_i32().unwrap_or(0); + + Box::new(obj.datasets.get_row(&key, index as usize).unwrap_or_else(|| "".to_string())) + }, + 5 => { + let key = params[0].get_string().unwrap_or("".to_string()); + + match obj.datasets.len(&key){ + Some(len) => Box::new(len as i32), + None => Box::new(json!( + {"result": false, "error": format!("Dataset {} not found", key)} + ).to_string()), + } + }, + 6 => { + let key = params[0].get_string().unwrap_or("".to_string()); + obj.datasets.remove(&key); + Box::new(json!({"result": true}).to_string()) + }, + 7 => { + + let text = params[0].get_string().unwrap_or("".to_string()); + let force = params[1].get_bool().unwrap_or(false); + + Box::new(methods::init_query(obj, &text, force)) + }, + 8 => { + let key = params[0].get_string().unwrap_or("".to_string()); + let param = params[1].get_string().unwrap_or("".to_string()); + + Box::new(methods::add_query_param(obj, &key, param)) + } _ => Box::new(false), // Неверный номер команды } @@ -77,6 +120,7 @@ pub struct AddIn { use_tls: bool, accept_invalid_certs: bool, ca_cert_path: String, + datasets: Datasets, } impl AddIn { @@ -88,6 +132,7 @@ impl AddIn { use_tls: false, accept_invalid_certs: false, ca_cert_path: String::new(), + datasets: Datasets::new(), } } diff --git a/src/en/OInt/addins/OPI_MSSQL.zip b/src/en/OInt/addins/OPI_MSSQL.zip index 09a8dda6fb..1788afb48c 100644 Binary files a/src/en/OInt/addins/OPI_MSSQL.zip and b/src/en/OInt/addins/OPI_MSSQL.zip differ diff --git a/src/en/OInt/addins/OPI_PostgreSQL.zip b/src/en/OInt/addins/OPI_PostgreSQL.zip index 4779f748e3..d604f28cf4 100644 Binary files a/src/en/OInt/addins/OPI_PostgreSQL.zip and b/src/en/OInt/addins/OPI_PostgreSQL.zip differ diff --git a/src/en/OPI/src/CommonTemplates/OPI_MSSQL/Template.addin b/src/en/OPI/src/CommonTemplates/OPI_MSSQL/Template.addin index 09a8dda6fb..1788afb48c 100644 Binary files a/src/en/OPI/src/CommonTemplates/OPI_MSSQL/Template.addin and b/src/en/OPI/src/CommonTemplates/OPI_MSSQL/Template.addin differ diff --git a/src/en/OPI/src/CommonTemplates/OPI_PostgreSQL/Template.addin b/src/en/OPI/src/CommonTemplates/OPI_PostgreSQL/Template.addin index 4779f748e3..d604f28cf4 100644 Binary files a/src/en/OPI/src/CommonTemplates/OPI_PostgreSQL/Template.addin and b/src/en/OPI/src/CommonTemplates/OPI_PostgreSQL/Template.addin differ diff --git a/src/ru/OInt/addins/OPI_MSSQL.zip b/src/ru/OInt/addins/OPI_MSSQL.zip index 09a8dda6fb..1788afb48c 100644 Binary files a/src/ru/OInt/addins/OPI_MSSQL.zip and b/src/ru/OInt/addins/OPI_MSSQL.zip differ diff --git a/src/ru/OInt/addins/OPI_PostgreSQL.zip b/src/ru/OInt/addins/OPI_PostgreSQL.zip index 4779f748e3..d604f28cf4 100644 Binary files a/src/ru/OInt/addins/OPI_PostgreSQL.zip and b/src/ru/OInt/addins/OPI_PostgreSQL.zip differ diff --git a/src/ru/OPI/src/CommonModules/OPI_MSSQL/Module.bsl b/src/ru/OPI/src/CommonModules/OPI_MSSQL/Module.bsl index 7071bc206b..5a4c5208df 100644 --- a/src/ru/OPI/src/CommonModules/OPI_MSSQL/Module.bsl +++ b/src/ru/OPI/src/CommonModules/OPI_MSSQL/Module.bsl @@ -145,11 +145,6 @@ , Знач Соединение = "" , Знач Tls = "") Экспорт - OPI_ПреобразованиеТипов.ПолучитьСтроку(ТекстЗапроса, Истина); - OPI_ПреобразованиеТипов.ПолучитьБулево(ФорсироватьРезультат); - - Параметры_ = ОбработатьПараметры(Параметры); - Если ЭтоКоннектор(Соединение) Тогда ЗакрыватьСоединение = Ложь; Коннектор = Соединение; @@ -161,15 +156,17 @@ Если Не ЭтоКоннектор(Коннектор) Тогда Возврат Коннектор; КонецЕсли; + + OPI_ПреобразованиеТипов.ПолучитьСтроку(ТекстЗапроса, Истина); + OPI_ПреобразованиеТипов.ПолучитьБулево(ФорсироватьРезультат); - Результат = Коннектор.Execute(ТекстЗапроса, Параметры_, ФорсироватьРезультат); + Параметры_ = ОбработатьПараметры(Параметры); + Результат = OPI_ЗапросыSQL.ВыполнитьЗапросСОбработкой(Коннектор, ТекстЗапроса, ФорсироватьРезультат, Параметры_); Если ЗакрыватьСоединение Тогда ЗакрытьСоединение(Коннектор); КонецЕсли; - Результат = OPI_Инструменты.JsonВСтруктуру(Результат); - Возврат Результат; КонецФункции @@ -581,27 +578,28 @@ Функция ОбработатьПараметры(Знач Параметры) Если Не ЗначениеЗаполнено(Параметры) Тогда - Возврат "[]"; + Возврат Новый Массив; КонецЕсли; + Параметры_ = Новый Массив; OPI_ПреобразованиеТипов.ПолучитьМассив(Параметры); - Для Н = 0 По Параметры.ВГраница() Цикл + Счетчик = 0; + Для Каждого Параметр Из Параметры Цикл - ТекущийПараметр = Параметры[Н]; - - ТекущийПараметр = ОбработатьПараметр(ТекущийПараметр); - - Параметры[Н] = ТекущийПараметр; + ТекущийПараметр = ОбработатьПараметр(Параметр); + ТекущийПараметр = OPI_Инструменты.JSONСтрокой(ТекущийПараметр, , Ложь); + Если СтрНачинаетсяС(ТекущийПараметр, "НЕ JSON") Тогда + ВызватьИсключение СтрШаблон("Ошибка валидации JSON параметра. Индекс массив %1", Счетчик); + Иначе + Параметры_.Добавить(ТекущийПараметр); + КонецЕсли; + + Счетчик = Счетчик + 1; + КонецЦикла; - Параметры_ = OPI_Инструменты.JSONСтрокой(Параметры, , Ложь); - - Если СтрНачинаетсяС(Параметры_, "НЕ JSON") Тогда - ВызватьИсключение "Ошибка валидации JSON массива параметров!"; - КонецЕсли; - Возврат Параметры_; КонецФункции diff --git a/src/ru/OPI/src/CommonModules/OPI_PostgreSQL/Module.bsl b/src/ru/OPI/src/CommonModules/OPI_PostgreSQL/Module.bsl index 1cad675688..4904c196bc 100644 --- a/src/ru/OPI/src/CommonModules/OPI_PostgreSQL/Module.bsl +++ b/src/ru/OPI/src/CommonModules/OPI_PostgreSQL/Module.bsl @@ -74,7 +74,7 @@ КонецЕсли; Коннектор.ConnectionString = СтрокаПодключения; - + Результат = Коннектор.Connect(); Результат = OPI_Инструменты.JsonВСтруктуру(Результат, Ложь); @@ -145,14 +145,6 @@ , Знач Соединение = "" , Знач Tls = "") Экспорт - OPI_ПреобразованиеТипов.ПолучитьСтроку(ТекстЗапроса, Истина); - OPI_ПреобразованиеТипов.ПолучитьБулево(ФорсироватьРезультат); - - Параметры_ = ОбработатьПараметры(Параметры); - - ТекстЗапроса = ПолучитьДвоичныеДанныеИзСтроки(ТекстЗапроса); - Параметры_ = ПолучитьДвоичныеДанныеИзСтроки(Параметры_); - Если ЭтоКоннектор(Соединение) Тогда ЗакрыватьСоединение = Ложь; Коннектор = Соединение; @@ -164,16 +156,17 @@ Если Не ЭтоКоннектор(Коннектор) Тогда Возврат Коннектор; КонецЕсли; + + OPI_ПреобразованиеТипов.ПолучитьСтроку(ТекстЗапроса, Истина); + OPI_ПреобразованиеТипов.ПолучитьБулево(ФорсироватьРезультат); - Результат = Коннектор.Execute(ТекстЗапроса, Параметры_, ФорсироватьРезультат); - Результат = ПолучитьСтрокуИзДвоичныхДанных(Результат); + Параметры_ = ОбработатьПараметры(Параметры); + Результат = OPI_ЗапросыSQL.ВыполнитьЗапросСОбработкой(Коннектор, ТекстЗапроса, ФорсироватьРезультат, Параметры_); Если ЗакрыватьСоединение Тогда ЗакрытьСоединение(Коннектор); КонецЕсли; - - Результат = OPI_Инструменты.JsonВСтруктуру(Результат); - + Возврат Результат; КонецФункции @@ -575,32 +568,33 @@ Функция ОбработатьПараметры(Знач Параметры) Если Не ЗначениеЗаполнено(Параметры) Тогда - Возврат "[]"; + Возврат Новый Массив; КонецЕсли; + Параметры_ = Новый Массив; OPI_ПреобразованиеТипов.ПолучитьМассив(Параметры); - Для Н = 0 По Параметры.ВГраница() Цикл + Счетчик = 0; + Для Каждого Параметр Из Параметры Цикл - ТекущийПараметр = Параметры[Н]; - - ТекущийПараметр = ОбработатьПараметр(ТекущийПараметр); - - Параметры[Н] = ТекущийПараметр; + ТекущийПараметр = ОбработатьПараметр(Параметр); + ТекущийПараметр = OPI_Инструменты.JSONСтрокой(ТекущийПараметр, , Ложь); + Если СтрНачинаетсяС(ТекущийПараметр, "НЕ JSON") Тогда + ВызватьИсключение СтрШаблон("Ошибка валидации JSON параметра. Индекс массив %1", Счетчик); + Иначе + Параметры_.Добавить(ТекущийПараметр); + КонецЕсли; + + Счетчик = Счетчик + 1; + КонецЦикла; - Параметры_ = OPI_Инструменты.JSONСтрокой(Параметры, , Ложь); - - Если СтрНачинаетсяС(Параметры_, "НЕ JSON") Тогда - ВызватьИсключение "Ошибка валидации JSON массива параметров!"; - КонецЕсли; - Возврат Параметры_; КонецФункции -Функция ОбработатьПараметр(ТекущийПараметр) +Функция ОбработатьПараметр(ТекущийПараметр, Вложенный = Ложь) ТекущийТип = ТипЗнч(ТекущийПараметр); @@ -611,10 +605,12 @@ ИначеЕсли ТекущийТип = Тип("УникальныйИдентификатор") Тогда ТекущийПараметр = Строка(ТекущийПараметр); + ТекущийПараметр = ?(Вложенный, ТекущийПараметр, Новый Структура("UUID", ТекущийПараметр)); ИначеЕсли ТекущийТип = Тип("Дата") Тогда ТекущийПараметр = OPI_Инструменты.ДатаRFC3339(ТекущийПараметр); + ТекущийПараметр = ?(Вложенный, ТекущийПараметр, Новый Структура("TIMESTAMP", ТекущийПараметр)); ИначеЕсли OPI_Инструменты.ПолеКоллекцииСуществует(ТекущийПараметр, "BYTEA") Тогда @@ -634,14 +630,17 @@ Продолжить; КонецЕсли; - ТекущийПараметр[ЭлементПараметра.Ключ] = ОбработатьПараметр(ТекущееЗначение); + ТекущийПараметр[ЭлементПараметра.Ключ] = ОбработатьПараметр(ТекущееЗначение, Истина); КонецЦикла; Иначе Если Не OPI_Инструменты.ЭтоПримитивныйТип(ТекущийПараметр) Тогда + OPI_ПреобразованиеТипов.ПолучитьСтроку(ТекущийПараметр); + ТекущийПараметр = ?(Вложенный, ТекущийПараметр, Новый Структура("VARCHAR", ТекущийПараметр)); + КонецЕсли; КонецЕсли; diff --git a/src/ru/OPI/src/CommonModules/OPI_ЗапросыSQL/Module.bsl b/src/ru/OPI/src/CommonModules/OPI_ЗапросыSQL/Module.bsl index fd7ac6b882..c42b22290a 100644 --- a/src/ru/OPI/src/CommonModules/OPI_ЗапросыSQL/Module.bsl +++ b/src/ru/OPI/src/CommonModules/OPI_ЗапросыSQL/Module.bsl @@ -353,6 +353,28 @@ КонецФункции +Функция ВыполнитьЗапросСОбработкой(Коннектор, Знач ТекстЗапроса, Знач ФорсироватьРезультат, Знач Параметры) Экспорт + + КлючЗапроса = Коннектор.InitQuery(ТекстЗапроса, ФорсироватьРезультат); + + Для Каждого Параметр Из Параметры Цикл + + Добавление = Коннектор.AddQueryParam(КлючЗапроса, Параметр); + Добавление = OPI_Инструменты.JsonВСтруктуру(Добавление); + + Если Не Добавление["result"] Тогда + Возврат Добавление; + КонецЕсли; + + КонецЦикла; + + Результат = Коннектор.Execute(КлючЗапроса); + Результат = ОбработатьРезультатЗапроса(Коннектор, КлючЗапроса, Результат); + + Возврат Результат; + +КонецФункции + #КонецОбласти #Область СлужебныеПроцедурыИФункции @@ -1211,6 +1233,45 @@ КонецФункции +Функция ОбработатьРезультатЗапроса(Знач Коннектор, Знач КлючЗапроса, Знач Результат) + + Результат = OPI_Инструменты.JsonВСтруктуру(Результат); + + Если Результат["result"] Тогда + + Если Не Результат["data"] Тогда + Результат.Удалить("data"); + Возврат Результат; + КонецЕсли; + + КоличествоСтрок = Коннектор.GetQueryResultLength(КлючЗапроса); + + Если Не ТипЗнч(КоличествоСтрок) = Тип("Число") Тогда + Возврат OPI_Инструменты.JsonВСтруктуру(КоличествоСтрок); + КонецЕсли; + + МассивСтрок = Новый Массив; + + Для Н = 0 По КоличествоСтрок Цикл + + ТекущаяСтрока = Коннектор.GetQueryResultRow(КлючЗапроса, Н); + МассивСтрок.Добавить(ТекущаяСтрока); + + КонецЦикла; + + Если МассивСтрок.Количество() > 0 Тогда + ТекстJSON = СтрШаблон("[%1]", СтрСоединить(МассивСтрок, "," + Символы.ПС)); + МассивСтрок = OPI_Инструменты.JSONВСтруктуру(ТекстJSON); + КонецЕсли; + + Результат.Вставить("data", МассивСтрок); + + КонецЕсли; + + Возврат Результат; + +КонецФункции + Процедура РазделитьКоллекциюДанных(Знач Запись, МассивПолей, МассивЗначений) ТекстОшибки = "Некорректный набор данных для обновления"; diff --git a/src/ru/OPI/src/CommonTemplates/OPI_MSSQL/Template.addin b/src/ru/OPI/src/CommonTemplates/OPI_MSSQL/Template.addin index 09a8dda6fb..1788afb48c 100644 Binary files a/src/ru/OPI/src/CommonTemplates/OPI_MSSQL/Template.addin and b/src/ru/OPI/src/CommonTemplates/OPI_MSSQL/Template.addin differ diff --git a/src/ru/OPI/src/CommonTemplates/OPI_PostgreSQL/Template.addin b/src/ru/OPI/src/CommonTemplates/OPI_PostgreSQL/Template.addin index 4779f748e3..d604f28cf4 100644 Binary files a/src/ru/OPI/src/CommonTemplates/OPI_PostgreSQL/Template.addin and b/src/ru/OPI/src/CommonTemplates/OPI_PostgreSQL/Template.addin differ