Skip to content
This repository was archived by the owner on May 3, 2021. It is now read-only.

First draft of a dynamic select clause feature in diesel-dynamic-schema #10

Draft
wants to merge 17 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -3,9 +3,13 @@ name = "diesel-dynamic-schema"
version = "1.0.0"
authors = ["Sean Griffin <[email protected]>"]
license = "MIT OR Apache-2.0"
autotests = false

[dependencies]
diesel = "1.0.0"
diesel = { version = "1.4.2", default-features = false }

[dev-dependencies]
dotenv = "0.14"

[[example]]
name = "querying_basic_schemas"
@@ -26,4 +30,14 @@ required-features = ["diesel/sqlite"]
name = "integration_tests"
path = "tests/lib.rs"
harness = true
required-features = ["diesel/sqlite"]
#required-features = ["diesel/sqlite"]

[features]
postgres = ["diesel/postgres"]
sqlite = ["diesel/sqlite"]
mysql = ["diesel/mysql"]


[patch.crates-io]
diesel = {git = "https://github.com/GiGainfosystems/diesel", rev = "0744b7e6e05582bf8fca21c0a5fbba08555abd94"}
diesel_derives = {git = "https://github.com/GiGainfosystems/diesel", rev = "0744b7e6e05582bf8fca21c0a5fbba08555abd94"}
10 changes: 10 additions & 0 deletions src/column.rs
Original file line number Diff line number Diff line change
@@ -22,6 +22,16 @@ impl<T, U, ST> Column<T, U, ST> {
_sql_type: PhantomData,
}
}

/// Gets a reference to the table of the column.
pub fn table(&self) -> &T {
&self.table
}

/// Gets the name of the column, as provided on creation.
pub fn name(&self) -> &U {
&self.name
}
}

impl<T, U, ST> QueryId for Column<T, U, ST> {
94 changes: 94 additions & 0 deletions src/dynamic_select.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use diesel::backend::Backend;
use diesel::query_builder::{
AstPass, IntoBoxedSelectClause, QueryFragment, QueryId, SelectClauseExpression,
SelectClauseQueryFragment,
};
use diesel::{
sql_types::HasSqlType, AppearsOnTable, Expression, QueryResult, SelectableExpression,
};
use std::marker::PhantomData;

#[allow(missing_debug_implementations)]
pub struct DynamicSelectClause<'a, DB, QS> {
selects: Vec<Box<dyn QueryFragment<DB> + 'a>>,
p: PhantomData<QS>,
}

impl<'a, DB, QS> QueryId for DynamicSelectClause<'a, DB, QS> {
const HAS_STATIC_QUERY_ID: bool = false;
type QueryId = ();
}

impl<'a, DB, QS> DynamicSelectClause<'a, DB, QS> {
pub fn new() -> Self {
Self {
selects: Vec::new(),
p: PhantomData,
}
}
pub fn add_field<F>(&mut self, field: F)
where
F: QueryFragment<DB> + SelectableExpression<QS> + 'a,
DB: Backend,
{
self.selects.push(Box::new(field))
}
}

impl<'a, QS, DB> Expression for DynamicSelectClause<'a, DB, QS>
where
DB: Backend + HasSqlType<crate::dynamic_value::Any>,
{
type SqlType = crate::dynamic_value::Any;
}

impl<'a, DB, QS> AppearsOnTable<QS> for DynamicSelectClause<'a, DB, QS> where Self: Expression {}

impl<'a, DB, QS> SelectableExpression<QS> for DynamicSelectClause<'a, DB, QS> where
Self: AppearsOnTable<QS>
{
}

impl<'a, QS, DB> SelectClauseExpression<QS> for DynamicSelectClause<'a, DB, QS> {
type SelectClauseSqlType = crate::dynamic_value::Any;
}

impl<'a, QS, DB> SelectClauseQueryFragment<QS, DB> for DynamicSelectClause<'a, QS, DB>
where
DB: Backend,
Self: QueryFragment<DB>,
{
fn walk_ast(&self, _source: &QS, pass: AstPass<DB>) -> QueryResult<()> {
<Self as QueryFragment<DB>>::walk_ast(self, pass)
}
}

