odbc_iter/
lib.rs

1/*!
2`odbc-iter` is a Rust high level database access library based on `odbc` crate that uses native ODBC drivers to access a variety of databases.
3
4With this library you can:
5* connect to any database supporting ODBC standard (e.g. via `unixodbc` library and ODBC database driver),
6* run one-off, prepared or parametrized queries,
7* iterate result set via standard `Iterator` interface,
8* automatically convert rows into:
9    * tuples of Rust standard types,
10    * custom type implementing a trait,
11    * vector of dynamically typed values,
12* create thread local connections for multithreaded applications.
13
14Things still missing:
15* support for `DECIMAL` types - currently `DECIMAL` columns need to be cast to `DOUBLE` on the query (PR welcome),
16* rest of this list - please open issue in `GitHub` issue tracker for missing functionality, bugs, etc..
17
18Example usage
19=============
20
21Connect and run one-off queries with row type conversion
22-------------
23
24```rust
25use odbc_iter::{Odbc, ValueRow};
26
27// Connect to database using connection string
28let connection_string = std::env::var("DB_CONNECTION_STRING")
29    .expect("DB_CONNECTION_STRING environment not set");
30let mut connection = Odbc::connect(&connection_string)
31    .expect("failed to connect to database");
32
33// Handle statically guards access to connection and provides query functionality
34let mut db = connection.handle();
35
36// Get single row single column value
37println!("{}", db.query::<String>("SELECT 'hello world'").expect("failed to run query")
38    .single().expect("failed to fetch row"));
39
40// Iterate rows with single column
41for row in db.query::<String>("SELECT 'hello world' UNION SELECT 'foo bar'")
42    .expect("failed to run query") {
43    println!("{}", row.expect("failed to fetch row"))
44}
45// Prints:
46// hello world
47// foo bar
48
49// Iterate rows with multiple columns
50for row in db.query::<(String, i8)>(
51    "SELECT 'hello world', CAST(24 AS TINYINT) UNION SELECT 'foo bar', CAST(32 AS TINYINT)")
52    .expect("failed to run query") {
53    let (string, number) = row.expect("failed to fetch row");
54    println!("{} {}", string, number);
55}
56// Prints:
57// hello world 24
58// foo bar 32
59
60// Iterate rows with dynamically typed values using `ValueRow` type that can represent
61// any row
62for row in db.query::<ValueRow>("SELECT 'hello world', 24 UNION SELECT 'foo bar', 32")
63    .expect("failed to run query") {
64    println!("{:?}", row.expect("failed to fetch row"))
65}
66// Prints:
67// [Some(String("hello world")), Some(Tinyint(24))]
68// [Some(String("foo bar")), Some(Tinyint(32))]
69```
70
71Using prepared statements and parametrized queries
72-------------
73
74```rust
75use odbc_iter::{Odbc, ValueRow};
76
77// Connect to database using connection string
78let connection_string = std::env::var("DB_CONNECTION_STRING")
79    .expect("DB_CONNECTION_STRING environment not set");
80let mut connection = Odbc::connect(&connection_string)
81    .expect("failed to connect to database");
82
83// Handle statically guards access to connection and provides query functionality
84let mut db = connection.handle();
85
86// Allocate `PreparedStatement` on given connection
87let prepared_statement = db
88    .prepare("SELECT 'hello world' AS foo, CAST(42 AS INTEGER) AS bar, CAST(10000000 AS BIGINT) AS baz")
89    .expect("prepare prepared_statement");
90
91// Use `?` as placeholder for value
92let parametrized_query = db
93    .prepare("SELECT ?, ?, ?")
94    .expect("prepare parametrized_query");
95
96// Database can infer schema of prepared statement
97println!("{:?}", prepared_statement.schema());
98// Prints:
99// Ok([ColumnType { datum_type: String, odbc_type: SQL_VARCHAR, nullable: false, name: "foo" },
100// ColumnType { datum_type: Integer, odbc_type: SQL_INTEGER, nullable: true, name: "bar" },
101// ColumnType { datum_type: Bigint, odbc_type: SQL_EXT_BIGINT, nullable: true, name: "baz" }])
102
103// Execute prepared statement without binding parameters
104let result_set = db
105    .execute::<ValueRow>(prepared_statement)
106    .expect("failed to run query");
107
108// Note that in this example `prepared_statement` will be dropped with the `result_set`
109// iterator and cannot be reused
110for row in result_set {
111    println!("{:?}", row.expect("failed to fetch row"))
112}
113// Prints:
114// [Some(String("hello world")), Some(Integer(42)), Some(Bigint(10000000))]
115
116// Execute parametrized query by binding parameters to statement
117let mut result_set = db
118    .execute_with_parameters::<ValueRow, _>(parametrized_query, |q| {
119        q
120            .bind(&"hello world")?
121            .bind(&43)?
122            .bind(&1_000_000)
123    })
124    .expect("failed to run query");
125
126// Passing `&mut` reference so we don't lose access to `result_set`
127for row in &mut result_set {
128    println!("{:?}", row.expect("failed to fetch row"))
129}
130// Prints:
131// [Some(String("hello world")), Some(Integer(43)), Some(Bigint(1000000))]
132
133// Get back the statement for later use dropping any unconsumed rows
134let parametrized_query = result_set.close().expect("failed to close result set");
135
136// Bind new set of parameters to prepared statement
137let mut result_set = db
138    .execute_with_parameters::<ValueRow, _>(parametrized_query, |q| {
139        q
140            .bind(&"foo bar")?
141            .bind(&99)?
142            .bind(&2_000_000)
143    })
144    .expect("failed to run query");
145
146for row in &mut result_set {
147    println!("{:?}", row.expect("failed to fetch row"))
148}
149// Prints:
150// [Some(String("foo bar")), Some(Integer(99)), Some(Bigint(2000000))]
151```
152
153Using thread local connection
154-------------
155```rust
156use odbc_iter::{Odbc, ValueRow};
157
158// Connect to database using connection string
159let connection_string = std::env::var("DB_CONNECTION_STRING")
160    .expect("DB_CONNECTION_STRING environment not set");
161
162// `connection_with` can be used to create one connection per thread
163let result = odbc_iter::thread_local::connection_with(&connection_string, |mut connection| {
164    // Provided object contains result of the connection operation
165    // in case of error calling `connection_with` again will result
166    // in new connection attempt
167    let mut connection = connection.expect("failed to connect");
168
169    // Handle statically guards access to connection and provides query functionality
170    let mut db = connection.handle();
171
172    // Get single row single column value
173    let result = db.query::<String>("SELECT 'hello world'")
174        .expect("failed to run query").single().expect("failed to fetch row");
175
176    // Return connection back to thread local so it can be reused later on along
177    // with the result of the query that will be returned by the `connection_with` call
178    // Returning `None` connection is useful to force new connection attempt on the
179    // next call
180    (Some(connection), result)
181});
182
183println!("{}", result);
184// Prints:
185// hello world
186
187```
188
189Converting column values to `chrono` crate's date and time types (with "chrono" feature)
190-------------
191```rust
192# #[cfg(feature = "chrono")]
193# {
194use odbc_iter::{Odbc, ValueRow};
195use chrono::NaiveDateTime;
196
197// Connect to database using connection string
198let connection_string = std::env::var("DB_CONNECTION_STRING")
199    .expect("DB_CONNECTION_STRING environment not set");
200let mut connection = Odbc::connect(&connection_string)
201    .expect("failed to connect to database");
202
203// Handle statically guards access to connection and provides query functionality
204let mut db = connection.handle();
205
206// Get `chrono::NaiveDateTime` value
207println!("{}", db.query::<NaiveDateTime>("SELECT CAST('2019-05-03 13:21:33.749' AS DATETIME2)")
208    .expect("failed to run query").single().expect("failed to fetch row"));
209// Prints:
210// 2019-05-03 13:21:33.749
211# }
212```
213
214Query JSON column from MonetDB (with "serde_json" feature)
215-------------
216
217```rust
218# #[cfg(feature = "serde_json")]
219# #[cfg(feature = "test-monetdb")]
220# {
221use odbc_iter::{Odbc, Value};
222
223// Connect to database using connection string
224let connection_string = std::env::var("MONETDB_ODBC_CONNECTION")
225    .expect("MONETDB_ODBC_CONNECTION environment not set");
226let mut connection = Odbc::connect(&connection_string)
227    .expect("failed to connect to database");
228
229// Handle statically guards access to connection and provides query functionality
230let mut db = connection.handle();
231
232// Get `Value::Json` variant containing `serde_json::Value` object
233println!("{}", db.query::<Value>(r#"SELECT CAST('{ "foo": 42 }' AS JSON)"#)
234    .expect("failed to run query").single().expect("failed to fetch row"));
235// Prints:
236// {"foo":42}
237# }
238```
239
240Serializing `Value` and `ValueRow` using `serde` to JSON string (with "serde" feature)
241-------------
242
243```rust
244# #[cfg(feature = "serde_json")]
245# #[cfg(feature = "serde")]
246# {
247use odbc_iter::{Odbc, ValueRow};
248
249// Connect to database using connection string
250let connection_string = std::env::var("DB_CONNECTION_STRING")
251    .expect("DB_CONNECTION_STRING environment not set");
252let mut connection = Odbc::connect(&connection_string)
253    .expect("failed to connect to database");
254
255// Handle statically guards access to connection and provides query functionality
256let mut db = connection.handle();
257
258// Get `ValueRow` (or just single `Value`) that implements `serde::Serialize` trait
259let row = db.query::<ValueRow>("SELECT 'hello world', CAST(42 AS INTEGER), CAST(10000000 AS BIGINT)")
260    .expect("failed to run query").single().expect("failed to fetch row");
261
262println!("{}", serde_json::to_string(&row).expect("failed to serialize"));
263// Prints:
264// ["hello world",42,10000000]
265# }
266```
267
268UTF-16 databases (e.g. SQL Server)
269=============
270
271With SQL Server `NVARCHAR` data cannot be passed via query text (`N"foo"`) as query text itself is encoded as Rust String and hence UTF-8 and not UTF-16 as expected by SQL Server.
272
273To correctly query `NVARCHAR` columns as `String` connection has to be configured like this:
274```rust
275use odbc_iter::{Odbc, Settings, ValueRow};
276
277// Connect to database using connection string
278let connection_string = std::env::var("DB_CONNECTION_STRING")
279    .expect("DB_CONNECTION_STRING environment not set");
280
281let mut connection = Odbc::connect_with_settings(&connection_string, Settings {
282    utf_16_strings: true,
283}).expect("failed to connect to database");
284```
285
286To correctly insert `NVARCHAR` column value, the `String` has to be cast to UTF-16 and bound as `&[u16]`:
287```rust
288use odbc_iter::{Odbc, Settings, ValueRow};
289
290// Connect to database using connection string
291let connection_string = std::env::var("DB_CONNECTION_STRING")
292    .expect("DB_CONNECTION_STRING environment not set");
293
294let mut connection = Odbc::connect_with_settings(&connection_string, Settings {
295    utf_16_strings: true,
296}).expect("failed to connect to database");
297
298let mut db = connection.handle();
299
300let utf_16_string = "Fóó".encode_utf16().collect::<Vec<u16>>();
301let data: ValueRow = db.query_with_parameters("SELECT ? AS val", |q| q.bind(&utf_16_string))
302    .expect("failed to run query")
303    .single()
304    .expect("fetch data");
305```
306
307Alternatively the provided `StringUtf16` type can be bound (implementes Deserialize and custom Debug):
308```rust
309use odbc_iter::{Odbc, Settings, ValueRow, StringUtf16};
310
311// Connect to database using connection string
312let connection_string = std::env::var("DB_CONNECTION_STRING")
313    .expect("DB_CONNECTION_STRING environment not set");
314
315let mut connection = Odbc::connect_with_settings(&connection_string, Settings {
316    utf_16_strings: true,
317}).expect("failed to connect to database");
318
319let mut db = connection.handle();
320
321let utf_16_string = StringUtf16::from("Fóó");
322let data: ValueRow = db.query_with_parameters("SELECT ? AS val", |q| q.bind(&utf_16_string))
323    .expect("failed to run query")
324    .single()
325    .expect("fetch data");
326```
327
328Runtime statistics (with "statistics" feature)
329-------------
330
331If enabled, function `odbc_iter::statistics()` will provide runtime statistics that can be `Display`ed.
332
333
334`ODBC statistics: connections: open: 5, queries: preparing: 2, executing: 1, fetching: 2, done: 5, failed: 0`
335
336Note that they are not strongly synchronised so things may be observed counted twice.
337
338!*/
339
340use error_context::prelude::*;
341use lazy_static::lazy_static;
342use odbc::{DiagnosticRecord, DriverInfo, Environment, Version3};
343use regex::Regex;
344use std::error::Error;
345use std::fmt;
346use std::sync::atomic;
347use std::sync::atomic::AtomicBool;
348
349// Extra types that can be queried
350pub use odbc::{SqlDate, SqlSsTime2, SqlTime, SqlTimestamp};
351// ResultSet can be parametrized with this types
352pub use odbc::{Executed, Prepared};
353
354mod query;
355pub use query::*;
356mod result_set;
357pub use result_set::*;
358mod row;
359pub use row::*;
360mod value;
361pub use value::*;
362mod value_row;
363pub use value_row::*;
364mod stats;
365#[cfg(feature = "statistics")]
366pub use stats::statistics;
367
368pub mod odbc_type;
369pub mod thread_local;
370
371pub use odbc_type::StringUtf16;
372
373/// ODBC library initialization and connection errors.
374#[derive(Debug)]
375pub struct OdbcError(Option<DiagnosticRecord>, &'static str);
376
377impl fmt::Display for OdbcError {
378    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
379        write!(f, "ODBC call failed while {}", self.1)
380    }
381}
382
383fn to_dyn(diag: &Option<DiagnosticRecord>) -> Option<&(dyn Error + 'static)> {
384    diag.as_ref().map(|e| e as &(dyn Error + 'static))
385}
386
387impl Error for OdbcError {
388    fn source(&self) -> Option<&(dyn Error + 'static)> {
389        to_dyn(&self.0)
390    }
391}
392
393impl From<ErrorContext<Option<DiagnosticRecord>, &'static str>> for OdbcError {
394    fn from(err: ErrorContext<Option<DiagnosticRecord>, &'static str>) -> OdbcError {
395        OdbcError(err.error, err.context)
396    }
397}
398
399impl From<ErrorContext<DiagnosticRecord, &'static str>> for OdbcError {
400    fn from(err: ErrorContext<DiagnosticRecord, &'static str>) -> OdbcError {
401        OdbcError(Some(err.error), err.context)
402    }
403}
404
405/// ODBC environment entry point.
406///
407/// There should be only one object of this type in your program.
408/// It is stored as global static and accessed via associated static functions.
409pub struct Odbc {
410    environment: Environment<Version3>,
411}
412
413/// "The ODBC Specification indicates that an external application or process should use a single environment handle
414/// that is shared by local threads. The threads share the environment handle by using it as a common resource
415/// for allocating individual connection handles." (http://www.firstsql.com/ithread5.htm)
416/// lazy_static will make sure only one environment is initialized.
417unsafe impl Sync for Odbc {}
418
419lazy_static! {
420    static ref ODBC: Odbc = Odbc::new().expect("Failed to initialize ODBC environment");
421}
422
423/// We need to allow mutable environment to be used to list drivers but only one environment should exist at the same time.
424static ODBC_INIT: AtomicBool = AtomicBool::new(false);
425
426impl fmt::Debug for Odbc {
427    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
428        fmt.debug_struct("Odbc").field("version", &3).finish()
429    }
430}
431
432impl Odbc {
433    fn new() -> Result<Odbc, OdbcError> {
434        if ODBC_INIT.compare_exchange(false, true, atomic::Ordering::SeqCst, atomic::Ordering::SeqCst).is_err() {
435            panic!("ODBC environment already initialised");
436        }
437
438        odbc::create_environment_v3()
439            .wrap_error_while("creating v3 environment")
440            .map_err(Into::into)
441            .map(|environment| Odbc { environment })
442    }
443
444    /// Initialize global static ODBC environment now.
445    /// After this was called call to `list_drivers()` will panic.
446    /// Connecting to a database will also initialize the environment.
447    /// This function will panic if there was a problem crating ODBC environment.
448    pub fn initialize() {
449        lazy_static::initialize(&ODBC);
450    }
451
452    /// Provides list of `DriverInfo` structures describing available ODBC drivers.
453    /// This will panic if ODBC was already initialized by `Odbc::connect()` or `Odbc::initialize()`.
454    pub fn list_drivers() -> Result<Vec<DriverInfo>, OdbcError> {
455        // we need mutable access to environment
456        let mut odbc = Odbc::new()?;
457        let ret = odbc
458            .environment
459            .drivers()
460            .wrap_error_while("listing drivers")
461            .map_err(Into::into);
462
463        // Drop Odbc after providing list of drivers so we can allocate static singleton
464        drop(odbc);
465        ODBC_INIT.store(false, atomic::Ordering::SeqCst);
466
467        ret
468    }
469
470    /// Connect to database using connection string with default configuration options.
471    /// This implementation will synchronize driver connect calls.
472    pub fn connect(connection_string: &str) -> Result<Connection, OdbcError> {
473        Connection::new(&ODBC, connection_string)
474    }
475
476    /// Connect to database using connection string with default configuration options.
477    /// Assume that driver connect call is thread safe.
478    pub unsafe fn connect_concurrent(connection_string: &str) -> Result<Connection, OdbcError> {
479        Connection::new_concurrent(&ODBC, connection_string)
480    }
481
482    /// Connect to database using connection string with configuration options.
483    /// This implementation will synchronize driver connect calls.
484    pub fn connect_with_settings(
485        connection_string: &str,
486        settings: Settings,
487    ) -> Result<Connection, OdbcError> {
488        Connection::with_settings(&ODBC, connection_string, settings)
489    }
490
491    /// Connect to database using connection string with configuration options.
492    /// Assume that driver connect call is thread safe.
493    pub unsafe fn connect_with_settings_concurrent(
494        connection_string: &str,
495        settings: Settings,
496    ) -> Result<Connection, OdbcError> {
497        Connection::with_settings_concurrent(&ODBC, connection_string, settings)
498    }
499}
500
501/// Error splitting SQL script into single queries.
502#[derive(Debug)]
503pub struct SplitQueriesError;
504
505impl fmt::Display for SplitQueriesError {
506    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
507        write!(f, "failed to split queries")
508    }
509}
510
511impl Error for SplitQueriesError {}
512
513/// Split SQL script into list of queries.
514/// Each query needs to be terminated with semicolon (";").
515/// Lines starting with two dashes ("--") are skipped.
516pub fn split_queries(queries: &str) -> impl Iterator<Item = Result<&str, SplitQueriesError>> {
517    lazy_static! {
518        // https://regex101.com/r/6YTuVG/4
519        static ref RE: Regex = Regex::new(r#"(?:[\t \n]|--.*\n|!.*\n)*((?:[^;"']+(?:'(?:[^'\\]*(?:\\.)?)*')?(?:"(?:[^"\\]*(?:\\.)?)*")?)*;) *"#).unwrap();
520    }
521    RE.captures_iter(queries)
522        .map(|c| c.get(1).ok_or(SplitQueriesError))
523        .map(|r| r.map(|m| m.as_str()))
524}
525
526#[cfg(test)]
527pub mod tests {
528    use super::*;
529    #[allow(unused_imports)]
530    use assert_matches::assert_matches;
531
532    // 600 chars
533    #[cfg(any(
534        feature = "test-sql-server",
535        feature = "test-hive",
536        feature = "test-monetdb"
537    ))]
538    const LONG_STRING: &'static str = "Lórem ipsum dołor sit amet, cońsectetur adipiścing elit. Fusce risus ipsum, ultricies ac odio ut, vestibulum hendrerit leo. Nunc cursus dapibus mattis. Donec quis est arcu. Sed a tortor sit amet erat euismod pulvinar. Etiam eu erat eget turpis semper finibus. Etiam lobortis egestas diam a consequat. Morbi iaculis lorem sed erat iaculis vehicula. Praesent at porttitor eros. Quisque tincidunt congue ornare. Donec sed nulla a ex sollicitudin lacinia. Fusce ut fermentum tellus, id pretium libero. Donec dapibus faucibus sapien at semper. In id felis sollicitudin, luctus doloź sit amet orci aliquam.";
539
540    #[cfg(feature = "test-sql-server")]
541    pub fn sql_server_connection_string() -> String {
542        std::env::var("SQL_SERVER_ODBC_CONNECTION").expect("SQL_SERVER_ODBC_CONNECTION not set")
543    }
544
545    #[cfg(feature = "test-sql-server")]
546    pub fn connect_sql_server() -> Connection {
547        Odbc::connect(sql_server_connection_string().as_str()).expect("connect to SQL Server")
548    }
549
550    #[cfg(feature = "test-sql-server")]
551    pub fn connect_sql_server_with_settings(settings: Settings) -> Connection {
552        Odbc::connect_with_settings(sql_server_connection_string().as_str(), settings)
553            .expect("connect to SQL ServerMonetDB")
554    }
555
556    #[cfg(feature = "test-hive")]
557    pub fn hive_connection_string() -> String {
558        std::env::var("HIVE_ODBC_CONNECTION").expect("HIVE_ODBC_CONNECTION not set")
559    }
560
561    #[cfg(feature = "test-hive")]
562    pub fn connect_hive() -> Connection {
563        unsafe {
564            Odbc::connect_concurrent(hive_connection_string().as_str()).expect("connect to Hive")
565        }
566    }
567
568    #[cfg(feature = "test-hive")]
569    pub fn connect_hive_with_settings(settings: Settings) -> Connection {
570        unsafe {
571            Odbc::connect_with_settings_concurrent(hive_connection_string().as_str(), settings)
572                .expect("connect to Hive")
573        }
574    }
575
576    #[cfg(feature = "test-monetdb")]
577    pub fn monetdb_connection_string() -> String {
578        std::env::var("MONETDB_ODBC_CONNECTION").expect("MONETDB_ODBC_CONNECTION not set")
579    }
580
581    #[cfg(feature = "test-monetdb")]
582    pub fn connect_monetdb() -> Connection {
583        unsafe {
584            Odbc::connect_concurrent(monetdb_connection_string().as_str()).expect("connect to MonetDB")
585        }
586    }
587
588    #[cfg(feature = "test-monetdb")]
589    pub fn connect_monetdb_with_settings(settings: Settings) -> Connection {
590        unsafe {
591            Odbc::connect_with_settings_concurrent(monetdb_connection_string().as_str(), settings)
592                .expect("connect to MonetDB")
593        }
594    }
595
596    #[cfg(feature = "test-hive")]
597    #[test]
598    fn test_hive_multiple_rows() {
599        let mut hive = connect_hive();
600
601        let data = hive
602            .handle()
603            .query::<ValueRow>("SELECT explode(x) AS n FROM (SELECT array(42, 24) AS x) d;")
604            .expect("failed to run query")
605            .collect::<Result<Vec<_>, _>>()
606            .expect("fetch data");
607
608        assert_matches!(data[0][0], Some(Value::Integer(number)) => assert_eq!(number, 42));
609        assert_matches!(data[1][0], Some(Value::Integer(number)) => assert_eq!(number, 24));
610    }
611
612    #[cfg(feature = "test-hive")]
613    #[test]
614    fn test_hive_multiple_columns() {
615        let mut hive = connect_hive();
616
617        let data = hive
618            .handle()
619            .query::<ValueRow>("SELECT 42, 24;")
620            .expect("failed to run query")
621            .collect::<Result<Vec<_>, _>>()
622            .expect("fetch data");
623
624        assert_matches!(data[0][0], Some(Value::Integer(number)) => assert_eq!(number, 42));
625        assert_matches!(data[0][1], Some(Value::Integer(number)) => assert_eq!(number, 24));
626    }
627
628    #[cfg(feature = "test-hive")]
629    #[test]
630    fn test_hive_types_integer() {
631        let mut hive = connect_hive();
632
633        let data = hive.handle()
634            .query::<ValueRow>("SELECT cast(127 AS TINYINT), cast(32767 AS SMALLINT), cast(2147483647 AS INTEGER), cast(9223372036854775807 AS BIGINT);")
635            .expect("failed to run query")
636            .collect::<Result<Vec<_>, _>>()
637            .expect("fetch data");
638
639        assert_matches!(data[0][0], Some(Value::Tinyint(number)) => assert_eq!(number, 127));
640        assert_matches!(data[0][1], Some(Value::Smallint(number)) => assert_eq!(number, 32767));
641        assert_matches!(data[0][2], Some(Value::Integer(number)) => assert_eq!(number, 2147483647));
642        assert_matches!(data[0][3], Some(Value::Bigint(number)) => assert_eq!(number, 9223372036854775807));
643    }
644
645    #[cfg(feature = "test-hive")]
646    #[test]
647    fn test_hive_types_boolean() {
648        let mut hive = connect_hive();
649
650        let data = hive
651            .handle()
652            .query::<ValueRow>("SELECT true, false, CAST(NULL AS BOOLEAN)")
653            .expect("failed to run query")
654            .collect::<Result<Vec<_>, _>>()
655            .expect("fetch data");
656
657        assert_matches!(data[0][0], Some(Value::Bit(true)));
658        assert_matches!(data[0][1], Some(Value::Bit(false)));
659        assert!(data[0][2].is_none());
660    }
661
662    #[cfg(feature = "test-hive")]
663    #[test]
664    fn test_hive_types_string() {
665        let mut hive = connect_hive();
666
667        let data = hive
668            .handle()
669            .query::<ValueRow>("SELECT cast('foo' AS STRING), cast('bar' AS VARCHAR);")
670            .expect("failed to run query")
671            .collect::<Result<Vec<_>, _>>()
672            .expect("fetch data");
673
674        assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, "foo"));
675        assert_matches!(data[0][1], Some(Value::String(ref string)) => assert_eq!(string, "bar"));
676    }
677
678    #[cfg(feature = "test-sql-server")]
679    #[test]
680    fn test_sql_server_types_string() {
681        let mut connection = connect_sql_server();
682
683        let data = connection.handle()
684            .query::<ValueRow>("SELECT 'foo', cast('bar' AS NVARCHAR), cast('baz' AS TEXT), cast('quix' AS NTEXT);")
685            .expect("failed to run query")
686            .collect::<Result<Vec<_>, _>>()
687            .expect("fetch data");
688
689        assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, "foo"));
690        assert_matches!(data[0][1], Some(Value::String(ref string)) => assert_eq!(string, "bar"));
691        assert_matches!(data[0][2], Some(Value::String(ref string)) => assert_eq!(string, "baz"));
692        assert_matches!(data[0][3], Some(Value::String(ref string)) => assert_eq!(string, "quix"));
693    }
694
695    #[cfg(feature = "test-sql-server")]
696    #[test]
697    fn test_sql_server_types_string_empty() {
698        let mut connection = connect_sql_server();
699
700        let data = connection
701            .handle()
702            .query::<ValueRow>(
703                "SELECT '', cast('' AS NVARCHAR), cast('' AS TEXT), cast('' AS NTEXT);",
704            )
705            .expect("failed to run query")
706            .collect::<Result<Vec<_>, _>>()
707            .expect("fetch data");
708
709        assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, ""));
710        assert_matches!(data[0][1], Some(Value::String(ref string)) => assert_eq!(string, ""));
711        assert_matches!(data[0][2], Some(Value::String(ref string)) => assert_eq!(string, ""));
712        assert_matches!(data[0][3], Some(Value::String(ref string)) => assert_eq!(string, ""));
713    }
714
715    #[cfg(feature = "test-monetdb")]
716    #[test]
717    fn test_moentdb_string_empty() {
718        let mut monetdb = crate::tests::connect_monetdb();
719
720        let data = monetdb
721            .handle()
722            .query::<ValueRow>("SELECT ''")
723            .expect("failed to run query")
724            .collect::<Result<Vec<_>, _>>()
725            .expect("fetch data");
726
727        assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, ""));
728    }
729
730    #[cfg(feature = "test-monetdb")]
731    #[test]
732    fn test_moentdb_json() {
733        let mut monetdb = crate::tests::connect_monetdb();
734
735        let data = monetdb
736            .handle()
737            .query::<ValueRow>("SELECT CAST('[\"foo\"]' AS JSON)")
738            .expect("failed to run query")
739            .collect::<Result<Vec<_>, _>>()
740            .expect("fetch data");
741
742        #[cfg(feature = "serde_json")]
743        {
744            assert_matches!(data[0][0], Some(Value::Json(serde_json::Value::Array(ref arr))) => assert_eq!(arr, &[serde_json::Value::String("foo".to_owned())]));
745        }
746        #[cfg(not(feature = "serde_json"))]
747        {
748            assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, "[\"foo\"]"));
749        }
750    }
751
752    #[cfg(feature = "test-hive")]
753    #[test]
754    fn test_hive_types_float() {
755        let mut hive = connect_hive();
756
757        let data = hive
758            .handle()
759            .query::<ValueRow>("SELECT cast(1.5 AS FLOAT), cast(2.5 AS DOUBLE);")
760            .expect("failed to run query")
761            .collect::<Result<Vec<_>, _>>()
762            .expect("fetch data");
763
764        assert_matches!(data[0][0], Some(Value::Float(number)) => assert!(number > 1.0 && number < 2.0));
765        assert_matches!(data[0][1], Some(Value::Double(number)) => assert!(number > 2.0 && number < 3.0));
766    }
767
768    #[cfg(all(feature = "test-monetdb", feature = "rust_decimal"))]
769    #[test]
770    fn test_moentdb_types_decimal() {
771        let mut monetdb = crate::tests::connect_monetdb();
772
773        let data = monetdb
774            .handle()
775            .query::<ValueRow>("SELECT 10.9231213232423424324")
776            .expect("failed to run query")
777            .collect::<Result<Vec<_>, _>>()
778            .expect("fetch data");
779
780        assert_matches!(data[0][0], Some(Value::Decimal(ref dec)) => assert_eq!(dec.to_string(), "10.9231213232423424324"));
781    }
782
783    #[cfg(all(feature = "test-sql-server", feature = "rust_decimal"))]
784    #[test]
785    fn test_sql_server_types_decimal() {
786        let mut connection = connect_sql_server();
787
788        let data = connection
789            .handle()
790            .query::<ValueRow>("SELECT 10.9231213232423424324")
791            .expect("failed to run query")
792            .collect::<Result<Vec<_>, _>>()
793            .expect("fetch data");
794
795        assert_matches!(data[0][0], Some(Value::Decimal(ref dec)) => assert_eq!(dec.to_string(), "10.9231213232423424324"));
796    }
797
798    #[cfg(feature = "test-hive")]
799    #[test]
800    fn test_hive_types_null() {
801        let mut hive = connect_hive();
802
803        let data = hive
804            .handle()
805            .query::<ValueRow>("SELECT cast(NULL AS FLOAT), cast(NULL AS DOUBLE);")
806            .expect("failed to run query")
807            .collect::<Result<Vec<_>, _>>()
808            .expect("fetch data");
809
810        assert!(data[0][0].is_none());
811        assert!(data[0][1].is_none());
812    }
813
814    #[cfg(feature = "test-sql-server")]
815    #[test]
816    fn test_sql_server_tables() {
817        let mut connection = connect_sql_server();
818
819        let data = connection
820            .handle()
821            .tables::<ValueRow>("master", Some("sys"), None, Some("view"))
822            .expect("failed to run query")
823            .collect::<Result<Vec<_>, _>>()
824            .expect("fetch data");
825
826        assert!(data.len() > 0);
827    }
828
829    #[cfg(feature = "chrono")]
830    #[cfg(feature = "test-sql-server")]
831    #[test]
832    fn test_sql_server_date() {
833        let mut connection = connect_sql_server();
834
835        let data = connection
836            .handle()
837            .query::<ValueRow>("SELECT cast('2018-08-24' AS DATE) AS date")
838            .expect("failed to run query")
839            .collect::<Result<Vec<_>, _>>()
840            .expect("fetch data");
841
842        assert_matches!(&data[0][0], Some(date @ Value::Date(_)) => assert_eq!(&date.to_naive_date().unwrap().to_string(), "2018-08-24"));
843    }
844
845    #[cfg(feature = "chrono")]
846    #[cfg(feature = "test-sql-server")]
847    #[test]
848    fn test_sql_server_time() {
849        let mut connection = connect_sql_server();
850
851        let data = connection
852            .handle()
853            .query::<ValueRow>("SELECT cast('10:22:33.7654321' AS TIME) AS date")
854            .expect("failed to run query")
855            .collect::<Result<Vec<_>, _>>()
856            .expect("fetch data");
857
858        assert_matches!(&data[0][0], Some(time @ Value::Time(_)) => assert_eq!(&time.to_naive_time().unwrap().to_string(), "10:22:33.765432100"));
859    }
860
861    #[cfg(feature = "test-sql-server")]
862    #[test]
863    fn test_sql_server_affected_rows_query() {
864        let mut connection = connect_sql_server();
865
866        let mut db = connection.handle();
867
868        let data = db
869            .query::<ValueRow>("SELECT 1 UNION SELECT 2")
870            .expect("failed to run query");
871
872        assert!(data.affected_rows().unwrap().is_none());
873        data.close().ok();
874
875        let data = db
876            .query::<ValueRow>(
877                "SELECT foo INTO #bar FROM (SELECT 1 as foo UNION SELECT 2 as foo) a",
878            )
879            .expect("failed to run insert query");
880
881        assert_eq!(data.affected_rows().unwrap().unwrap(), 2);
882    }
883
884    #[cfg(feature = "test-sql-server")]
885    #[test]
886    fn test_sql_server_affected_rows_prepared() {
887        let mut connection = connect_sql_server();
888
889        let mut db = connection.handle();
890
891        let data = db
892            .query::<ValueRow>("SELECT 1 UNION SELECT 2")
893            .expect("failed to run query");
894
895        assert!(data.affected_rows().unwrap().is_none());
896        data.close().ok();
897
898        let statement = db
899            .prepare("SELECT foo INTO #bar FROM (SELECT 1 as foo UNION SELECT 2 as foo) a")
900            .expect("prepare statement");
901
902        let _data = db
903            .execute::<ValueRow>(statement)
904            .expect("failed to run insert query");
905
906        //TODO: this returns None since Prepared, NoResult has not affected_row_count method
907        // assert_eq!(data.affected_rows().unwrap().unwrap(), 2);
908    }
909
910    #[cfg(feature = "test-sql-server")]
911    #[test]
912    fn test_sql_server_query_with_parameters() {
913        let mut connection = connect_sql_server();
914
915        let val = 42;
916
917        let value: Value = connection
918            .handle()
919            .query_with_parameters("SELECT ? AS val;", |q| q.bind(&val))
920            .expect("failed to run query")
921            .single()
922            .expect("fetch data");
923
924        assert_eq!(value.to_i32().unwrap(), 42);
925    }
926
927    #[cfg(feature = "test-sql-server")]
928    #[test]
929    fn test_sql_server_query_with_many_parameters() {
930        let mut connection = connect_sql_server();
931
932        let val = [42, 24, 32, 666];
933
934        let data: Vec<ValueRow> = connection
935            .handle()
936            .query_with_parameters("SELECT ?, ?, ?, ? AS val;", |q| {
937                val.iter().fold(Ok(q), |q, v| q.and_then(|q| q.bind(v)))
938            })
939            .expect("failed to run query")
940            .collect::<Result<_, _>>()
941            .expect("fetch data");
942
943        assert_matches!(data[0][0], Some(Value::Integer(ref number)) => assert_eq!(*number, 42));
944        assert_matches!(data[0][1], Some(Value::Integer(ref number)) => assert_eq!(*number, 24));
945        assert_matches!(data[0][2], Some(Value::Integer(ref number)) => assert_eq!(*number, 32));
946        assert_matches!(data[0][3], Some(Value::Integer(ref number)) => assert_eq!(*number, 666));
947    }
948
949    #[cfg(feature = "test-sql-server")]
950    #[test]
951    fn test_sql_server_query_with_many_parameters_prepared() {
952        let mut connection = connect_sql_server();
953
954        let val = [42, 24, 32, 666];
955
956        let mut handle = connection.handle();
957
958        let statement = handle
959            .prepare("SELECT ?, ?, ?, ? AS val;")
960            .expect("prepare statement");
961
962        let data: Vec<ValueRow> = handle
963            .execute_with_parameters(statement, |q| {
964                val.iter().fold(Ok(q), |q, v| q.and_then(|q| q.bind(v)))
965            })
966            .expect("failed to run query")
967            .collect::<Result<_, _>>()
968            .expect("fetch data");
969
970        assert_matches!(data[0][0], Some(Value::Integer(ref number)) => assert_eq!(*number, 42));
971        assert_matches!(data[0][1], Some(Value::Integer(ref number)) => assert_eq!(*number, 24));
972        assert_matches!(data[0][2], Some(Value::Integer(ref number)) => assert_eq!(*number, 32));
973        assert_matches!(data[0][3], Some(Value::Integer(ref number)) => assert_eq!(*number, 666));
974    }
975
976    #[cfg(feature = "test-sql-server")]
977    #[test]
978    fn test_sql_server_prepared_columns() {
979        let mut connection = connect_sql_server();
980
981        let statement = connection
982            .handle()
983            .prepare("SELECT ?, ?, ?, ? AS val;")
984            .expect("prepare statement");
985
986        assert_eq!(statement.columns().unwrap(), 4);
987    }
988
989    #[cfg(feature = "test-sql-server")]
990    #[test]
991    fn test_sql_server_prepared_schema() {
992        let mut connection = connect_sql_server();
993
994        let statement = connection
995            .handle()
996            .prepare("SELECT ?, CAST(? as INTEGER) as foo, ?, ? AS val;")
997            .expect("prepare statement");
998
999        let schema = statement.schema().unwrap();
1000        assert_eq!(schema.len(), 4);
1001        assert_eq!(schema[1].nullable, true);
1002        assert_eq!(schema[1].datum_type, DatumType::Integer);
1003    }
1004
1005    #[cfg(feature = "test-hive")]
1006    #[test]
1007    fn test_hive_empty_data_set() {
1008        let mut hive = connect_hive();
1009
1010        let data = hive
1011            .handle()
1012            .query::<ValueRow>("USE default;")
1013            .expect("failed to run query")
1014            .collect::<Result<Vec<_>, _>>()
1015            .expect("fetch data");
1016
1017        assert!(data.is_empty());
1018    }
1019
1020    #[cfg(feature = "test-hive")]
1021    #[test]
1022    fn test_hive_long_string_fetch_utf_8() {
1023        let mut hive = connect_hive();
1024
1025        let data = hive
1026            .handle()
1027            .query::<ValueRow>(&format!("SELECT '{}'", LONG_STRING))
1028            .expect("failed to run query")
1029            .collect::<Result<Vec<_>, _>>()
1030            .expect("fetch data");
1031
1032        assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, LONG_STRING));
1033    }
1034
1035    #[cfg(feature = "test-monetdb")]
1036    #[test]
1037    fn test_moentdb_long_string_fetch_utf_8() {
1038        let mut monetdb = crate::tests::connect_monetdb();
1039
1040        let data = monetdb
1041            .handle()
1042            .query::<ValueRow>(&format!("SELECT '{}'", LONG_STRING))
1043            .expect("failed to run query")
1044            .collect::<Result<Vec<_>, _>>()
1045            .expect("fetch data");
1046
1047        assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, LONG_STRING));
1048    }
1049
1050    #[cfg(feature = "test-sql-server")]
1051    #[test]
1052    fn test_sql_server_long_string_fetch_utf_16_bind() {
1053        let mut connection = connect_sql_server_with_settings(Settings {
1054            utf_16_strings: true,
1055        });
1056
1057        let utf_16_string = LONG_STRING.encode_utf16().collect::<Vec<u16>>();
1058
1059        let mut handle = connection.handle();
1060
1061        let statement = handle
1062            .prepare("SELECT ? AS val;")
1063            .expect("prepare statement");
1064
1065        let data: Vec<ValueRow> = handle
1066            .execute_with_parameters(statement, |q| q.bind(&utf_16_string))
1067            .expect("failed to run query")
1068            .collect::<Result<Vec<_>, _>>()
1069            .expect("fetch data");
1070
1071        assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, LONG_STRING));
1072    }
1073
1074    #[cfg(feature = "test-sql-server")]
1075    #[test]
1076    fn test_sql_server_long_string_fetch_utf_16_bind_string_utf_16() {
1077        let mut connection = connect_sql_server_with_settings(Settings {
1078            utf_16_strings: true,
1079        });
1080
1081        let utf_16_string = StringUtf16::from(LONG_STRING);
1082
1083        let mut handle = connection.handle();
1084
1085        let statement = handle
1086            .prepare("SELECT ? AS val;")
1087            .expect("prepare statement");
1088
1089        let data: Vec<ValueRow> = handle
1090            .execute_with_parameters(statement, |q| q.bind(&utf_16_string))
1091            .expect("failed to run query")
1092            .collect::<Result<Vec<_>, _>>()
1093            .expect("fetch data");
1094
1095        assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, LONG_STRING));
1096    }
1097
1098    #[cfg(feature = "test-hive")]
1099    #[test]
1100    fn test_hive_long_string_fetch_utf_16() {
1101        let mut hive = connect_hive_with_settings(Settings {
1102            utf_16_strings: true,
1103        });
1104
1105        let data = hive
1106            .handle()
1107            .query::<ValueRow>(&format!("SELECT '{}'", LONG_STRING))
1108            .expect("failed to run query")
1109            .collect::<Result<Vec<_>, _>>()
1110            .expect("fetch data");
1111
1112        assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, LONG_STRING));
1113    }
1114
1115    #[cfg(feature = "test-monetdb")]
1116    #[test]
1117    fn test_moentdb_long_string_fetch_utf_16() {
1118        let mut monetdb = connect_monetdb_with_settings(Settings {
1119            utf_16_strings: true,
1120        });
1121
1122        let data = monetdb
1123            .handle()
1124            .query::<ValueRow>(&format!("SELECT '{}'", LONG_STRING))
1125            .expect("failed to run query")
1126            .collect::<Result<Vec<_>, _>>()
1127            .expect("fetch data");
1128
1129        assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, LONG_STRING));
1130    }
1131
1132    #[test]
1133    fn test_split_queries() {
1134        let queries = split_queries(
1135            r#"-- Foo
1136---
1137CREATE DATABASE IF NOT EXISTS daily_reports;
1138USE daily_reports;
1139
1140SELECT *;"#,
1141        )
1142        .collect::<Result<Vec<_>, _>>()
1143        .expect("failed to parse");
1144        assert_eq!(
1145            queries,
1146            [
1147                "CREATE DATABASE IF NOT EXISTS daily_reports;",
1148                "USE daily_reports;",
1149                "SELECT *;"
1150            ]
1151        );
1152    }
1153
1154    #[test]
1155    fn test_split_queries_end_white() {
1156        let queries = split_queries(
1157            r#"USE daily_reports;
1158SELECT *;
1159
1160"#,
1161        )
1162        .collect::<Result<Vec<_>, _>>()
1163        .expect("failed to parse");
1164        assert_eq!(queries, ["USE daily_reports;", "SELECT *;"]);
1165    }
1166
1167    #[test]
1168    fn test_split_queries_simple() {
1169        let queries = split_queries("SELECT 42;\nSELECT 24;\nSELECT 'foo';")
1170            .collect::<Result<Vec<_>, _>>()
1171            .expect("failed to parse");
1172        assert_eq!(queries, ["SELECT 42;", "SELECT 24;", "SELECT 'foo';"]);
1173    }
1174
1175    #[test]
1176    fn test_split_queries_semicolon() {
1177        let queries = split_queries("SELECT 'foo; bar';\nSELECT 1;")
1178            .collect::<Result<Vec<_>, _>>()
1179            .expect("failed to parse");
1180        assert_eq!(queries, [r#"SELECT 'foo; bar';"#, "SELECT 1;"]);
1181    }
1182
1183    #[test]
1184    fn test_split_queries_semicolon2() {
1185        let queries = split_queries(r#"foo "bar" baz "quix; but" foo "bar" baz "quix; but" fsad; foo "bar" baz "quix; but" foo "bar" baz "quix; but" fsad; select foo; foo "bar" baz 'quix; but' foo "bar" baz "quix; but" fsad; foo "bar" baz "quix; but" foo "bar" baz "quix; but" fsad; select foo;"#).collect::<Result<Vec<_>, _>>().expect("failed to parse");
1186        assert_eq!(
1187            queries,
1188            [
1189                r#"foo "bar" baz "quix; but" foo "bar" baz "quix; but" fsad;"#,
1190                r#"foo "bar" baz "quix; but" foo "bar" baz "quix; but" fsad;"#,
1191                r#"select foo;"#,
1192                r#"foo "bar" baz 'quix; but' foo "bar" baz "quix; but" fsad;"#,
1193                r#"foo "bar" baz "quix; but" foo "bar" baz "quix; but" fsad;"#,
1194                r#"select foo;"#,
1195            ]
1196        );
1197    }
1198
1199    #[test]
1200    fn test_split_queries_escaped_quote() {
1201        let queries = split_queries("SELECT 'foo; b\\'ar';\nSELECT 1;")
1202            .collect::<Result<Vec<_>, _>>()
1203            .expect("failed to parse");
1204        assert_eq!(queries, [r#"SELECT 'foo; b\'ar';"#, "SELECT 1;"]);
1205    }
1206
1207    #[test]
1208    fn test_split_queries_escaped_quote2() {
1209        let queries = split_queries("SELECT 'foo; b\\'ar';\nSELECT 'foo\\'bar';")
1210            .collect::<Result<Vec<_>, _>>()
1211            .expect("failed to parse");
1212        assert_eq!(
1213            queries,
1214            [r#"SELECT 'foo; b\'ar';"#, r#"SELECT 'foo\'bar';"#]
1215        );
1216    }
1217
1218    #[test]
1219    fn test_split_queries_escaped_doublequote() {
1220        let queries = split_queries(r#"SELECT "foo; b\"ar";SELECT "foo\"bar";"#)
1221            .collect::<Result<Vec<_>, _>>()
1222            .expect("failed to parse");
1223        assert_eq!(
1224            queries,
1225            [r#"SELECT "foo; b\"ar";"#, r#"SELECT "foo\"bar";"#]
1226        );
1227    }
1228
1229    #[test]
1230    fn test_split_queries_comments() {
1231        let queries =
1232            split_queries("SELECT 1;\n-- SELECT x;\n---- SELECT x;\nSELECT 2;\nSELECT 3;")
1233                .collect::<Result<Vec<_>, _>>()
1234                .expect("failed to parse");
1235        assert_eq!(queries, ["SELECT 1;", "SELECT 2;", "SELECT 3;"]);
1236    }
1237
1238    #[test]
1239    fn test_split_queries_comments2() {
1240        let queries = split_queries("-- TODO: add last_search_or_brochure_logentry_id\n-- TODO: DISTRIBUTE BY analytics_record_id SORT BY analytics_record_id ASC;\n-- TODO: check previous day for landing logentry detail\nSELECT '1' LEFT JOIN source_wcc.domain d ON regexp_extract(d.domain, '.*\\\\.([^\\.]+)$', 1) = c.domain AND d.snapshot_day = c.index;").collect::<Result<Vec<_>, _>>().expect("failed to parse");
1241        assert_eq!(queries, [r#"SELECT '1' LEFT JOIN source_wcc.domain d ON regexp_extract(d.domain, '.*\\.([^\.]+)$', 1) = c.domain AND d.snapshot_day = c.index;"#]);
1242    }
1243
1244    #[test]
1245    fn test_split_queries_control() {
1246        let queries = split_queries(
1247            "!outputformat vertical\nSELECT 1;\n-- SELECT x;\n---- SELECT x;\nSELECT 2;\nSELECT 3;",
1248        )
1249        .collect::<Result<Vec<_>, _>>()
1250        .expect("failed to parse");
1251        assert_eq!(queries, ["SELECT 1;", "SELECT 2;", "SELECT 3;"]);
1252    }
1253
1254    #[test]
1255    fn test_split_queries_white() {
1256        let queries = split_queries(" \n  SELECT 1;\n  \nSELECT 2;\n \nSELECT 3;\n\n ")
1257            .collect::<Result<Vec<_>, _>>()
1258            .expect("failed to parse");
1259        assert_eq!(queries, ["SELECT 1;", "SELECT 2;", "SELECT 3;"]);
1260    }
1261
1262    #[test]
1263    fn test_split_queries_white2() {
1264        let queries = split_queries("SELECT 1; \t \nSELECT 2; \n \nSELECT 3; ")
1265            .collect::<Result<Vec<_>, _>>()
1266            .expect("failed to parse");
1267        assert_eq!(queries, ["SELECT 1;", "SELECT 2;", "SELECT 3;"]);
1268    }
1269
1270    #[test]
1271    fn test_split_queries_white_comment() {
1272        let queries = split_queries("SELECT 1; \t \nSELECT 2; -- foo bar\n \nSELECT 3; ")
1273            .collect::<Result<Vec<_>, _>>()
1274            .expect("failed to parse");
1275        assert_eq!(queries, ["SELECT 1;", "SELECT 2;", "SELECT 3;"]);
1276    }
1277
1278    #[cfg(feature = "test-sql-server")]
1279    #[test]
1280    fn test_sql_server_debug() {
1281        let mut connection = connect_sql_server_with_settings(Settings {
1282            utf_16_strings: true,
1283        });
1284
1285        assert_eq!(
1286            format!("{:?}", connection),
1287            "Connection { settings: Settings { utf_16_strings: true } }"
1288        );
1289
1290        let utf_16_string = LONG_STRING.encode_utf16().collect::<Vec<u16>>();
1291
1292        let mut handle = connection.handle();
1293        assert_eq!(
1294            format!("{:?}", handle),
1295            "Handle { connection: Connection { settings: Settings { utf_16_strings: true } }, configuration: DefaultConfiguration }"
1296        );
1297
1298        let statement = handle
1299            .prepare("SELECT ? AS foo, ? AS bar, ? AS baz;")
1300            .expect("prepare statement");
1301
1302        assert_eq!(format!("{:?}", statement), "PreparedStatement { odbc_schema: [ColumnDescriptor { name: \"foo\", data_type: SQL_VARCHAR, column_size: Some(4000), decimal_digits: None, nullable: Some(true) }, ColumnDescriptor { name: \"bar\", data_type: SQL_VARCHAR, column_size: Some(4000), decimal_digits: None, nullable: Some(true) }, ColumnDescriptor { name: \"baz\", data_type: SQL_VARCHAR, column_size: Some(4000), decimal_digits: None, nullable: Some(true) }] }");
1303
1304        let result_set = handle
1305            .execute_with_parameters::<ValueRow, _>(statement, |q| {
1306                let q = q.bind(&utf_16_string)?;
1307                assert_eq!(format!("{:?}", q), "Binder { index: 1 }");
1308                q.bind(&12)?.bind(&true)
1309            })
1310            .expect("failed to run query");
1311
1312        assert_eq!(format!("{:?}", result_set), "ResultSet { schema: [ColumnType { datum_type: String, odbc_type: SQL_EXT_WVARCHAR, nullable: true, name: \"foo\" }, ColumnType { datum_type: Integer, odbc_type: SQL_INTEGER, nullable: true, name: \"bar\" }, ColumnType { datum_type: Bit, odbc_type: SQL_EXT_BIT, nullable: true, name: \"baz\" }], columns: 3, settings: Settings { utf_16_strings: true }, configuration: DefaultConfiguration }");
1313    }
1314}