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}