impl<'a, DB, QS> QueryFragment<DB> for DynamicSelectClause<'a, DB, QS>
where
DB: Backend,
{
fn walk_ast(&self, mut pass: AstPass<DB>) -> QueryResult<()> {
let mut first = true;
for s in &self.selects {
if first {
first = false;
} else {
pass.push_sql(", ");
}
s.walk_ast(pass.reborrow())?;
}
Ok(())
}
}

impl<'a, DB, QS> IntoBoxedSelectClause<'a, DB, QS> for DynamicSelectClause<'a, DB, QS>
where
Self: 'a + QueryFragment<DB> + SelectClauseExpression<QS>,
DB: Backend,
{
type SqlType = <Self as SelectClauseExpression<QS>>::SelectClauseSqlType;

fn into_boxed(self, _source: &QS) -> Box<dyn QueryFragment<DB> + 'a> {
Box::new(self)
}
}
228 changes: 228 additions & 0 deletions src/dynamic_value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
use diesel::deserialize::{self, FromSql, FromSqlRow, Queryable, QueryableByName};
use diesel::row::{NamedRow, Row};
use diesel::{backend::Backend, QueryId, SqlType};
use std::iter::FromIterator;
use std::ops::Index;

#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)]
#[postgres(oid = "0", array_oid = "0")]
#[sqlite_type = "Integer"]
pub struct Any;

#[cfg(feature = "mysql")]
impl diesel::sql_types::HasSqlType<Any> for diesel::mysql::Mysql {
fn metadata(_lookup: &Self::MetadataLookup) -> Self::TypeMetadata {
None
}
}

