Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Database Client

Provides a set of functions that establish connections, execute queries and manage data transactions in different databases.

Benchmark

Independent benchmarks are available at https://github.com/diesel-rs/metrics.

PostgreSQL

Implements a subset of https://www.postgresql.org/docs/16/protocol.html. PostgreSQL is a robust, open-source relational database management system that, among other things, supports several data types and usually also excels in concurrent scenarios.

To use this functionality, it is necessary to activate the postgres feature.

Example

//! Demonstrates different interactions with a PostgreSQL database.

extern crate tokio;
extern crate wtx;
extern crate wtx_instances;

use wtx::{
  database::{Executor as _, Record, Records},
  misc::into_rslt,
};

#[tokio::main]
async fn main() -> wtx::Result<()> {
  let uri = "postgres://USER:PASSWORD@localhost/DATABASE";
  let mut executor = wtx_instances::executor_postgres(uri).await?;
  executor
    .transaction(|this| async {
      this.execute_ignored("CREATE TABLE IF NOT EXISTS example(id INT, name VARCHAR)").await?;
      this
        .execute_stmt_none("INSERT INTO foo VALUES ($1, $2), ($3, $4)", (1u32, "one", 2u32, "two"))
        .await?;
      Ok(((), this))
    })
    .await?;
  let records = executor
    .execute_stmt_many("SELECT id, name FROM example", (), |_| Ok::<_, wtx::Error>(()))
    .await?;
  let record0 = into_rslt(records.get(0))?;
  let record1 = into_rslt(records.get(1))?;
  assert_eq!((record0.decode::<_, u32>(0)?, record0.decode::<_, &str>("name")?), (1, "one"));
  assert_eq!((record1.decode::<_, u32>("id")?, record1.decode::<_, &str>(1)?), (2, "two"));
  Ok(())
}

MySQL

Implements a subset of https://dev.mysql.com/doc/dev/mysql-server/latest/. MySQL is also a robust, open-source relational database management system generally used in web applications.

WTX includes CI coverage for MariaDB and Percona, as such, interactions with these MySQL-based databases shouldn’t be a problem.

To use this functionality, it is necessary to activate the mysql feature.

Example

//! Demonstrates a MySQL query.

extern crate tokio;
extern crate wtx;
extern crate wtx_instances;

use wtx::database::{Executor, Record, Records};
use wtx_instances::executor_mysql;

#[tokio::main]
async fn main() -> wtx::Result<()> {
  let mut executor = executor_mysql("mysql://USER:PASSWORD@localhost/DATABASE").await?;
  let records = executor
    .execute_stmt_many("SELECT id, name FROM example", (), |_| Ok::<_, wtx::Error>(()))
    .await?;
  assert_eq!(records.get(0).as_ref().and_then(|record| record.decode("id").ok()), Some(1));
  assert_eq!(records.get(1).as_ref().and_then(|record| record.decode("name").ok()), Some("two"));
  Ok(())
}

Batch

Only PostgreSQL supports the sending of multiple statements in a single round-trip.

  • MariaDB has MARIADB_CLIENT_STMT_BULK_OPERATIONS but it only prevents one round trip of a single statement.
  • The X protocol (MySQL) is not implemented at the current time and is also not supported by MariaDB or PerconaDB.
  • MULTI_STATEMENT, from the Client/Server protocol, does not allow multiple prepared statements.
//! Sends multiple commands at once and awaits them.

extern crate tokio;
extern crate wtx;
extern crate wtx_instances;

use wtx::{
  collection::ArrayVectorU8,
  database::{Record, Records},
  misc::into_rslt,
};

const COMMANDS: &[&str] = &["SELECT 0 = $1", "SELECT 1 = $1", "SELECT 2 = $1"];

#[tokio::main]
async fn main() -> wtx::Result<()> {
  let uri = "postgres://USER:PASSWORD@localhost/DATABASE";
  let mut executor = wtx_instances::executor_postgres(uri).await?;
  let mut batch = executor.batch();
  let mut idx: u32 = 0;
  let mut records = ArrayVectorU8::<_, { COMMANDS.len() }>::new();
  for cmd in COMMANDS {
    batch.stmt(cmd, (idx,))?;
    idx = idx.wrapping_add(1);
  }
  batch.flush(&mut records, |_| Ok(())).await?;
  for record in records {
    assert_eq!(into_rslt(record.get(0))?.decode::<_, bool>(0)?, true);
  }
  Ok(())
}

Tests

The #[wtx::db] macro automatically migrates and seeds individual tests in isolation to allow concurrent evaluations.

Its current state is limited to PostgreSQL tests that use the standard std::net::TcpStream along side the built-in executor.

Required features: executor, macros, postgres and schema-manager-dev. Connected users must have the right to create new databases.

//! The DB isolation provided by `#[wtx::db]` allows the concurrent execution of identical queries
//! without causing conflicts.
//!
//! The `dir` parameter is optional.

fn main() {}

#[cfg(test)]
mod tests {
  use std::net::TcpStream;
  use wtx::database::{
    Executor, Record,
    client::postgres::{ExecutorBuffer, PostgresExecutor},
  };

  #[wtx::db(dir("../.test-utils"))]
  async fn first_test(conn: PostgresExecutor<wtx::Error, ExecutorBuffer, TcpStream>) {
    common(conn).await;
  }

  #[wtx::db(dir("../.test-utils"))]
  async fn second_test(conn: PostgresExecutor<wtx::Error, ExecutorBuffer, TcpStream>) {
    common(conn).await;
  }

  #[wtx::db(dir("../.test-utils"))]
  async fn third_test(conn: PostgresExecutor<wtx::Error, ExecutorBuffer, TcpStream>) {
    common(conn).await;
  }

  async fn common(mut conn: PostgresExecutor<wtx::Error, ExecutorBuffer, TcpStream>) {
    conn
      .execute_ignored(
        "
        CREATE TABLE foo(id INT PRIMARY KEY, description TEXT NOT NULL);
        INSERT INTO foo VALUES (1, 'BAR!');
    ",
      )
      .await
      .unwrap();
    let id: &str = conn.execute_single("SELECT * FROM foo").await.unwrap().decode(0).unwrap();
    assert_eq!(id, "1");
  }
}