1
0
mirror of https://github.com/Bayselonarrend/OpenIntegrations.git synced 2025-08-10 22:41:43 +02:00

Переработка MSSQL и PG для #72

This commit is contained in:
Anton Titovets
2025-08-08 16:26:48 +03:00
parent c06dc67958
commit 36c4c633f4
23 changed files with 585 additions and 145 deletions

View File

@@ -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",
]

View File

@@ -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"
dashmap = "7.0.0-rc2"

View File

@@ -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

View File

@@ -29,9 +29,9 @@ enum BackendCommand {
},
Execute {
query: String,
params_json: String,
params_json: Vec<Value>,
force_result: bool,
response: Sender<String>,
response: Sender<Result<Option<Vec<Value>>, 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, &params_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<Value>, force_result: bool) -> Result<Option<Vec<Value>>, String> {
let (response_tx, response_rx) = mpsc::channel();
let (response_tx, response_rx) = mpsc::channel::<Result<Option<Vec<Value>>, 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<Compat<TcpStream>>,
query: &str,
params_json: &str,
params_json: Vec<Value>,
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<Option<Vec<Value>>, 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, &params_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<Row>) -> String {
fn rows_to_json_array(rows: Vec<Row>) -> Vec<Value> {
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 {

View File

@@ -0,0 +1,84 @@
use dashmap::DashMap;
use serde_json::Value;
use std::sync::Arc;
use uuid::Uuid;
pub struct Datasets {
data: Arc<DashMap<String, QueryData>>,
}
#[derive(Debug, Clone)]
pub struct QueryData {
pub results: Vec<Value>,
pub params: Vec<Value>,
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<QueryData> {
self.data.get(key).map(|guard| guard.value().clone())
}
pub fn set_results(&self, key: &str, values: Vec<Value>) {
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<usize> {
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<String> {
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,
}
}
}

View File

@@ -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<dyn
0 => 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(&param) {
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 _,

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -0,0 +1,84 @@
use dashmap::DashMap;
use serde_json::Value;
use std::sync::Arc;
use uuid::Uuid;
pub struct Datasets {
data: Arc<DashMap<String, QueryData>>,
}
#[derive(Debug, Clone)]
pub struct QueryData {
pub results: Vec<Value>,
pub params: Vec<Value>,
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<QueryData> {
self.data.get(key).map(|guard| guard.value().clone())
}
pub fn set_results(&self, key: &str, values: Vec<Value>) {
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<usize> {
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<String> {
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,
}
}
}

View File

@@ -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<u8> {
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(&param) {
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<Value> = match serde_json::from_str(&params_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(&params){
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, &params_unboxed) {
if text.trim_start().to_uppercase().starts_with("SELECT") || force_result {
match client.query(&text, &params_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, &params_unboxed.as_slice()) {
Ok(_) => json!({"result": true}).to_string().into_bytes(),
match client.execute(&text, &params_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<Value>) -> Result<Vec<Box<dyn ToSql + Sync>>, String> {
let mut result = Vec::new();
for param in params {
@@ -212,7 +234,7 @@ fn process_object(object: &Map<String, Value>) -> Result<Box<dyn ToSql + Sync>,
}
}
fn rows_to_json(rows: Vec<postgres::Row>) -> String {
fn rows_to_json(rows: Vec<postgres::Row>) -> Vec<Value> {
let mut result = Vec::new();
for row in rows {
@@ -230,8 +252,7 @@ fn rows_to_json(rows: Vec<postgres::Row>) -> 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<Value, postgres::Error> {
@@ -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<u8> {
fn format_json_error(error: &str) -> String {
json!({
"result": false,
"error": error
})
.to_string().into_bytes()
.to_string()
}
fn parse_date(input: &str) -> Result<NaiveDateTime, String> {

View File

@@ -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<dyn
1 => 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<dyn
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(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(),
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -145,11 +145,6 @@
, Знач Соединение = ""
, Знач Tls = "") Экспорт
OPI_ПреобразованиеТипов.ПолучитьСтроку(ТекстЗапроса, Истина);
OPI_ПреобразованиеТипов.ПолучитьБулево(ФорсироватьРезультат);
Параметры_ = ОбработатьПараметры(Параметры);
Если ЭтоКоннектор(Соединение) Тогда
ЗакрыватьСоединение = Ложь;
Коннектор = Соединение;
@@ -162,14 +157,16 @@
Возврат Коннектор;
КонецЕсли;
Результат = Коннектор.Execute(ТекстЗапроса, Параметры_, ФорсироватьРезультат);
OPI_ПреобразованиеТипов.ПолучитьСтроку(ТекстЗапроса, Истина);
OPI_ПреобразованиеТипов.ПолучитьБулево(ФорсироватьРезультат);
Параметры_ = ОбработатьПараметры(Параметры);
Результат = OPI_ЗапросыSQL.ВыполнитьЗапросСОбработкой(Коннектор, ТекстЗапроса, ФорсироватьРезультат, Параметры_);
Если ЗакрыватьСоединение Тогда
ЗакрытьСоединение(Коннектор);
КонецЕсли;
Результат = OPI_Инструменты.JsonВСтруктуру(Результат);
Возврат Результат;
КонецФункции
@@ -581,27 +578,28 @@
Функция ОбработатьПараметры(Знач Параметры)
Если Не ЗначениеЗаполнено(Параметры) Тогда
Возврат "[]";
Возврат Новый Массив;
КонецЕсли;
Параметры_ = Новый Массив;
OPI_ПреобразованиеТипов.ПолучитьМассив(Параметры);
Для Н = 0 По Параметры.ВГраница() Цикл
Счетчик = 0;
Для Каждого Параметр Из Параметры Цикл
ТекущийПараметр = Параметры[Н];
ТекущийПараметр = ОбработатьПараметр(Параметр);
ТекущийПараметр = OPI_Инструменты.JSONСтрокой(ТекущийПараметр, , Ложь);
ТекущийПараметр = ОбработатьПараметр(ТекущийПараметр);
Если СтрНачинаетсяС(ТекущийПараметр, "НЕ JSON") Тогда
ВызватьИсключение СтрШаблон("Ошибка валидации JSON параметра. Индекс массив %1", Счетчик);
Иначе
Параметры_.Добавить(ТекущийПараметр);
КонецЕсли;
Параметры[Н] = ТекущийПараметр;
Счетчик = Счетчик + 1;
КонецЦикла;
Параметры_ = OPI_Инструменты.JSONСтрокой(Параметры, , Ложь);
Если СтрНачинаетсяС(Параметры_, "НЕ JSON") Тогда
ВызватьИсключение "Ошибка валидации JSON массива параметров!";
КонецЕсли;
Возврат Параметры_;
КонецФункции

View File

@@ -145,14 +145,6 @@
, Знач Соединение = ""
, Знач Tls = "") Экспорт
OPI_ПреобразованиеТипов.ПолучитьСтроку(ТекстЗапроса, Истина);
OPI_ПреобразованиеТипов.ПолучитьБулево(ФорсироватьРезультат);
Параметры_ = ОбработатьПараметры(Параметры);
ТекстЗапроса = ПолучитьДвоичныеДанныеИзСтроки(ТекстЗапроса);
Параметры_ = ПолучитьДвоичныеДанныеИзСтроки(Параметры_);
Если ЭтоКоннектор(Соединение) Тогда
ЗакрыватьСоединение = Ложь;
Коннектор = Соединение;
@@ -165,15 +157,16 @@
Возврат Коннектор;
КонецЕсли;
Результат = Коннектор.Execute(ТекстЗапроса, Параметры_, ФорсироватьРезультат);
Результат = ПолучитьСтрокуИзДвоичныхДанных(Результат);
OPI_ПреобразованиеТипов.ПолучитьСтроку(ТекстЗапроса, Истина);
OPI_ПреобразованиеТипов.ПолучитьБулево(ФорсироватьРезультат);
Параметры_ = ОбработатьПараметры(Параметры);
Результат = 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", ТекущийПараметр));
КонецЕсли;
КонецЕсли;

View File

@@ -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", МассивСтрок);
КонецЕсли;
Возврат Результат;
КонецФункции
Процедура РазделитьКоллекциюДанных(Знач Запись, МассивПолей, МассивЗначений)
ТекстОшибки = "Некорректный набор данных для обновления";