#[derive(Debug, Clone)]
pub struct DynamicRow<I> {
values: Vec<I>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct NamedField<I> {
pub name: String,
pub value: I,
}

impl<I> FromIterator<I> for DynamicRow<I> {
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = I>,
{
DynamicRow {
values: iter.into_iter().collect(),
}
}
}

impl<I> DynamicRow<I> {
pub fn get(&self, index: usize) -> Option<&I> {
self.values.get(index)
}
}

impl<I> DynamicRow<NamedField<I>> {
pub fn get_by_name<S: AsRef<str>>(&self, name: S) -> Option<&I> {
self.values
.iter()
.find(|f| f.name == name.as_ref())
.map(|f| &f.value)
}
}

#[cfg(feature = "postgres")]
impl<I> FromSqlRow<Any, diesel::pg::Pg> for DynamicRow<I>
where
I: FromSql<Any, diesel::pg::Pg>,
{
const FIELDS_NEEDED: usize = 1;

fn build_from_row<T: Row<diesel::pg::Pg>>(row: &mut T) -> deserialize::Result<Self> {
(0..row.column_count())
.map(|_| I::from_sql(row.take()))
.collect::<deserialize::Result<_>>()
}
}

#[cfg(feature = "sqlite")]
impl<I> FromSqlRow<Any, diesel::sqlite::Sqlite> for DynamicRow<I>
where
I: FromSql<Any, diesel::sqlite::Sqlite>,
{
const FIELDS_NEEDED: usize = 1;

fn build_from_row<T: Row<diesel::sqlite::Sqlite>>(row: &mut T) -> deserialize::Result<Self> {
(0..row.column_count())
.map(|_| I::from_sql(row.take()))
.collect::<deserialize::Result<_>>()
}
}

#[cfg(feature = "mysql")]
impl<I> FromSqlRow<Any, diesel::mysql::Mysql> for DynamicRow<I>
where
I: FromSql<Any, diesel::mysql::Mysql>,
{
const FIELDS_NEEDED: usize = 1;

fn build_from_row<T: Row<diesel::mysql::Mysql>>(row: &mut T) -> deserialize::Result<Self> {
(0..row.column_count())
.map(|_| I::from_sql(row.take()))
.collect::<deserialize::Result<_>>()
}
}

impl<I, DB> Queryable<Any, DB> for DynamicRow<I>
where
DB: Backend,
Self: FromSqlRow<Any, DB>,
{
type Row = DynamicRow<I>;

fn build(row: Self::Row) -> Self {
row
}
}

impl<I, DB> QueryableByName<DB> for DynamicRow<NamedField<I>>
where
DB: Backend,
I: FromSql<Any, DB>,
{
fn build<R: NamedRow<DB>>(row: &R) -> deserialize::Result<Self> {
row.field_names()
.into_iter()
.map(|name| {
Ok(NamedField {
name: name.to_owned(),
value: row.get::<Any, I>(name)?,
})
})
.collect()
}
}

#[cfg(feature = "postgres")]
impl<I> QueryableByName<diesel::pg::Pg> for DynamicRow<I>
where
I: FromSql<Any, diesel::pg::Pg>,
{
fn build<R: NamedRow<diesel::pg::Pg>>(row: &R) -> deserialize::Result<Self> {
row.field_names()
.into_iter()
.map(|name| row.get::<Any, I>(name))
.collect()
}
}

#[cfg(feature = "sqlite")]
impl<I> QueryableByName<diesel::sqlite::Sqlite> for DynamicRow<I>
where
I: FromSql<Any, diesel::sqlite::Sqlite>,
{
fn build<R: NamedRow<diesel::sqlite::Sqlite>>(row: &R) -> deserialize::Result<Self> {
row.field_names()
.into_iter()
.map(|name| row.get::<Any, I>(name))
.collect()
}
}

#[cfg(feature = "mysql")]
impl<I> QueryableByName<diesel::mysql::Mysql> for DynamicRow<I>
where
I: FromSql<Any, diesel::mysql::Mysql>,
{
fn build<R: NamedRow<diesel::mysql::Mysql>>(row: &R) -> deserialize::Result<Self> {
row.field_names()
.into_iter()
.map(|name| row.get::<Any, I>(name))
.collect()
}
}

impl<I, DB> FromSqlRow<Any, DB> for DynamicRow<NamedField<I>>
where
DB: Backend,
I: FromSql<Any, DB>,
{
const FIELDS_NEEDED: usize = 1;

fn build_from_row<T: Row<DB>>(row: &mut T) -> deserialize::Result<Self> {
Ok(DynamicRow {
values: (0..row.column_count())
.map(|_| {
let name = row
.column_name()
.ok_or_else(|| "Request name for an unnamed column")?
.into();
Ok(NamedField {
name,
value: I::from_sql(row.take())?,
})
})
.collect::<deserialize::Result<_>>()?,
})
}
}

impl<I> Index<usize> for DynamicRow<I> {
type Output = I;

fn index(&self, index: usize) -> &Self::Output {
&self.values[index]
}
}

impl<'a, I> Index<&'a str> for DynamicRow<NamedField<I>> {
type Output = I;

fn index(&self, field_name: &'a str) -> &Self::Output {
self.values
.iter()
.find(|f| f.name == field_name)
.map(|f| &f.value)
.expect("Field not found")
}
}

impl<V> IntoIterator for DynamicRow<V> {
type Item = V;
type IntoIter = <Vec<V> as IntoIterator>::IntoIter;

fn into_iter(self) -> Self::IntoIter {
self.values.into_iter()
}
}

impl<'a, V> IntoIterator for &'a DynamicRow<V> {
type Item = &'a V;
type IntoIter = <&'a Vec<V> as IntoIterator>::IntoIter;

fn into_iter(self) -> Self::IntoIter {
(&self.values).into_iter()
}
}
30 changes: 15 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! # Diesel dynamic schema
//!
//! Diesel is an ORM and query builder designed to reduce
//! Diesel is an ORM and query builder designed to reduce
//! the boilerplate for database interactions.
//!
//! If this is your first time reading about Diesel, then
@@ -11,21 +11,21 @@
//! [many other long form guides]: https://diesel.rs/guides
//!
//! Diesel is built to provide strong compile time guarantees that your
//! queries are valid. To do this, it needs to represent your schema
//! at compile time. However, there are some times where you don't
//! queries are valid. To do this, it needs to represent your schema
//! at compile time. However, there are some times where you don't
//! actually know the schema you're interacting with until runtime.
//!
//!
//! This crate provides tools to work with those cases, while still being
//! able to use Diesel's query builder. Keep in mind that many compile time
//! guarantees are lost. We cannot verify that the tables/columns you ask
//! able to use Diesel's query builder. Keep in mind that many compile time
//! guarantees are lost. We cannot verify that the tables/columns you ask
//! for actually exist, or that the types you state are correct.
//!
//!
//! # Getting Started
//!
//!
//! The `table` function is used to create a new Diesel table.
//! Note that you must always provide an explicit select clause
//! Note that you must always provide an explicit select clause
//! when using this crate.
//!
//!
//! ```rust
//! # extern crate diesel;
//! # extern crate diesel_dynamic_schema;
@@ -44,7 +44,7 @@
//! let users = table("users");
//! let id = users.column::<Integer, _>("id");
//! let name = users.column::<Text, _>("name");
//!
//!
//! // Now you can use typical Diesel syntax; see the Diesel docs for more.
//! let results = users
//! .select((id, name))
@@ -59,7 +59,7 @@
//! println!("id:{} name:{}", x, y);
//! }
//! ```
//!
//!
//! See the `/examples` directory for runnable code examples.
//!
//! ## Getting help
@@ -69,14 +69,14 @@
//! [gitter.im/diesel-rs/diesel](https://gitter.im/diesel-rs/diesel)
// Built-in Lints
#![deny(
missing_docs
)]
#![warn(missing_docs)]

