shed/sql/common/lib.rs (143 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under both the MIT license found in the
* LICENSE-MIT file in the root directory of this source tree and the Apache
* License, Version 2.0 found in the LICENSE-APACHE file in the root directory
* of this source tree.
*/
//! Contains basic definitions for the sql crate and for any crate that wish
//! to implement traits to be used with the sql's queries macro.
#![deny(warnings, missing_docs, clippy::all, rustdoc::broken_intra_doc_links)]
pub mod error;
pub mod mysql;
pub mod sqlite;
pub mod transaction;
use anyhow::{bail, format_err, Context, Error};
use std::fmt::{self, Debug};
use std::sync::Arc;
// Used in docs
#[cfg(test)]
mod _unused {
use sql as _;
use sql_tests_lib as _;
}
/// Struct to store a set of write, read and read-only connections for a shard.
#[derive(Clone)]
pub struct SqlConnections {
/// Write connection to the master
pub write_connection: Connection,
/// Read connection
pub read_connection: Connection,
/// Read master connection
pub read_master_connection: Connection,
}
impl SqlConnections {
/// Create SqlConnections from a single connection.
pub fn new_single(connection: Connection) -> Self {
Self {
write_connection: connection.clone(),
read_connection: connection.clone(),
read_master_connection: connection,
}
}
}
/// Struct to store a set of write, read and read-only connections for a shard.
/// Plus a schema connection so that sqlite tables can be setup.
#[derive(Clone)]
pub struct SqlConnectionsWithSchema {
/// Normal connections not used for schema creation
connections: SqlConnections,
/// Connections for schema creation, only populated for sqlite.
/// This is separate from write_connection as test cases still rely on schema being
/// present but empty when in readonly mode.
schema_connection: Option<Connection>,
}
impl SqlConnectionsWithSchema {
/// Create a new SqlConnectionsWithSchema
pub fn new(connections: SqlConnections, schema_connection: Option<Connection>) -> Self {
Self {
connections,
schema_connection,
}
}
/// Create SqlConnections from a single connection.
pub fn new_single(connection: Connection) -> Self {
Self {
connections: SqlConnections::new_single(connection.clone()),
schema_connection: Some(connection),
}
}
/// Get a reference to the regular connections
pub fn connections(&self) -> &SqlConnections {
&self.connections
}
/// Execute sql on the schema connection to create schema if not present
/// For mysql the schema connection should be None as schema is setup in advance2
pub fn create_schema(&self, schema_sql: &str) -> Result<(), Error> {
match &self.schema_connection {
Some(Connection::Sqlite(conn)) => conn
.get_sqlite_guard()
.execute_batch(schema_sql)
.with_context(|| format_err!("failed sql: {}", schema_sql)),
Some(_) => bail!("not expecting schema connection for mysql"),
None => Ok(()),
}
}
}
impl From<SqlConnectionsWithSchema> for SqlConnections {
fn from(from: SqlConnectionsWithSchema) -> SqlConnections {
from.connections
}
}
/// Struct to store a set of write, read and read-only connections for multiple shards.
#[derive(Clone)]
pub struct SqlShardedConnections {
/// Write connections to the master for each shard
pub write_connections: Vec<Connection>,
/// Read connections for each shard
pub read_connections: Vec<Connection>,
/// Read master connections for each shard
pub read_master_connections: Vec<Connection>,
}
impl SqlShardedConnections {
/// Check if the struct is empty.
pub fn is_empty(&self) -> bool {
self.write_connections.is_empty()
}
}
impl From<Vec<SqlConnections>> for SqlShardedConnections {
fn from(shard_connections: Vec<SqlConnections>) -> Self {
let mut write_connections = Vec::with_capacity(shard_connections.len());
let mut read_connections = Vec::with_capacity(shard_connections.len());
let mut read_master_connections = Vec::with_capacity(shard_connections.len());
for connections in shard_connections.into_iter() {
write_connections.push(connections.write_connection);
read_connections.push(connections.read_connection);
read_master_connections.push(connections.read_master_connection);
}
Self {
read_connections,
read_master_connections,
write_connections,
}
}
}
/// Enum that generalizes over connections to Sqlite and MyRouter.
#[derive(Clone)]
pub enum Connection {
/// Sqlite lets you use this crate with rusqlite connections such as in memory or on disk Sqlite
/// databases, both useful in case of testing or local sql db use cases.
Sqlite(Arc<sqlite::SqliteMultithreaded>),
/// A variant used for the new Mysql client connection factory.
Mysql(mysql::Connection),
}
impl From<sqlite::SqliteMultithreaded> for Connection {
fn from(con: sqlite::SqliteMultithreaded) -> Self {
Connection::Sqlite(Arc::new(con))
}
}
impl From<mysql::Connection> for Connection {
fn from(conn: mysql::Connection) -> Self {
Connection::Mysql(conn)
}
}
impl Debug for Connection {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Connection::Sqlite(..) => write!(f, "Sqlite"),
Connection::Mysql(..) => write!(f, "Mysql client"),
}
}
}
/// Value returned from a `write` type of query
#[derive(Debug)]
pub struct WriteResult {
last_insert_id: Option<u64>,
affected_rows: u64,
}
impl WriteResult {
/// Method made public for access from inside macros, you probably don't want to use it.
pub fn new(last_insert_id: Option<u64>, affected_rows: u64) -> Self {
WriteResult {
last_insert_id,
affected_rows,
}
}
/// Return the id of last inserted row if any.
pub fn last_insert_id(&self) -> Option<u64> {
self.last_insert_id
}
/// Return number of rows affected by the `write` query
pub fn affected_rows(&self) -> u64 {
self.affected_rows
}
}