Skip to content
This repository has been archived by the owner on Apr 25, 2023. It is now read-only.

Microsoft SQL Server Support #131

Merged
merged 16 commits into from
Jun 16, 2020
Merged
Show file tree
Hide file tree
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
7 changes: 7 additions & 0 deletions .buildkite/docker.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/bin/bash

MYSQL_ROOT_PASSWORD=prisma
MSSQL_SA_PASSWORD="<YourStrong@Passw0rd>"

docker network create test-net
docker run --name test-postgres --network test-net \
Expand All @@ -14,9 +15,15 @@ docker run --name test-mysql --network test-net \
-e MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD \
-e MYSQL_PASSWORD=prisma -d mysql

docker run --name test-mssql --network test-net \
-e ACCEPT_EULA=Y \
-e SA_PASSWORD=$MSSQL_SA_PASSWORD \
-d mcr.microsoft.com/mssql/server:2019-latest

docker run -w /build --network test-net -v $BUILDKITE_BUILD_CHECKOUT_PATH:/build \
-e TEST_MYSQL=mysql://prisma:prisma@test-mysql:3306/prisma \
-e TEST_PSQL=postgres://prisma:prisma@test-postgres:5432/prisma \
-e TEST_MSSQL="sqlserver://test-mssql:1433;user=SA;password=$MSSQL_SA_PASSWORD;trustServerCertificate=true" \
prismagraphql/build:test cargo test --features full,json-1,uuid-0_8,chrono-0_4,tracing-log,serde-support

exit_code=$?
Expand Down
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export TEST_MYSQL=mysql://root:prisma@localhost:3306/prisma
export TEST_PSQL=postgres://postgres:prisma@localhost:5432/postgres
export TEST_MSSQL="sqlserver://localhost:1433;database=master;user=SA;password=<YourStrong@Passw0rd>;trustServerCertificate=true"
17 changes: 13 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,26 @@ features = [ "full", "serde-support", "json-1", "uuid-0_8", "chrono-0_4", "array
[features]
default = []

full = ["pooled", "sqlite", "json-1", "postgresql", "uuid-0_8", "chrono-0_4", "mysql"]
full = ["pooled", "sqlite", "json-1", "postgresql", "uuid-0_8", "chrono-0_4", "mysql", "mssql"]
full-postgresql = ["pooled", "postgresql", "json-1", "uuid-0_8", "chrono-0_4", "array"]
full-mysql = ["pooled", "mysql", "json-1", "uuid-0_8", "chrono-0_4"]
full-sqlite = ["pooled", "sqlite", "json-1", "uuid-0_8", "chrono-0_4"]
full-mssql = ["pooled", "mssql"]

single = ["sqlite", "json-1", "postgresql", "uuid-0_8", "chrono-0_4", "mysql"]
single = ["sqlite", "json-1", "postgresql", "uuid-0_8", "chrono-0_4", "mysql", "mssql"]
single-postgresql = ["postgresql", "json-1", "uuid-0_8", "chrono-0_4", "array"]
single-mysql = ["mysql", "json-1", "uuid-0_8", "chrono-0_4"]
single-sqlite = ["sqlite", "json-1", "uuid-0_8", "chrono-0_4"]
single-mssql = ["mssql"]

pooled = ["mobc"]
sqlite = ["rusqlite", "libsqlite3-sys", "tokio/sync"]
json-1 = ["serde_json", "base64"]
postgresql = ["rust_decimal/postgres", "native-tls", "tokio-postgres", "postgres-native-tls", "array", "bytes", "tokio", "bit-vec"]
postgresql = ["rust_decimal/tokio-pg", "native-tls", "tokio-postgres", "postgres-native-tls", "array", "bytes", "tokio", "bit-vec"]
uuid-0_8 = ["uuid"]
chrono-0_4 = ["chrono"]
mysql = ["mysql_async", "tokio"]
mssql = ["tiberius", "uuid-0_8", "chrono-0_4", "tokio-util"]
tracing-log = ["tracing", "tracing-core"]
array = []
serde-support = ["serde", "chrono/serde"]
Expand All @@ -51,10 +54,11 @@ metrics = "0.12"
percent-encoding = "2"
once_cell = "1.3"
num_cpus = "1.12"
rust_decimal = "=1.1.0"
rust_decimal = "1.6"
futures = "0.3"
thiserror = "1.0"
async-trait = "0.1"
hex = "0.4"

uuid = { version = "0.8", optional = true }
chrono = { version = "0.4", optional = true }
Expand All @@ -70,16 +74,21 @@ native-tls = { version = "0.2", optional = true }

mysql_async = { version = "0.23", optional = true }

tiberius = { version = "0.4", optional = true, features = ["rust_decimal", "sql-browser-tokio"] }

log = { version = "0.4", features = ["release_max_level_trace"] }
tracing = { version = "0.1", optional = true }
tracing-core = { version = "0.1", optional = true }

mobc = { version = "0.5.7", optional = true }
bytes = { version = "0.5", optional = true }
tokio = { version = "0.2", features = ["rt-threaded", "macros", "sync"], optional = true}
tokio-util = { version = "0.3", features = ["compat"], optional = true }
serde = { version = "1.0", optional = true }
bit-vec = { version = "0.6.1", optional = true }

[dev-dependencies]
tokio = { version = "0.2", features = ["rt-threaded", "macros"]}
serde = { version = "1.0", features = ["derive"] }
indoc = "0.3"
names = "0.11"
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ Quaint is an abstraction over certain SQL databases. It provides:
- `full-postgresql`: Pooled support for PostgreSQL
- `full-mysql`: Pooled support for MySQL
- `full-sqlite`: Pooled support for SQLite
- `full-mssql`: Pooled support for Microsoft SQL Server
- `single`: All connectors, but no pooling
- `single-postgresql`: Single connection support for PostgreSQL
- `single-mysql`: Single connection support for MySQL
- `single-sqlite`: Single connection support for SQLite
- `single-mssql`: Single connection support for Microsoft SQL Server

### Goals:

Expand All @@ -43,8 +45,8 @@ choice.

### Testing:

- See `.envrc` for connection params. Override variables if different. MySQL and
PostgreSQL needs to be running for tests to succeed.
- See `.envrc` for connection params. Override variables if different. MySQL,
PostgreSQL and SQL Server needs to be running for tests to succeed.

Then:

Expand Down
8 changes: 6 additions & 2 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ mod delete;
mod expression;
mod function;
mod grouping;
mod index;
mod insert;
mod join;
mod merge;
mod ops;
mod ordering;
mod over;
Expand All @@ -26,16 +28,18 @@ mod union;
mod update;
mod values;

pub use column::Column;
pub use column::{Column, DefaultValue};
pub use compare::{Comparable, Compare};
pub use conditions::ConditionTree;
pub use conjunctive::Conjunctive;
pub use delete::Delete;
pub use expression::*;
pub use function::*;
pub use grouping::*;
pub use index::*;
pub use insert::*;
pub use join::{Join, JoinData, Joinable};
pub(crate) use merge::*;
pub use ops::*;
pub use ordering::{IntoOrderDefinition, Order, OrderDefinition, Orderable, Ordering};
pub use over::*;
Expand All @@ -45,7 +49,7 @@ pub use select::Select;
pub use table::*;
pub use union::Union;
pub use update::*;
pub use values::{Value, Values};
pub use values::{IntoRaw, Raw, Value, Values};

#[cfg(any(feature = "sqlite", feature = "mysql", feature = "postgresql"))]
pub(crate) use values::Params;
95 changes: 65 additions & 30 deletions src/ast/column.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,78 @@
use super::Aliasable;
use crate::ast::{Expression, ExpressionKind, Table};
use crate::{
ast::{Expression, ExpressionKind, Table},
Value,
};
use std::borrow::Cow;

/// A column definition.
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default)]
pub struct Column<'a> {
pub name: Cow<'a, str>,
pub(crate) table: Option<Table<'a>>,
pub(crate) alias: Option<Cow<'a, str>>,
pub(crate) default: Option<DefaultValue<'a>>,
}