extern crate diesel;

mod column;
mod dummy_expression;
pub mod dynamic_select;
pub mod dynamic_value;
mod schema;
mod table;

5 changes: 5 additions & 0 deletions src/schema.rs
Original file line number Diff line number Diff line change
@@ -12,6 +12,11 @@ impl<T> Schema<T> {
Self { name }
}

/// Gets the name of the schema, as specified on creation.
pub fn name(&self) -> &T {
&self.name
}

/// Create a table with this schema.
pub fn table<U>(&self, name: U) -> Table<U, T>
where
7 changes: 6 additions & 1 deletion src/table.rs
Original file line number Diff line number Diff line change
@@ -21,14 +21,19 @@ impl<T, U> Table<T, U> {
Self { name, schema: None }
}

/// Gets the name of the column, as especified on creation.
pub fn name(&self) -> &T {
&self.name
}

pub(crate) fn with_schema(schema: U, name: T) -> Self {
Self {
name,
schema: Some(schema),
}
}

/// Create a column with this table.
/// Creates a column with this table.
pub fn column<ST, V>(&self, name: V) -> Column<Self, V, ST>
where
Self: Clone,
232 changes: 232 additions & 0 deletions tests/dynamic_values.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
use diesel::deserialize::*;
use diesel::prelude::*;
use diesel::sql_query;
use diesel::sql_types::*;
use diesel_dynamic_schema::dynamic_select::DynamicSelectClause;
use diesel_dynamic_schema::dynamic_value::*;

#[derive(PartialEq, Debug)]
enum MyDynamicValue {
String(String),
Integer(i32),
Null,
}

#[cfg(feature = "postgres")]
impl FromSql<Any, diesel::pg::Pg> for MyDynamicValue {
fn from_sql(value: Option<diesel::pg::PgValue>) -> Result<Self> {
use diesel::pg::Pg;
use std::num::NonZeroU32;

const VARCHAR_OID: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(1043) };
const TEXT_OID: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(25) };
const INTEGER_OID: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(23) };

if let Some(value) = value {
match value.get_oid() {
VARCHAR_OID | TEXT_OID => {
<String as FromSql<diesel::sql_types::Text, Pg>>::from_sql(Some(value))
.map(MyDynamicValue::String)
}
INTEGER_OID => {
<i32 as FromSql<diesel::sql_types::Integer, Pg>>::from_sql(Some(value))
.map(MyDynamicValue::Integer)
}
e => Err(format!("Unknown type: {}", e).into()),
}
} else {
Ok(MyDynamicValue::Null)
}
}
}

#[cfg(feature = "sqlite")]
impl FromSql<Any, diesel::sqlite::Sqlite> for MyDynamicValue {
fn from_sql(value: Option<&diesel::sqlite::SqliteValue>) -> Result<Self> {
use diesel::sqlite::{Sqlite, SqliteType};
if let Some(value) = value {
match value.value_type() {
Some(SqliteType::Text) => {
<String as FromSql<diesel::sql_types::Text, Sqlite>>::from_sql(Some(value))
.map(MyDynamicValue::String)
}
Some(SqliteType::Long) => {
<i32 as FromSql<diesel::sql_types::Integer, Sqlite>>::from_sql(Some(value))
.map(MyDynamicValue::Integer)
}
_ => Err("Unknown data type".into()),
}
} else {
Ok(MyDynamicValue::Null)
}
}
}

