rsdbc_core/
lib.rs

1use std::borrow::{Borrow, Cow};
2use std::collections::HashMap;
3use std::time::Duration;
4use url::Url;
5use crate::error::RsdbcErrors;
6
7pub mod error;
8pub mod connection;
9
10/// RSDBC Result type
11pub type Result<T> = std::result::Result<T, RsdbcErrors>;
12
13
14#[derive(Debug, Clone)]
15pub enum Value {
16    Int32(i32),
17    UInt32(u32),
18    String(String),
19    // TODO: add other types
20}
21
22
23
24
25// TODO: maybe enum?
26pub struct SQLWarning {
27
28}
29
30
31pub struct ConfigurationOption {
32
33}
34
35// Golang has TxOptions
36/// Specification of properties to be used when starting a transaction.
37/// This interface is typically implemented by code that calls [beginTransaction(TransactionDefinition)]
38pub trait TransactionDefinition {
39
40    // TODO: This might have to return Option<&V> like HashMap
41    /// Retrieve a transaction attribute value by its attribute identifier.
42    /// This low-level interface allows querying transaction attributes supported by the {@link Connection} that should be applied
43    ///
44    /// returns the value of the transaction attribute. Can be None to indicate absence of the attribute
45    fn get_attribute(&self, attribute: &str) -> OptionValue;
46}
47
48// TODO: where to put constants?
49pub struct TransactionOptions;
50impl TransactionOptions {
51    /// Isolation level requested for the transaction.
52    const ISOLATION_LEVEL: &'static str = "isolation_level";
53
54    /// The transaction mutability (i.e. whether the transaction should be started in read-only mode)
55    const READ_ONLY: &'static str = "read_only";
56
57    /// Name of the transaction.
58    const NAME: &'static str = "name";
59
60    ///
61    const LOCK_WAIT_TIMEOUT: &'static str = "lock_wait_timeout";
62}
63
64
65
66// TODO: Rename to ConfigurationValue??
67// TODO: improve experience with &str
68// TODO: add convenience methods to get values of a certain type back. Should be Result<blah>. TryInto?
69#[derive(Debug, Clone, PartialEq)]
70#[non_exhaustive]
71pub enum OptionValue {
72    Bool(bool),
73    Duration(Duration), // Chrono Duration? Should that be a feature?
74    Int(i32),
75    Map(HashMap<String, String>), // TODO: what type should the generics be?
76    String(String),
77}
78
79
80// TODO: this might be a good time to use a macro
81// TODO: implement others
82impl From<i32> for OptionValue {
83    fn from(value: i32) -> Self {
84        OptionValue::Int(value)
85    }
86}
87
88impl From<u16> for OptionValue {
89    fn from(value: u16) -> Self {
90        OptionValue::Int(value as i32)
91    }
92}
93
94impl From<bool> for OptionValue {
95    fn from(value: bool) -> Self {
96        OptionValue::Bool(value)
97    }
98}
99
100impl From<Cow<'_, str>> for OptionValue {
101    fn from(value: Cow<'_, str>) -> Self {
102        OptionValue::String(value.to_string())
103    }
104}
105
106impl From<String> for OptionValue {
107    fn from(value: String) -> Self {
108        OptionValue::String(value)
109    }
110}
111
112impl From<&str> for OptionValue {
113    fn from(value: &str) -> Self {
114        OptionValue::String(value.to_string())
115    }
116}
117
118impl From<Duration> for OptionValue {
119    fn from(value: Duration) -> Self {
120        OptionValue::Duration(value)
121    }
122}
123
124impl From<Url> for OptionValue {
125    fn from(value: Url) -> Self {
126        OptionValue::String(value.to_string())
127    }
128}
129
130impl From<HashMap<String, String>> for OptionValue {
131    fn from(value: HashMap<String, String>) -> Self {
132        OptionValue::Map(value)
133    }
134}
135
136// TODO: this is icky...is there a better way?
137impl From<HashMap<&str, &str>> for OptionValue {
138    fn from(value: HashMap<&str, &str>) -> Self {
139        let mut map = HashMap::new();
140        for (k, v) in value {
141            map.insert(k.to_string(), v.to_string());
142        }
143
144        OptionValue::Map(map)
145    }
146}
147
148fn parse_connection(url: &str) {
149    // TODO: parse url to Connection options
150}
151
152// TODO: explore this
153// https://github.com/launchbadge/sqlx/blob/b6e127561797fe9aababa24ec640275ecb9b42af/sqlx-core/src/value.rs
154// /// An owned value from the database.
155// pub trait Value {
156//
157// }
158
159
160
161
162/// An iterator over the mapped resulting rows of a query.
163///
164/// `F` is used to transform the _streaming_ iterator into a _standard_ iterator.
165// #[must_use = "iterators are lazy and do nothing unless consumed"]
166// pub struct MappedRows<'stmt, F> {
167//     rows: Rows<'stmt>,
168//     map: F,
169// }
170//
171// impl<T, F> Iterator for MappedRows<'_, F>
172//     where
173//         F: FnMut(&dyn Row<'_>) -> Result<T>,
174// {
175//     type Item = Result<T>;
176//
177//     #[inline]
178//     fn next(&mut self) -> Option<Result<T>> {
179//         let map = &mut self.map;
180//         self.rows
181//             .next()
182//             .transpose()
183//             .map(|row_result| row_result.and_then(|row| (map)(&row)))
184//     }
185// }
186
187/// Result set from executing a query against a statement
188pub trait ResultSet {
189    /// get meta data about this result set
190    fn meta_data(&self) -> Result<Box<dyn ResultSetMetaData>>;
191
192    /// Move the cursor to the next available row if one exists and return true if it does
193    fn next(&mut self) -> bool;
194
195    fn get_bool(&self, i: u64) -> Result<Option<bool>>;
196    fn get_i8(&self, i: u64) -> Result<Option<i8>>;
197    fn get_i16(&self, i: u64) -> Result<Option<i16>>;
198    fn get_i32(&self, i: u64) -> Result<Option<i32>>;
199    fn get_i64(&self, i: u64) -> Result<Option<i64>>;
200    fn get_f32(&self, i: u64) -> Result<Option<f32>>;
201    fn get_f64(&self, i: u64) -> Result<Option<f64>>;
202    fn get_string(&self, i: u64) -> Result<Option<String>>;
203    fn get_bytes(&self, i: u64) -> Result<Option<Vec<u8>>>;
204}
205
206
207
208/// Meta data for result set
209pub trait ResultSetMetaData {
210    fn num_columns(&self) -> u64;
211    fn column_name(&self, i: u64) -> String;
212    fn column_type(&self, i: u64) -> DataType;
213    fn column_type_name(&self, i: u64) -> String;
214    fn precision(&self, i: u64) -> u64;
215    fn schema_name(&self, i: u64) -> String;
216    fn table_name(&self, i: u64) -> String;
217    fn is_nullable(&self, i: u64) -> String;
218    fn is_read_only(&self, i: u64) -> String;
219}
220
221pub trait Row<'stmt> {
222    fn get_via_index<R>(&self, index: u32) -> R;
223
224    fn get_via_name<R>(&self, name: &str) -> R;
225
226
227    // from java RSDBC - extends readable
228    // RowMetadata getMetadata();
229
230}
231
232/// Represents the metadata for a row of the results returned from a query.
233/// Metadata for columns can be either retrieved by specifying a column name or the column index.
234/// Columns indexes are 0-based.
235/// Column names do not necessarily reflect the column names how they are in the underlying tables
236/// but rather how columns are represented (e.g. aliased) in the result.
237pub trait RowMetadata {
238
239    /// Returns the [ColumnMetadata] for one column in this row.
240    ///
241    /// Arguments:
242    ///
243    /// * `index`: the column index starting at 0
244    ///
245    /// return the [ColumnMetadata] for one column in this row
246    /// return index out of bounds error if [index] is out of range (negative or equals/exceeds [getColumnMetadatas().len()]
247    fn get_column_metadata(index: i32) -> Result<Box<dyn ColumnMetadata>>;
248
249
250    /// Returns the [ColumnMetadata] for one column in this row.
251    ///
252    /// Arguments:
253    ///
254    /// * `name`: the name of the column. Column names are case insensitive.
255    /// When a get method contains several columns with same name,
256    /// then the value of the first matching column will be returned.
257    ///
258    /// return the [ColumnMetadata] for one column in this row
259    /// returns illegal argument error if [name] empty
260    /// returns no such element if there is no column with the [name]
261    /// NoSuchElementException   if there is no column with the {@code name}
262    fn get_column_metadata_by_name<S: Into<String>>(name: S) -> Result<Box<dyn ColumnMetadata>>;
263
264
265    /// Returns the [ColumnMetadata] for all columns in this row.
266    fn get_column_metadatas() -> Vec<Box<dyn ColumnMetadata>>;
267
268    /// Returns whether this object contains metadata for [column_name].
269    /// Lookups are case-insensitive.
270    /// Implementations may allow escape characters to enforce a particular mode of comparison
271    /// when querying for presence/absence of a column.
272    ///
273    /// return true if this object contains metadata for [column_name]; false otherwise.
274    fn contains<S: Into<String>>(column_name: S) -> bool;
275}
276
277
278// TODO: do we want this generic trait or do we want something more specific like ColumnMetadata
279// Java R2DBC has this has database / sql types called Type
280// Java R2DBC has an enum that implements interface
281// R2dbcType is Definition of generic SQL types
282// Type descriptor for column- and parameter types.
283// SQLX - Provides information about a SQL type for the database driver.
284pub trait TypeInfo {
285
286    // might not need this
287    // https://stackoverflow.com/questions/21747136/how-do-i-print-the-type-of-a-variable-in-rust
288    fn rust_type(&self) -> &'static str;
289
290    fn name(&self) -> &str;
291}
292
293pub enum RsdbcType {
294    Char,
295    Varchar,
296    Nchar,
297    Nvarchar,
298    Clob,
299    Nclob,
300    Boolean,
301    Varbinary,
302    Blob,
303    Integer,
304    Tinyint,
305    Smallint,
306    Bigint,
307    Numeric,
308    Decimal,
309    Float,
310    Real,
311    Double,
312    Date,
313    Time,
314    TimeWithTimeZone,
315    Timestamp,
316    TimestampWithTimeZone,
317    Collection,
318}
319
320impl TypeInfo for RsdbcType {
321    fn rust_type(&self) -> &'static str {
322        todo!()
323    }
324
325    fn name(&self) -> &str {
326        match self {
327            RsdbcType::Char => "CHAR",
328            RsdbcType::Varchar => "VARCHAR",
329            RsdbcType::Nchar => "NCHAR",
330            RsdbcType::Nvarchar => "NVARCHAR",
331            RsdbcType::Clob => "CLOB",
332            RsdbcType::Nclob => "NCLOB",
333            RsdbcType::Boolean => "BOOLEAN",
334            RsdbcType::Varbinary => "VARBINARY",
335            RsdbcType::Blob => "BLOB",
336            RsdbcType::Integer => "INTEGER",
337            RsdbcType::Tinyint => "TINYINT",
338            RsdbcType::Smallint => "SMALLINT",
339            RsdbcType::Bigint => "BIGINT",
340            RsdbcType::Numeric => "NUMERIC",
341            RsdbcType::Decimal => "DECIMAL",
342            RsdbcType::Float => "FLOAT",
343            RsdbcType::Real => "REAL",
344            RsdbcType::Double => "DOUBLE",
345            RsdbcType::Date => "DATE",
346            RsdbcType::Time => "TIME",
347            RsdbcType::TimeWithTimeZone => "TIME_WITH_TIME_ZONE",
348            RsdbcType::Timestamp => "TIMESTAMP",
349            RsdbcType::TimestampWithTimeZone => "TIMESTAMP_WITH_TIME_ZONE",
350            RsdbcType::Collection => "COLLECTION",
351        }
352    }
353}
354
355
356// java R2DBC extends ReadableMetadata.
357/// Represents the metadata for a column of the results returned from a query.
358/// The implementation of all methods except [getName()] is optional for drivers.
359/// Column metadata is optionally available as by-product of statement execution on a best-effort basis.
360pub trait ColumnMetadata: ReadableMetadata {
361
362}
363
364
365pub enum Nullability {
366    Nullable,
367    NonNull,
368    Unknown
369}
370
371
372/// RSDBC Data Types
373#[derive(Debug, Copy, Clone, PartialEq, Eq)]
374pub enum DataType {
375    Bool,
376    Byte,
377    Char,
378    Short,
379    Integer,
380    Float,
381    Double,
382    Decimal,
383    Date,
384    Time,
385    Datetime,
386    Utf8,
387    Binary,
388}
389
390#[derive(Debug, Clone)]
391pub struct Column {
392    name: String,
393    data_type: DataType,
394    //precision: u6,
395}
396
397impl Column {
398    pub fn new(name: &str, data_type: DataType) -> Self {
399        Column {
400            name: name.to_owned(),
401            data_type,
402        }
403    }
404}
405
406impl ResultSetMetaData for Vec<Column> {
407    fn num_columns(&self) -> u64 {
408        self.len() as u64
409    }
410
411    fn column_name(&self, i: u64) -> String {
412        self[i as usize].name.clone()
413    }
414
415    fn column_type(&self, i: u64) -> DataType {
416        self[i as usize].data_type
417    }
418
419    // fn precision(&self, i: u64) -> u64 {
420    //     match self.column_type {
421
422    //     }
423    // }
424
425    fn column_type_name(&self, i: u64) -> String {
426        todo!()
427    }
428
429    fn precision(&self, i: u64) -> u64 {
430        todo!()
431    }
432
433    fn schema_name(&self, i: u64) -> String {
434        todo!()
435    }
436
437    fn table_name(&self, i: u64) -> String {
438        todo!()
439    }
440
441    fn is_nullable(&self, i: u64) -> String {
442        todo!()
443    }
444
445    fn is_read_only(&self, i: u64) -> String {
446        todo!()
447    }
448}
449
450pub trait DatabaseMetadata {
451
452}
453
454/// Represents a set of {@code OUT} parameters returned from a stored procedure.
455/// Values from out parameters can be either retrieved by specifying a parameter name or the parameter index.
456/// Parameter indexes are {@code 0}-based.
457///
458/// Parameter names used as input to getter methods are case insensitive.
459/// When a get method is called with a parameter name and several parameters have the same name,
460/// then the value of the first matching parameter will be returned.
461/// Parameters that are not explicitly named in the query should be referenced through parameter indexes.
462///
463/// For maximum portability, parameters within each {@link OutParameters} should be read in
464/// left-to-right order, and each parameter should be read only once.
465///
466/// [#get(String)] and [#get(int)] without specifying a target type returns a suitable value representation.
467/// The R2DBC specification contains a mapping table that shows default mappings between database
468/// types and Java types.
469/// Specifying a target type, the R2DBC driver attempts to convert the value to the target type.
470///
471/// A parameter is invalidated after consumption.
472///
473/// The number, type and characteristics of parameters are described through [OutParametersMetadata].
474pub trait OutParameters: Readable {
475    fn get_metadata(&self) -> Box<dyn OutParametersMetadata>;
476}
477
478/// Represents the metadata for [OUT] parameters of the results returned from a stored procedure.
479/// Metadata for parameters can be either retrieved by specifying a out parameter name or
480/// the out parameter index.
481/// Parameter indexes are 0-based.
482pub trait OutParametersMetadata {
483
484    /// Returns the [OutParameterMetadata] for one out parameter.
485    ///
486    /// Arguments:
487    /// * index the out parameter index starting at 0
488    ///
489    /// index out of bounds error if [index] is out of range (negative or equals/exceeds [getParameterMetadatas().len()])
490    fn get_parameter_metadata_by_index(&self, index: u32) -> Result<Box<dyn OutParameterMetadata>>;
491
492    /// Returns the [OutParameterMetadata] for one out parameter.
493    ///
494    /// Arguments:
495    /// * name the name of the out parameter.  Parameter names are case insensitive.
496    ///
497    /// index out of bounds error if [index] is out of range (negative or equals/exceeds [getParameterMetadatas().len()])
498    ///
499    /// illegal argument error is name is empty
500    /// no such element if there is no output parameter
501    fn get_parameter_metadata_by_name(&self, name: &str) -> Result<Box<dyn OutParameterMetadata>>;
502
503    /// Returns the [OutParameterMetadata] for all out parameters.
504    fn get_parameter_metadatas(&self) -> Vec<Box<dyn OutParameterMetadata>>; // TODO: bound trait to this?
505}
506
507/// Represents the metadata for an [OUT] parameter.
508/// The implementation of all methods except [#getName()]  is optional for drivers.
509/// Parameter metadata is optionally available as by-product of statement execution on a best-effort basis.
510pub trait OutParameterMetadata: ReadableMetadata {}
511
512/// Represents the metadata for readable object, for example a column of the results returned from
513/// a query or [OUT] parameter as result of running a stored procedure.
514/// The implementation of all methods except [get_name()] is optional for drivers.
515/// Metadata is optionally available as by-product of statement execution on a best-effort basis.
516pub trait ReadableMetadata {
517    // rust type
518    fn rust_type(&self) -> &'static str;
519
520    // type
521    /// returns the database Type [TypeInfo]
522    fn db_type(&self) -> dyn TypeInfo;
523
524    /// Returns the name.
525    ///
526    /// The name does not necessarily reflect the names how they are in the underlying tables but
527    /// rather how results are represented (e.g. aliased) in the result.
528    fn get_name(&self) -> String;
529
530    /// Returns the native type descriptor that potentially exposes more metadata.
531    /// Drivers should implement this method if they can expose a driver-specific type metadata
532    /// object exposing additional information.
533    ///
534    /// The default implementation returns [None].
535    fn get_native_type_metadata(&self);
536
537    // TODO: is this required?
538    /// Returns the nullability of values.
539    /// Implementation of this method is optional.
540    /// The default implementation returns [Nullability::Unknown].
541    fn get_nullability(&self) -> Nullability {
542        Nullability::Unknown
543    }
544
545    /// Returns the precision.
546    ///
547    /// * For numeric data, this is the maximum precision.
548    /// * For character data, this is the length in characters.
549    /// * For datetime data types, this is the length in bytes required to represent the value (assuming the
550    /// * maximum allowed precision of the fractional seconds component).
551    /// * For binary data, this is the length in bytes.
552    /// * Returns {@code null} for data types where data type size is not applicable or if the precision cannot be provided.
553    ///
554    /// Implementation of this method is optional.
555    /// The default implementation returns [None].
556    fn get_precision(&self) -> Option<u64> {
557        None
558    }
559
560    /// Returns the scale.
561    ///
562    /// * This is the number of digits to right of the decimal point.
563    /// * Returns {@code null} for data types where the scale is not applicable or if the scale cannot be provided.
564    ///
565    /// Implementation of this method is optional.
566    /// The default implementation returns [None].
567    ///
568    /// the scale or [None] if the scale is not available.
569    fn get_scale(&self) -> Option<u64> {
570        None
571    }
572}
573
574// TODO: revisit this along with docs relating to mapping of database types and rust
575/// Represents a readable object, for example a set of columns or {@code OUT} parameters from a
576/// database query, later on referred to as items.
577/// Values can for columns or {@code OUT} parameters be either retrieved by specifying a name or the index.
578/// Indexes are {@code 0}-based.
579///
580/// Column and [OUT] parameter names used as input to getter methods are case insensitive.
581/// When a [get] method is called with a name and several items have the same name, then the value
582/// of the first matching item will be returned.
583/// Items that are not explicitly named in the query should be referenced through indexes.
584///
585/// For maximum portability, items within each [Readable] should be read in left-to-right order,
586/// and each item should be read only once.
587///
588/// [#get(String)] and [#get(int)] without specifying a target type returns a suitable value representation.
589/// The R2DBC specification contains a mapping table that shows default mappings between database
590/// types and Rust types.
591/// Specifying a target type, the R2DBC driver attempts to convert the value to the target type.
592///
593/// A item is invalidated after consumption.
594pub trait Readable {
595
596    /// Returns the value for a parameter.
597    ///
598    /// Arguments:
599    ///
600    /// * `index`: the index starting at 0
601    ///
602    /// return the value which can be None
603    /// index out of bounds error in index is out of range (negative or equals/exceeds the number of readable objects)
604    fn get<T: FromSql>(&self, index: u32) -> Result<T>;
605
606    /// Returns the value for a parameter.
607    ///
608    /// Arguments:
609    ///
610    /// * `index`: the index starting at 0
611    ///
612    /// return the value which can be None
613    /// index out of bounds error in index is out of range (negative or equals/exceeds the number of readable objects)
614    fn get_by_name<S: Into<String>, T: FromSql>(&self, name: S) -> Result<T>;
615
616}
617
618
619// TODO: change name
620/// A specialized result type representing the result of deserializing
621/// a value from the database.
622pub type FomSqlResult<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
623
624// https://github.com/diesel-rs/diesel/blob/8a53cb7c8a09ae891df6c104d1c8a03d51ee07fc/diesel/src/deserialize.rs#L412
625pub trait FromSql {
626
627    // TODO: I'm sure this wont work and we'll need to do something more
628    fn from_sql<T>(bytes: T) -> Result<Box<Self>>;
629
630}
631
632
633
634// * Represents a parameter to be interchanged. Parameters are typed and can define a value.
635// Parameters without a value correspond with a SQL {@code NULL} value.
636// * Parameters can be classified as {@link In input} or {@link Out output} parameters.
637pub trait Parameter {
638    // get type
639    // get value
640}
641
642// Marker interface to classify a parameter as input parameter.
643// Parameters that do not implement {@link Out} default to in parameters.
644pub trait In {}
645
646// Marker interface to classify a parameter as output parameter.
647// Parameters can implement both, {@code In} and {@code Out} interfaces to be classified as in-out parameters.
648pub trait Out {}
649
650
651// Lob / Clob / Blob
652
653
654
655
656#[cfg(test)]
657mod tests {
658    #[test]
659    fn it_works() {
660        let result = 2 + 2;
661        assert_eq!(result, 4);
662    }
663}