#[macro_export]
/// Marks a given string or a tuple as a column. Useful when using a column in
/// calculations, e.g.
///
/// ``` rust
/// # use quaint::{col, val, ast::*, visitor::{Visitor, Sqlite}};
/// let join = "dogs".on(("dogs", "slave_id").equals(Column::from(("cats", "master_id"))));
///
/// let query = Select::from_table("cats")
/// .value(Table::from("cats").asterisk())
/// .value(col!("dogs", "age") - val!(4))
/// .inner_join(join);
///
/// let (sql, params) = Sqlite::build(query);
///
/// assert_eq!(
/// "SELECT `cats`.*, (`dogs`.`age` - ?) FROM `cats` INNER JOIN `dogs` ON `dogs`.`slave_id` = `cats`.`master_id`",
/// sql
/// );
/// ```
macro_rules! col {
($e1:expr) => {
Expression::from(Column::from($e1))
};

($e1:expr, $e2:expr) => {
Expression::from(Column::from(($e1, $e2)))
};
/// Defines a default value for a `Column`.
#[derive(Clone, Debug, PartialEq)]
pub enum DefaultValue<'a> {
/// A static value.
Provided(Value<'a>),
/// Generated in the database.
Generated,
}

impl<'a> Default for DefaultValue<'a> {
fn default() -> Self {
Self::Generated
}
}

impl<'a, V> From<V> for DefaultValue<'a>
where
V: Into<Value<'a>>,
{
fn from(v: V) -> Self {
Self::Provided(v.into())
}
}

impl<'a> PartialEq for Column<'a> {
fn eq(&self, other: &Column) -> bool {
self.name == other.name && self.table == other.table
}
}

impl<'a> Column<'a> {
/// Create a bare version of the column, stripping out all other information
/// other than the name.
pub(crate) fn into_bare(self) -> Self {
Self {
name: self.name,
table: None,
alias: None,
default: None,
}
}

/// Sets the default value for the column.
pub fn default<V>(mut self, value: V) -> Self
where
V: Into<DefaultValue<'a>>,
{
self.default = Some(value.into());
self
}
pimeys marked this conversation as resolved.
Show resolved Hide resolved

/// True when the default value is set and automatically generated in the
/// database.
pub fn default_autogen(&self) -> bool {
self.default
.as_ref()
.map(|d| d == &DefaultValue::Generated)
.unwrap_or(false)
}
}

impl<'a> From<Column<'a>> for Expression<'a> {
Expand Down
Loading