#[cfg(feature = "mysql")]
impl FromSql<Any, diesel::mysql::Mysql> for MyDynamicValue {
fn from_sql(value: Option<diesel::mysql::MysqlValue>) -> Result<Self> {
use diesel::mysql::{Mysql, MysqlType, MysqlTypeMetadata};
if let Some(value) = value {
match value.value_type() {
MysqlTypeMetadata {
data_type: MysqlType::String,
..
}
| MysqlTypeMetadata {
data_type: MysqlType::Blob,
..
} => <String as FromSql<diesel::sql_types::Text, Mysql>>::from_sql(Some(value))
.map(MyDynamicValue::String),
MysqlTypeMetadata {
data_type: MysqlType::Long,
is_unsigned: false,
} => <i32 as FromSql<diesel::sql_types::Integer, Mysql>>::from_sql(Some(value))
.map(MyDynamicValue::Integer),
e => Err(format!("Unknown data type: {:?}", e).into()),
}
} else {
Ok(MyDynamicValue::Null)
}
}
}

#[test]
fn dynamic_query() {
let connection = super::establish_connection();
super::create_user_table(&connection);
sql_query("INSERT INTO users (name) VALUES ('Sean'), ('Tess')")
.execute(&connection)
.unwrap();

let users = diesel_dynamic_schema::table("users");
let id = users.column::<Integer, _>("id");
let name = users.column::<Text, _>("name");
let hair_color = users.column::<Nullable<Text>, _>("hair_color");

let mut select = DynamicSelectClause::new();

select.add_field(id);
select.add_field(name);
select.add_field(hair_color);

let actual_data: Vec<DynamicRow<NamedField<MyDynamicValue>>> =
users.select(select).load(&connection).unwrap();

assert_eq!(
actual_data[0]["name"],
MyDynamicValue::String("Sean".into())
);
assert_eq!(
actual_data[0][1],
NamedField {
name: "name".into(),
value: MyDynamicValue::String("Sean".into())
}
);
assert_eq!(
actual_data[1]["name"],
MyDynamicValue::String("Tess".into())
);
assert_eq!(
actual_data[1][1],
NamedField {
name: "name".into(),
value: MyDynamicValue::String("Tess".into())
}
);
assert_eq!(actual_data[0]["hair_color"], MyDynamicValue::Null);
assert_eq!(
actual_data[0][2],
NamedField {
name: "hair_color".into(),
value: MyDynamicValue::Null
}
);
assert_eq!(actual_data[1]["hair_color"], MyDynamicValue::Null);
assert_eq!(
actual_data[1][2],
NamedField {
name: "hair_color".into(),
value: MyDynamicValue::Null
}
);

let mut select = DynamicSelectClause::new();

select.add_field(id);
select.add_field(name);
select.add_field(hair_color);

let actual_data: Vec<DynamicRow<MyDynamicValue>> =
users.select(select).load(&connection).unwrap();

assert_eq!(actual_data[0][1], MyDynamicValue::String("Sean".into()));
assert_eq!(actual_data[1][1], MyDynamicValue::String("Tess".into()));
assert_eq!(actual_data[0][2], MyDynamicValue::Null);
assert_eq!(actual_data[1][2], MyDynamicValue::Null);
}

#[test]
fn dynamic_query_2() {
let connection = super::establish_connection();
super::create_user_table(&connection);
sql_query("INSERT INTO users (name) VALUES ('Sean'), ('Tess')")
.execute(&connection)
.unwrap();

let actual_data: Vec<DynamicRow<NamedField<MyDynamicValue>>> =
sql_query("SELECT id, name, hair_color FROM users")
.load(&connection)
.unwrap();

dbg!(&actual_data);

assert_eq!(
actual_data[0]["name"],
MyDynamicValue::String("Sean".into())
);
assert_eq!(
actual_data[0][1],
NamedField {
name: "name".into(),
value: MyDynamicValue::String("Sean".into())
}
);
assert_eq!(
actual_data[1]["name"],
MyDynamicValue::String("Tess".into())
);
assert_eq!(
actual_data[1][1],
NamedField {
name: "name".into(),
value: MyDynamicValue::String("Tess".into())
}
);
assert_eq!(actual_data[0]["hair_color"], MyDynamicValue::Null);
assert_eq!(
actual_data[0][2],
NamedField {
name: "hair_color".into(),
value: MyDynamicValue::Null
}
);
assert_eq!(actual_data[1]["hair_color"], MyDynamicValue::Null);
assert_eq!(
actual_data[1][2],
NamedField {
name: "hair_color".into(),
value: MyDynamicValue::Null
}
);

let actual_data: Vec<DynamicRow<MyDynamicValue>> =
sql_query("SELECT id, name, hair_color FROM users")
.load(&connection)
.unwrap();

assert_eq!(actual_data[0][1], MyDynamicValue::String("Sean".into()));
assert_eq!(actual_data[1][1], MyDynamicValue::String("Tess".into()));
assert_eq!(actual_data[0][2], MyDynamicValue::Null);
assert_eq!(actual_data[1][2], MyDynamicValue::Null);
}
79 changes: 63 additions & 16 deletions tests/lib.rs
Original file line number Diff line number Diff line change
@@ -2,16 +2,69 @@ extern crate diesel;
extern crate diesel_dynamic_schema;

