diff --git a/examples/createdb.rs b/examples/createdb.rs index eb48eb7..6e6c7f2 100644 --- a/examples/createdb.rs +++ b/examples/createdb.rs @@ -18,6 +18,7 @@ fn main() -> Result<(), FbError> { .user("SYSDBA") .pass("masterkey") .page_size(8 * 1024) // Optional + .dialect(rsfbclient::Dialect::D1) .create_database()?; #[cfg(feature = "dynamic_loading")] @@ -38,6 +39,7 @@ fn main() -> Result<(), FbError> { .user("SYSDBA") .pass("masterkey") .page_size(16 * 1024) // Optional + .dialect(rsfbclient::Dialect::D3) .create_database()?; conn.close()?; diff --git a/rsfbclient-core/src/connection.rs b/rsfbclient-core/src/connection.rs index f147c29..de69814 100644 --- a/rsfbclient-core/src/connection.rs +++ b/rsfbclient-core/src/connection.rs @@ -36,6 +36,8 @@ pub trait FirebirdClientDbOps: Send { fn attach_database( &mut self, config: &Self::AttachmentConfig, + dialect: Dialect, + no_db_triggers: bool, ) -> Result; /// Disconnect from the database @@ -50,6 +52,7 @@ pub trait FirebirdClientDbOps: Send { &mut self, config: &Self::AttachmentConfig, page_size: Option, + dialect: Dialect, ) -> Result; } @@ -145,12 +148,13 @@ pub trait FirebirdClientDbEvents: FirebirdClientDbOps { ) -> Result<(), FbError>; } -#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)] #[repr(u8)] /// Firebird sql dialect pub enum Dialect { D1 = 1, D2 = 2, + #[default] D3 = 3, } diff --git a/rsfbclient-native/src/connection.rs b/rsfbclient-native/src/connection.rs index 04873f8..82cba90 100644 --- a/rsfbclient-native/src/connection.rs +++ b/rsfbclient-native/src/connection.rs @@ -113,10 +113,17 @@ impl FirebirdClientDbOps for NativeFbClient { fn attach_database( &mut self, config: &Self::AttachmentConfig, + dialect: Dialect, + no_db_triggers: bool, ) -> Result { - let (dpb, conn_string) = self.build_dpb(config); + let (mut dpb, conn_string) = self.build_dpb(config, dialect); let mut handle = 0; + if no_db_triggers { + dpb.extend(&[ibase::isc_dpb_no_db_triggers as u8, 1 as u8]); + dpb.extend(&[1 as u8]); + } + unsafe { if self.ibase.isc_attach_database()( &mut self.status[0], @@ -162,8 +169,9 @@ impl FirebirdClientDbOps for NativeFbClient { &mut self, config: &Self::AttachmentConfig, page_size: Option, + dialect: Dialect, ) -> Result { - let (mut dpb, conn_string) = self.build_dpb(config); + let (mut dpb, conn_string) = self.build_dpb(config, dialect); let mut handle = 0; if let Some(ps) = page_size { @@ -668,7 +676,11 @@ impl NativeFbClient { /// Build the dpb and the connection string /// /// Used by attach database operations - fn build_dpb(&mut self, config: &NativeFbAttachmentConfig) -> (Vec, String) { + fn build_dpb( + &mut self, + config: &NativeFbAttachmentConfig, + dialect: Dialect, + ) -> (Vec, String) { let user = &config.user; let mut password = None; let db_name = &config.db_name; @@ -709,6 +721,9 @@ impl NativeFbClient { dpb.extend(role.bytes()); } + dpb.extend(&[ibase::isc_dpb_sql_dialect as u8, 1 as u8]); + dpb.extend(&[dialect as u8]); + dpb }; diff --git a/rsfbclient-rust/src/client.rs b/rsfbclient-rust/src/client.rs index ac8d8c2..02f2999 100644 --- a/rsfbclient-rust/src/client.rs +++ b/rsfbclient-rust/src/client.rs @@ -85,6 +85,8 @@ impl FirebirdClientDbOps for RustFbClient { fn attach_database( &mut self, config: &Self::AttachmentConfig, + dialect: Dialect, + no_db_triggers: bool, ) -> Result { let host = config.host.as_str(); let port = config.port; @@ -109,7 +111,8 @@ impl FirebirdClientDbOps for RustFbClient { )?, }; - let attach_result = conn.attach_database(db_name, user, pass, role); + let attach_result = + conn.attach_database(db_name, user, pass, role, dialect, no_db_triggers); // Put the connection back self.conn.replace(conn); @@ -135,6 +138,7 @@ impl FirebirdClientDbOps for RustFbClient { &mut self, config: &Self::AttachmentConfig, page_size: Option, + dialect: Dialect, ) -> Result { let host = config.host.as_str(); let port = config.port; @@ -159,7 +163,7 @@ impl FirebirdClientDbOps for RustFbClient { )?, }; - let attach_result = conn.create_database(db_name, user, pass, page_size, role); + let attach_result = conn.create_database(db_name, user, pass, page_size, role, dialect); // Put the connection back self.conn.replace(conn); @@ -391,6 +395,7 @@ impl FirebirdWireConnection { pass: &str, page_size: Option, role_name: Option<&str>, + dialect: Dialect, ) -> Result { self.socket.write_all(&create( db_name, @@ -400,6 +405,7 @@ impl FirebirdWireConnection { self.charset.clone(), page_size, role_name.clone(), + dialect, ))?; self.socket.flush()?; @@ -415,6 +421,8 @@ impl FirebirdWireConnection { user: &str, pass: &str, role_name: Option<&str>, + dialect: Dialect, + no_db_triggers: bool, ) -> Result { self.socket.write_all(&attach( db_name, @@ -423,6 +431,8 @@ impl FirebirdWireConnection { self.version, self.charset.clone(), role_name.clone(), + dialect, + no_db_triggers, ))?; self.socket.flush()?; @@ -1042,7 +1052,9 @@ fn connection_test() { let mut conn = FirebirdWireConnection::connect("127.0.0.1", 3050, db_name, user, pass, UTF_8).unwrap(); - let mut db_handle = conn.attach_database(db_name, user, pass, None).unwrap(); + let mut db_handle = conn + .attach_database(db_name, user, pass, None, Dialect::D3, false) + .unwrap(); let mut tr_handle = conn .begin_transaction(&mut db_handle, TransactionConfiguration::default()) diff --git a/rsfbclient-rust/src/wire.rs b/rsfbclient-rust/src/wire.rs index 5ad3033..6075d7c 100644 --- a/rsfbclient-rust/src/wire.rs +++ b/rsfbclient-rust/src/wire.rs @@ -12,7 +12,7 @@ use crate::{ util::*, xsqlda::{XSqlVar, XSQLDA_DESCRIBE_VARS}, }; -use rsfbclient_core::{ibase, Charset, Column, FbError, FreeStmtOp, SqlType, TrOp}; +use rsfbclient_core::{ibase, Charset, Column, Dialect, FbError, FreeStmtOp, SqlType, TrOp}; /// Buffer length to use in the connection pub const BUFFER_LENGTH: u32 = 1024; @@ -139,8 +139,19 @@ pub fn attach( protocol: ProtocolVersion, charset: Charset, role_name: Option<&str>, + dialect: Dialect, + no_db_triggers: bool, ) -> Bytes { - let dpb = build_dpb(user, pass, protocol, charset, None, role_name); + let dpb = build_dpb( + user, + pass, + protocol, + charset, + None, + role_name, + dialect, + no_db_triggers, + ); let mut attach = BytesMut::with_capacity(16 + db_name.len() + dpb.len()); @@ -163,8 +174,11 @@ pub fn create( charset: Charset, page_size: Option, role_name: Option<&str>, + dialect: Dialect, ) -> Bytes { - let dpb = build_dpb(user, pass, protocol, charset, page_size, role_name); + let dpb = build_dpb( + user, pass, protocol, charset, page_size, role_name, dialect, false, + ); let mut create = BytesMut::with_capacity(16 + db_name.len() + dpb.len()); @@ -186,6 +200,8 @@ fn build_dpb( charset: Charset, page_size: Option, role_name: Option<&str>, + dialect: Dialect, + no_db_triggers: bool, ) -> Bytes { let mut dpb = BytesMut::with_capacity(64); @@ -209,6 +225,14 @@ fn build_dpb( dpb.extend(role.bytes()); } + dpb.extend(&[ibase::isc_dpb_sql_dialect as u8, 1 as u8]); + dpb.extend(&[dialect as u8]); + + if no_db_triggers { + dpb.extend(&[ibase::isc_dpb_no_db_triggers as u8, 1 as u8]); + dpb.extend(&[1 as u8]); + } + match protocol { // Plaintext password ProtocolVersion::V10 => { diff --git a/src/connection/builders/builder_native.rs b/src/connection/builders/builder_native.rs index 6ea6ece..da7cf76 100644 --- a/src/connection/builders/builder_native.rs +++ b/src/connection/builders/builder_native.rs @@ -214,6 +214,12 @@ where self.conn_conf.transaction_conf = builder(&mut transaction_builder()).build(); self } + + /// Disabled the database triggers + pub fn no_db_triggers(&mut self) -> &mut Self { + self.conn_conf.no_db_triggers = true; + self + } } impl NativeConnectionBuilder { diff --git a/src/connection/builders/builder_pure_rust.rs b/src/connection/builders/builder_pure_rust.rs index 636a6da..3ba2244 100644 --- a/src/connection/builders/builder_pure_rust.rs +++ b/src/connection/builders/builder_pure_rust.rs @@ -106,6 +106,12 @@ impl PureRustConnectionBuilder { self } + /// Disabled the database triggers + pub fn no_db_triggers(&mut self) -> &mut Self { + self.0.no_db_triggers = true; + self + } + /// Default transaction configuration pub fn transaction(&mut self, conf: TransactionConfiguration) -> &mut Self { self.0.transaction_conf = conf; diff --git a/src/connection/mod.rs b/src/connection/mod.rs index fa6c605..17842de 100644 --- a/src/connection/mod.rs +++ b/src/connection/mod.rs @@ -60,6 +60,7 @@ pub trait FirebirdClientFactory { pub struct ConnectionConfiguration { attachment_conf: A, dialect: Dialect, + no_db_triggers: bool, stmt_cache_size: usize, transaction_conf: TransactionConfiguration, } @@ -71,6 +72,7 @@ impl Default for ConnectionConfiguration { dialect: Dialect::D3, stmt_cache_size: 20, transaction_conf: TransactionConfiguration::default(), + no_db_triggers: false, } } } @@ -107,7 +109,8 @@ impl Connection { mut cli: C, conf: &ConnectionConfiguration, ) -> Result, FbError> { - let handle = cli.attach_database(&conf.attachment_conf)?; + let handle = + cli.attach_database(&conf.attachment_conf, conf.dialect, conf.no_db_triggers)?; let stmt_cache = StmtCache::new(conf.stmt_cache_size); Ok(Connection { @@ -127,7 +130,7 @@ impl Connection { conf: &ConnectionConfiguration, page_size: Option, ) -> Result, FbError> { - let handle = cli.create_database(&conf.attachment_conf, page_size)?; + let handle = cli.create_database(&conf.attachment_conf, page_size, conf.dialect)?; let stmt_cache = StmtCache::new(conf.stmt_cache_size); Ok(Connection { diff --git a/src/tests/connection.rs b/src/tests/connection.rs index 2da4d62..524592c 100644 --- a/src/tests/connection.rs +++ b/src/tests/connection.rs @@ -309,4 +309,44 @@ mk_tests_default! { Ok(()) } + + #[test] + #[cfg(all(not(feature = "embedded_tests")))] + fn no_db_triggers() -> Result<(), FbError> { + + let epoch = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_nanos(); + + let mut conn = cbuilder() + .connect()?; + + conn.execute(&format!("create table log_conn{} (id int);", epoch), ())?; + + conn.execute(&format!("create trigger trig_conexao{0} on connect as begin insert into log_conn{0} (id) values (5); end", epoch), ())?; + + conn.close()?; + + let mut conn2 = cbuilder() + .no_db_triggers() + .connect()?; + + let resp: Option<(i32,)> = conn2.query_first(&format!("select * from log_conn{}", epoch), ())?; + assert_eq!(None, resp); + + conn2.close()?; + + let mut conn3 = cbuilder() + .connect()?; + + let resp: Option<(i32,)> = conn3.query_first(&format!("select * from log_conn{}", epoch), ())?; + assert_eq!(Some((5,)), resp); + + conn3.execute(&format!("drop trigger trig_conexao{};", epoch), ()).ok(); + conn3.execute(&format!("drop table log_conn{};", epoch), ()).ok(); + conn3.close()?; + + Ok(()) + } } diff --git a/src/tests/database.rs b/src/tests/database.rs index 99b99b8..cfd48e2 100644 --- a/src/tests/database.rs +++ b/src/tests/database.rs @@ -139,4 +139,38 @@ mk_tests_default! { Ok(()) } + + #[test] + #[cfg(all(feature = "linking", not(feature = "embedded_tests"), not(feature = "pure_rust")))] + fn dyn_linking_create_with_invalid_dialect() -> Result<(), FbError> { + + let rs = builder_native() + .with_dyn_link() + .with_remote() + .db_name("test_create_db5.fdb") + .user("SYSDBA") + .host("localhost") + .dialect(rsfbclient::Dialect::D2) + .create_database(); + + assert!(rs.is_err()); + assert!(rs.err().unwrap().to_string().contains("Database dialect 2 is not a valid dialec")); + + Ok(()) + } + + #[test] + #[cfg(all(feature = "pure_rust", not(feature = "native_client")))] + fn pure_rust_create_with_invalid_dialect() -> Result<(), FbError> { + let rs = builder_pure_rust() + .db_name("test_create_db55.fdb") + .user("SYSDBA") + .dialect(rsfbclient::Dialect::D2) + .create_database(); + + assert!(rs.is_err()); + assert!(rs.err().unwrap().to_string().contains("Database dialect 2 is not a valid dialec")); + + Ok(()) + } }