1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! Traits to abstract over firebird client implementations

use num_enum::TryFromPrimitive;
use std::str::FromStr;

use crate::*;

///A wrapper trait compatible with the niceties provided by the main rsfbclient crate
pub trait FirebirdClient
where
    Self: FirebirdClientDbOps,
    Self: FirebirdClientSqlOps<DbHandle = <Self as FirebirdClientDbOps>::DbHandle>,
{
}

impl<Hdl, A: FirebirdClientDbOps<DbHandle = Hdl> + FirebirdClientSqlOps<DbHandle = Hdl>>
    FirebirdClient for A
where
    Hdl: Send,
{
}

/// Responsible for database administration and attachment/detachment
pub trait FirebirdClientDbOps: Send {
    /// A database handle
    type DbHandle: Send;

    /// Configuration details for attaching to the database.
    /// A user of an implementation of this trait can configure attachment details
    /// (database name, user name, etcetera) and then pass this configuration to the implementation
    /// via this type when a new attachment is requested
    type AttachmentConfig: Send + Clone;

    /// Create a new attachment to a database with the provided configuration
    /// Returns a database handle on success
    fn attach_database(
        &mut self,
        config: &Self::AttachmentConfig,
        dialect: Dialect,
        no_db_triggers: bool,
    ) -> Result<Self::DbHandle, FbError>;

    /// Disconnect from the database
    fn detach_database(&mut self, db_handle: &mut Self::DbHandle) -> Result<(), FbError>;

    /// Drop the database
    fn drop_database(&mut self, db_handle: &mut Self::DbHandle) -> Result<(), FbError>;

    /// Create the database and attach
    /// Returns a database handle on success
    fn create_database(
        &mut self,
        config: &Self::AttachmentConfig,
        page_size: Option<u32>,
        dialect: Dialect,
    ) -> Result<Self::DbHandle, FbError>;
}

///Responsible for actual transaction and statement execution
pub trait FirebirdClientSqlOps {
    /// A database handle
    type DbHandle: Send;
    /// A transaction handle
    type TrHandle: Send;
    /// A statement handle
    type StmtHandle: Send;

    /// Start a new transaction, with the specified transaction parameter buffer
    fn begin_transaction(
        &mut self,
        db_handle: &mut Self::DbHandle,
        confs: TransactionConfiguration,
    ) -> Result<Self::TrHandle, FbError>;

    /// Commit / Rollback a transaction
    fn transaction_operation(
        &mut self,
        tr_handle: &mut Self::TrHandle,
        op: TrOp,
    ) -> Result<(), FbError>;

    /// Execute a sql immediately, without returning rows
    fn exec_immediate(
        &mut self,
        db_handle: &mut Self::DbHandle,
        tr_handle: &mut Self::TrHandle,
        dialect: Dialect,
        sql: &str,
    ) -> Result<(), FbError>;

    /// Allocate and prepare a statement
    /// Returns the statement type and handle
    fn prepare_statement(
        &mut self,
        db_handle: &mut Self::DbHandle,
        tr_handle: &mut Self::TrHandle,
        dialect: Dialect,
        sql: &str,
    ) -> Result<(StmtType, Self::StmtHandle), FbError>;

    /// Closes or drops a statement
    fn free_statement(
        &mut self,
        stmt_handle: &mut Self::StmtHandle,
        op: FreeStmtOp,
    ) -> Result<(), FbError>;

    /// Execute the prepared statement with parameters
    /// and returns the affected rows count
    fn execute(
        &mut self,
        db_handle: &mut Self::DbHandle,
        tr_handle: &mut Self::TrHandle,
        stmt_handle: &mut Self::StmtHandle,
        params: Vec<SqlType>,
    ) -> Result<usize, FbError>;

    /// Execute the prepared statement
    /// with input and output parameters.
    ///
    /// The output parameters will be returned
    /// as in the Result
    fn execute2(
        &mut self,
        db_handle: &mut Self::DbHandle,
        tr_handle: &mut Self::TrHandle,
        stmt_handle: &mut Self::StmtHandle,
        params: Vec<SqlType>,
    ) -> Result<Vec<Column>, FbError>;

    /// Fetch rows from the executed statement, coercing the types
    /// according to the provided blr
    fn fetch(
        &mut self,
        db_handle: &mut Self::DbHandle,
        tr_handle: &mut Self::TrHandle,
        stmt_handle: &mut Self::StmtHandle,
    ) -> Result<Option<Vec<Column>>, FbError>;
}

/// Firebird base event API
pub trait FirebirdClientDbEvents: FirebirdClientDbOps {
    /// Wait for an event to be posted on database
    fn wait_for_event(
        &mut self,
        db_handle: &mut Self::DbHandle,
        name: String,
    ) -> Result<(), FbError>;
}

#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
#[repr(u8)]
/// Firebird sql dialect
pub enum Dialect {
    D1 = 1,
    D2 = 2,
    #[default]
    D3 = 3,
}

impl FromStr for Dialect {
    type Err = FbError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "1" => Ok(Dialect::D1),
            "2" => Ok(Dialect::D2),
            "3" => Ok(Dialect::D3),
            _ => Err(FbError::from(format!(
                "'{}' doesn't represent any dialect",
                s
            ))),
        }
    }
}

#[repr(u8)]
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
/// Drop / Close statement
pub enum FreeStmtOp {
    Close = ibase::DSQL_close as u8,
    Drop = ibase::DSQL_drop as u8,
}

#[repr(u8)]
#[derive(Debug, Eq, PartialEq, Copy, Clone, TryFromPrimitive)]
/// Statement type
pub enum StmtType {
    Select = 1,        // isc_info_sql_stmt_select
    Insert = 2,        // isc_info_sql_stmt_insert
    Update = 3,        // isc_info_sql_stmt_update
    Delete = 4,        // isc_info_sql_stmt_delete
    Ddl = 5,           // isc_info_sql_stmt_ddl
    GetSegment = 6,    // isc_info_sql_stmt_get_segment
    PutSegment = 7,    // isc_info_sql_stmt_put_segment
    ExecProcedure = 8, // isc_info_sql_stmt_exec_procedure
    StartTrans = 9,    // isc_info_sql_stmt_start_trans
    Commit = 10,       // isc_info_sql_stmt_commit
    Rollback = 11,     // isc_info_sql_stmt_rollback
    SelectForUpd = 12, // isc_info_sql_stmt_select_for_upd
    SetGenerator = 13, // isc_info_sql_stmt_set_generator
    Savepoint = 14,    // isc_info_sql_stmt_savepoint
}