use diesel::sql_types::*;
use diesel::sqlite::Sqlite;
use diesel::*;
use diesel_dynamic_schema::{schema, table};
use diesel_dynamic_schema::table;

mod dynamic_values;

#[cfg(feature = "postgres")]
fn create_user_table(conn: &PgConnection) {
sql_query("CREATE TABLE users (id Serial PRIMARY KEY, name TEXT NOT NULL DEFAULT '', hair_color TEXT)")
.execute(conn)
.unwrap();
}

#[cfg(feature = "sqlite")]
fn create_user_table(conn: &SqliteConnection) {
sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL DEFAULT '', hair_color TEXT)")
.execute(conn)
.unwrap();
}

#[cfg(feature = "mysql")]
fn create_user_table(conn: &MysqlConnection) {
sql_query("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTO_INCREMENT, name TEXT NOT NULL DEFAULT '', hair_color TEXT)")
.execute(conn)
.unwrap();
sql_query("DELETE FROM users").execute(conn).unwrap();
}

#[cfg(feature = "sqlite")]
fn establish_connection() -> SqliteConnection {
SqliteConnection::establish(":memory:").unwrap()
}

#[cfg(feature = "postgres")]
fn establish_connection() -> PgConnection {
let conn = PgConnection::establish(
&dotenv::var("DATABASE_URL")
.or_else(|_| dotenv::var("PG_DATABASE_URL"))
.expect("Set either `DATABASE_URL` or `PG_DATABASE_URL`"),
)
.unwrap();

conn.begin_test_transaction().unwrap();
conn
}

#[cfg(feature = "mysql")]
fn establish_connection() -> MysqlConnection {
let conn = MysqlConnection::establish(
&dotenv::var("DATABASE_URL")
.or_else(|_| dotenv::var("MYSQL_DATABASE_URL"))
.expect("Set either `DATABASE_URL` or `MYSQL_DATABASE_URL`"),
)
.unwrap();

conn.begin_test_transaction().unwrap();

conn
}

#[test]
fn querying_basic_schemas() {
let conn = establish_connection();
sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT)")
.execute(&conn)
.unwrap();
create_user_table(&conn);
sql_query("INSERT INTO users DEFAULT VALUES")
.execute(&conn)
.unwrap();
@@ -25,9 +78,7 @@ fn querying_basic_schemas() {
#[test]
fn querying_multiple_types() {
let conn = establish_connection();
sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)")
.execute(&conn)
.unwrap();
create_user_table(&conn);
sql_query("INSERT INTO users (name) VALUES ('Sean'), ('Tess')")
.execute(&conn)
.unwrap();
@@ -42,9 +93,7 @@ fn querying_multiple_types() {
#[test]
fn columns_used_in_where_clause() {
let conn = establish_connection();
sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)")
.execute(&conn)
.unwrap();
create_user_table(&conn);
sql_query("INSERT INTO users (name) VALUES ('Sean'), ('Tess')")
.execute(&conn)
.unwrap();
@@ -60,12 +109,10 @@ fn columns_used_in_where_clause() {
}

#[test]
#[cfg(feature = "sqlite")]
fn providing_custom_schema_name() {
use diesel_dynamic_schema::schema;
let table = schema("information_schema").table("users");
let sql = debug_query::<Sqlite, _>(&table);
let sql = debug_query::<diesel::sqlite::Sqlite, _>(&table);
assert_eq!("`information_schema`.`users` -- binds: []", sql.to_string());
}

fn establish_connection() -> SqliteConnection {
SqliteConnection::establish(":memory:").unwrap()
}