Code Monkey home page Code Monkey logo

rust-mysql-simple's Introduction

Gitter

Crates.io Build Status

mysql

This crate offers:

  • MySql database driver in pure rust;
  • connection pool.

Features:

  • macOS, Windows and Linux support;
  • TLS support via nativetls or rustls (see the SSL Support section);
  • MySql text protocol support, i.e. support of simple text queries and text result sets;
  • MySql binary protocol support, i.e. support of prepared statements and binary result sets;
  • support of multi-result sets;
  • support of named parameters for prepared statements (see the Named Parameters section);
  • per-connection cache of prepared statements (see the Statement Cache section);
  • buffer pool (see the Buffer Pool section);
  • support of MySql packets larger than 2^24;
  • support of Unix sockets and Windows named pipes;
  • support of custom LOCAL INFILE handlers;
  • support of MySql protocol compression;
  • support of auth plugins:
    • mysql_native_password - for MySql prior to v8;
    • caching_sha2_password - for MySql v8 and higher;
    • mysql_clear_password - opt-in (see [Opts::get_enable_cleartext_plugin].

Installation

Put the desired version of the crate into the dependencies section of your Cargo.toml:

[dependencies]
mysql = "*"

Example

use mysql::*;
use mysql::prelude::*;

#[derive(Debug, PartialEq, Eq)]
struct Payment {
    customer_id: i32,
    amount: i32,
    account_name: Option<String>,
}


fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    let url = "mysql://root:password@localhost:3307/db_name";
    # Opts::try_from(url)?;
    # let url = get_opts();
    let pool = Pool::new(url)?;

    let mut conn = pool.get_conn()?;

    // Let's create a table for payments.
    conn.query_drop(
        r"CREATE TEMPORARY TABLE payment (
            customer_id int not null,
            amount int not null,
            account_name text
        )")?;

    let payments = vec![
        Payment { customer_id: 1, amount: 2, account_name: None },
        Payment { customer_id: 3, amount: 4, account_name: Some("foo".into()) },
        Payment { customer_id: 5, amount: 6, account_name: None },
        Payment { customer_id: 7, amount: 8, account_name: None },
        Payment { customer_id: 9, amount: 10, account_name: Some("bar".into()) },
    ];

    // Now let's insert payments to the database
    conn.exec_batch(
        r"INSERT INTO payment (customer_id, amount, account_name)
          VALUES (:customer_id, :amount, :account_name)",
        payments.iter().map(|p| params! {
            "customer_id" => p.customer_id,
            "amount" => p.amount,
            "account_name" => &p.account_name,
        })
    )?;

    // Let's select payments from database. Type inference should do the trick here.
    let selected_payments = conn
        .query_map(
            "SELECT customer_id, amount, account_name from payment",
            |(customer_id, amount, account_name)| {
                Payment { customer_id, amount, account_name }
            },
        )?;

    // Let's make sure, that `payments` equals to `selected_payments`.
    // Mysql gives no guaranties on order of returned rows
    // without `ORDER BY`, so assume we are lucky.
    assert_eq!(payments, selected_payments);
    println!("Yay!");

    Ok(())
}

Crate Features

  • feature sets:

    • default – includes default mysql_common features, native-tls, buffer-pool, flate2/zlib and derive
    • default-rustls - same as default but with rustls-tls instead of native-tls and flate2/rust_backend instead of flate2/zlib
    • minimal - includes flate2/zlib
  • crate's features:

    • native-tls (enabled by default) – specifies native-tls as the TLS backend (see the SSL Support section)
    • rustls-tls (disabled by default) – specifies rustls as the TLS backend (see the SSL Support section)
    • buffer-pool (enabled by default) – enables buffer pooling (see the Buffer Pool section)
    • derive (enabled by default) – reexports derive macros under prelude
  • external features enabled by default:

    • for the flate2 crate (please consult flate2 crate documentation for available features):

      • flate2/zlib (necessary) – zlib backend is chosed by default.
    • for the mysql_common crate (please consult mysql_common crate documentation for available features):

      • mysql_common/bigdecimal03 – the bigdecimal03 is enabled by default
      • mysql_common/rust_decimal – the rust_decimal is enabled by default
      • mysql_common/time03 – the time03 is enabled by default
      • mysql_common/uuid – the uuid is enabled by default
      • mysql_common/frunk – the frunk is enabled by default

Please note, that you'll need to reenable required features if you are using default-features = false:

[dependencies]
# Lets say that we want to use the `rustls-tls` feature:
mysql = { version = "*", default-features = false, features = ["minimal", "rustls-tls"] }
# Previous line disables default mysql features,
# so now we need to choose desired mysql_common features:
mysql_common = { version = "*", default-features = false, features = ["bigdecimal03", "time03", "uuid"]}

API Documentation

Please refer to the crate docs.

Basic structures

Opts

This structure holds server host name, client username/password and other settings, that controls client behavior.

URL-based connection string

Note, that you can use URL-based connection string as a source of an Opts instance. URL schema must be mysql. Host, port and credentials, as well as query parameters, should be given in accordance with the RFC 3986.

Examples:

let _ = Opts::from_url("mysql://localhost/some_db")?;
let _ = Opts::from_url("mysql://[::1]/some_db")?;
let _ = Opts::from_url("mysql://user:pass%[email protected]:3307/some_db?")?;

Supported URL parameters (for the meaning of each field please refer to the docs on Opts structure in the create API docs):

  • user: string – MySql client user name
  • password: string – MySql client password;
  • db_name: string – MySql database name;
  • host: Host – MySql server hostname/ip;
  • port: u16 – MySql server port;
  • pool_min: usize – see [PoolConstraints::min];
  • pool_max: usize – see [PoolConstraints::max];
  • prefer_socket: true | false - see [Opts::get_prefer_socket];
  • tcp_keepalive_time_ms: u32 - defines the value (in milliseconds) of the tcp_keepalive_time field in the Opts structure;
  • tcp_keepalive_probe_interval_secs: u32 - defines the value of the tcp_keepalive_probe_interval_secs field in the Opts structure;
  • tcp_keepalive_probe_count: u32 - defines the value of the tcp_keepalive_probe_count field in the Opts structure;
  • tcp_connect_timeout_ms: u64 - defines the value (in milliseconds) of the tcp_connect_timeout field in the Opts structure;
  • tcp_user_timeout_ms - defines the value (in milliseconds) of the tcp_user_timeout field in the Opts structure;
  • stmt_cache_size: u32 - defines the value of the same field in the Opts structure;
  • enable_cleartext_plugin – see [Opts::get_enable_cleartext_plugin];
  • secure_auth – see [Opts::get_secure_auth];
  • reset_connection – see [PoolOpts::reset_connection];
  • check_health – see [PoolOpts::check_health];
  • compress - defines the value of the same field in the Opts structure. Supported value are:
    • true - enables compression with the default compression level;
    • fast - enables compression with "fast" compression level;
    • best - enables compression with "best" compression level;
    • 1..9 - enables compression with the given compression level.
  • socket - socket path on UNIX, or pipe name on Windows.

OptsBuilder

It's a convenient builder for the Opts structure. It defines setters for fields of the Opts structure.

let opts = OptsBuilder::new()
    .user(Some("foo"))
    .db_name(Some("bar"));
let _ = Conn::new(opts)?;

Conn

This structure represents an active MySql connection. It also holds statement cache and metadata for the last result set.

Conn's destructor will gracefully disconnect it from the server.

Transaction

It's a simple wrapper on top of a routine, that starts with START TRANSACTION and ends with COMMIT or ROLLBACK.

use mysql::*;
use mysql::prelude::*;

let pool = Pool::new(get_opts())?;
let mut conn = pool.get_conn()?;

let mut tx = conn.start_transaction(TxOpts::default())?;
tx.query_drop("CREATE TEMPORARY TABLE tmp (TEXT a)")?;
tx.exec_drop("INSERT INTO tmp (a) VALUES (?)", ("foo",))?;
let val: Option<String> = tx.query_first("SELECT a from tmp")?;
assert_eq!(val.unwrap(), "foo");
// Note, that transaction will be rolled back implicitly on Drop, if not committed.
tx.rollback();

let val: Option<String> = conn.query_first("SELECT a from tmp")?;
assert_eq!(val, None);

Pool

It's a reference to a connection pool, that can be cloned and shared between threads.

use mysql::*;
use mysql::prelude::*;

use std::thread::spawn;

let pool = Pool::new(get_opts())?;

let handles = (0..4).map(|i| {
    spawn({
        let pool = pool.clone();
        move || {
            let mut conn = pool.get_conn()?;
            conn.exec_first::<u32, _, _>("SELECT ? * 10", (i,))
                .map(Option::unwrap)
        }
    })
});

let result: Result<Vec<u32>> = handles.map(|handle| handle.join().unwrap()).collect();

assert_eq!(result.unwrap(), vec![0, 10, 20, 30]);

Statement

Statement, actually, is just an identifier coupled with statement metadata, i.e an information about its parameters and columns. Internally the Statement structure also holds additional data required to support named parameters (see bellow).

use mysql::*;
use mysql::prelude::*;

let pool = Pool::new(get_opts())?;
let mut conn = pool.get_conn()?;

let stmt = conn.prep("DO ?")?;

// The prepared statement will return no columns.
assert!(stmt.columns().is_empty());

// The prepared statement have one parameter.
let param = stmt.params().get(0).unwrap();
assert_eq!(param.schema_str(), "");
assert_eq!(param.table_str(), "");
assert_eq!(param.name_str(), "?");

Value

This enumeration represents the raw value of a MySql cell. Library offers conversion between Value and different rust types via FromValue trait described below.

FromValue trait

This trait is reexported from mysql_common create. Please refer to its crate docs for the list of supported conversions.

Trait offers conversion in two flavours:

  • from_value(Value) -> T - convenient, but panicking conversion.

    Note, that for any variant of Value there exist a type, that fully covers its domain, i.e. for any variant of Value there exist T: FromValue such that from_value will never panic. This means, that if your database schema is known, than it's possible to write your application using only from_value with no fear of runtime panic.

  • from_value_opt(Value) -> Option<T> - non-panicking, but less convenient conversion.

    This function is useful to probe conversion in cases, where source database schema is unknown.

use mysql::*;
use mysql::prelude::*;

let via_test_protocol: u32 = from_value(Value::Bytes(b"65536".to_vec()));
let via_bin_protocol: u32 = from_value(Value::UInt(65536));
assert_eq!(via_test_protocol, via_bin_protocol);

let unknown_val = // ...

// Maybe it is a float?
let unknown_val = match from_value_opt::<f64>(unknown_val) {
    Ok(float) => {
        println!("A float value: {}", float);
        return Ok(());
    }
    Err(FromValueError(unknown_val)) => unknown_val,
};

// Or a string?
let unknown_val = match from_value_opt::<String>(unknown_val) {
    Ok(string) => {
        println!("A string value: {}", string);
        return Ok(());
    }
    Err(FromValueError(unknown_val)) => unknown_val,
};

// Screw this, I'll simply match on it
match unknown_val {
    val @ Value::NULL => {
        println!("An empty value: {:?}", from_value::<Option<u8>>(val))
    },
    val @ Value::Bytes(..) => {
        // It's non-utf8 bytes, since we already tried to convert it to String
        println!("Bytes: {:?}", from_value::<Vec<u8>>(val))
    }
    val @ Value::Int(..) => {
        println!("A signed integer: {}", from_value::<i64>(val))
    }
    val @ Value::UInt(..) => {
        println!("An unsigned integer: {}", from_value::<u64>(val))
    }
    Value::Float(..) => unreachable!("already tried"),
    val @ Value::Double(..) => {
        println!("A double precision float value: {}", from_value::<f64>(val))
    }
    val @ Value::Date(..) => {
        use time::PrimitiveDateTime;
        println!("A date value: {}", from_value::<PrimitiveDateTime>(val))
    }
    val @ Value::Time(..) => {
        use std::time::Duration;
        println!("A time value: {:?}", from_value::<Duration>(val))
    }
}

Row

Internally Row is a vector of Values, that also allows indexing by a column name/offset, and stores row metadata. Library offers conversion between Row and sequences of Rust types via FromRow trait described below.

FromRow trait

This trait is reexported from mysql_common create. Please refer to its crate docs for the list of supported conversions.

This conversion is based on the FromValue and so comes in two similar flavours:

  • from_row(Row) -> T - same as from_value, but for rows;
  • from_row_opt(Row) -> Option<T> - same as from_value_opt, but for rows.

Queryable trait offers implicit conversion for rows of a query result, that is based on this trait.

use mysql::*;
use mysql::prelude::*;

let mut conn = Conn::new(get_opts())?;

// Single-column row can be converted to a singular value:
let val: Option<String> = conn.query_first("SELECT 'foo'")?;
assert_eq!(val.unwrap(), "foo");

// Example of a mutli-column row conversion to an inferred type:
let row = conn.query_first("SELECT 255, 256")?;
assert_eq!(row, Some((255u8, 256u16)));

// The FromRow trait does not support to-tuple conversion for rows with more than 12 columns,
// but you can do this by hand using row indexing or `Row::take` method:
let row: Row = conn.exec_first("select 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12", ())?.unwrap();
for i in 0..row.len() {
    assert_eq!(row[i], Value::Int(i as i64));
}

// Another way to handle wide rows is to use HList (requires `mysql_common/frunk` feature)
use frunk::{HList, hlist, hlist_pat};
let query = "select 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15";
type RowType = HList!(u8, u16, u32, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8);
let first_three_columns = conn.query_map(query, |row: RowType| {
    // do something with the row (see the `frunk` crate documentation)
    let hlist_pat![c1, c2, c3, ...] = row;
    (c1, c2, c3)
});
assert_eq!(first_three_columns.unwrap(), vec![(0_u8, 1_u16, 2_u32)]);

// Some unknown row
let row: Row = conn.query_first(
    // ...
    # "SELECT 255, Null",
)?.unwrap();

for column in row.columns_ref() {
    // Cells in a row can be indexed by numeric index or by column name
    let column_value = &row[column.name_str().as_ref()];

    println!(
        "Column {} of type {:?} with value {:?}",
        column.name_str(),
        column.column_type(),
        column_value,
    );
}

Params

Represents parameters of a prepared statement, but this type won't appear directly in your code because binary protocol API will ask for T: Into<Params>, where Into<Params> is implemented:

  • for tuples of Into<Value> types up to arity 12;

    Note: singular tuple requires extra comma, e.g. ("foo",);

  • for IntoIterator<Item: Into<Value>> for cases, when your statement takes more than 12 parameters;

  • for named parameters representation (the value of the params! macro, described below).

use mysql::*;
use mysql::prelude::*;

let mut conn = Conn::new(get_opts())?;

// Singular tuple requires extra comma:
let row: Option<u8> = conn.exec_first("SELECT ?", (0,))?;
assert_eq!(row.unwrap(), 0);

// More than 12 parameters:
let row: Option<u8> = conn.exec_first(
    "SELECT CONVERT(? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ?, UNSIGNED)",
    (0..16).collect::<Vec<_>>(),
)?;
assert_eq!(row.unwrap(), 120);

Note: Please refer to the mysql_common crate docs for the list of types, that implements Into<Value>.

Serialized, Deserialized

Wrapper structures for cases, when you need to provide a value for a JSON cell, or when you need to parse JSON cell as a struct.

use mysql::*;
use mysql::prelude::*;

/// Serializable structure.
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Example {
    foo: u32,
}

// Value::from for Serialized will emit json string.
let value = Value::from(Serialized(Example { foo: 42 }));
assert_eq!(value, Value::Bytes(br#"{"foo":42}"#.to_vec()));

// from_value for Deserialized will parse json string.
let structure: Deserialized<Example> = from_value(value);
assert_eq!(structure, Deserialized(Example { foo: 42 }));

[QueryResult]

It's an iterator over rows of a query result with support of multi-result sets. It's intended for cases when you need full control during result set iteration. For other cases Queryable provides a set of methods that will immediately consume the first result set and drop everything else.

This iterator is lazy so it won't read the result from server until you iterate over it. MySql protocol is strictly sequential, so Conn will be mutably borrowed until the result is fully consumed (please also look at [QueryResult::iter] docs).

use mysql::*;
use mysql::prelude::*;

let mut conn = Conn::new(get_opts())?;

// This query will emit two result sets.
let mut result = conn.query_iter("SELECT 1, 2; SELECT 3, 3.14;")?;

let mut sets = 0;
while let Some(result_set) = result.iter() {
    sets += 1;

    println!("Result set columns: {:?}", result_set.columns());
    println!(
        "Result set meta: {}, {:?}, {} {}",
        result_set.affected_rows(),
        result_set.last_insert_id(),
        result_set.warnings(),
        result_set.info_str(),
    );

    for row in result_set {
        match sets {
            1 => {
                // First result set will contain two numbers.
                assert_eq!((1_u8, 2_u8), from_row(row?));
            }
            2 => {
                // Second result set will contain a number and a float.
                assert_eq!((3_u8, 3.14), from_row(row?));
            }
            _ => unreachable!(),
        }
    }
}

assert_eq!(sets, 2);

Text protocol

MySql text protocol is implemented in the set of Queryable::query* methods. It's useful when your query doesn't have parameters.

Note: All values of a text protocol result set will be encoded as strings by the server, so from_value conversion may lead to additional parsing costs.

Examples:

let pool = Pool::new(get_opts())?;
let val = pool.get_conn()?.query_first("SELECT POW(2, 16)")?;

// Text protocol returns bytes even though the result of POW
// is actually a floating point number.
assert_eq!(val, Some(Value::Bytes("65536".as_bytes().to_vec())));

The TextQuery trait.

The TextQuery trait covers the set of Queryable::query* methods from the perspective of a query, i.e. TextQuery is something, that can be performed if suitable connection is given. Suitable connections are:

  • &Pool
  • Conn
  • PooledConn
  • &mut Conn
  • &mut PooledConn
  • &mut Transaction

The unique characteristic of this trait, is that you can give away the connection and thus produce QueryResult that satisfies 'static:

use mysql::*;
use mysql::prelude::*;

fn iter(pool: &Pool) -> Result<impl Iterator<Item=Result<u32>>> {
    let result = "SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3".run(pool)?;
    Ok(result.map(|row| row.map(from_row)))
}

let pool = Pool::new(get_opts())?;

let it = iter(&pool)?;

assert_eq!(it.collect::<Result<Vec<u32>>>()?, vec![1, 2, 3]);

Binary protocol and prepared statements.

MySql binary protocol is implemented in prep, close and the set of exec* methods, defined on the Queryable trait. Prepared statements is the only way to pass rust value to the MySql server. MySql uses ? symbol as a parameter placeholder and it's only possible to use parameters where a single MySql value is expected. For example:

let pool = Pool::new(get_opts())?;
let val = pool.get_conn()?.exec_first("SELECT POW(?, ?)", (2, 16))?;

assert_eq!(val, Some(Value::Double(65536.0)));

Statements

In MySql each prepared statement belongs to a particular connection and can't be executed on another connection. Trying to do so will lead to an error. The driver won't tie statement to its connection in any way, but one can look on to the connection id, contained in the Statement structure.

let pool = Pool::new(get_opts())?;

let mut conn_1 = pool.get_conn()?;
let mut conn_2 = pool.get_conn()?;

let stmt_1 = conn_1.prep("SELECT ?")?;

// stmt_1 is for the conn_1, ..
assert!(stmt_1.connection_id() == conn_1.connection_id());
assert!(stmt_1.connection_id() != conn_2.connection_id());

// .. so stmt_1 will execute only on conn_1
assert!(conn_1.exec_drop(&stmt_1, ("foo",)).is_ok());
assert!(conn_2.exec_drop(&stmt_1, ("foo",)).is_err());

Statement cache

Note

Statemet cache only works for:

  1. for raw [Conn]
  2. for [PooledConn]:
    • within it's lifetime if [PoolOpts::reset_connection] is true
    • within the lifetime of a wrapped [Conn] if [PoolOpts::reset_connection] is false
Description

Conn will manage the cache of prepared statements on the client side, so subsequent calls to prepare with the same statement won't lead to a client-server roundtrip. Cache size for each connection is determined by the stmt_cache_size field of the Opts structure. Statements, that are out of this boundary will be closed in LRU order.

Statement cache is completely disabled if stmt_cache_size is zero.

Caveats:

  • disabled statement cache means, that you have to close statements yourself using Conn::close, or they'll exhaust server limits/resources;

  • you should be aware of the max_prepared_stmt_count option of the MySql server. If the number of active connections times the value of stmt_cache_size is greater, than you could receive an error while prepareing another statement.

Named parameters

MySql itself doesn't have named parameters support, so it's implemented on the client side. One should use :name as a placeholder syntax for a named parameter. Named parameters uses the following naming convention:

  • parameter name must start with either _ or a..z
  • parameter name may continue with _, a..z and 0..9

Named parameters may be repeated within the statement, e.g SELECT :foo, :foo will require a single named parameter foo that will be repeated on the corresponding positions during statement execution.

One should use the params! macro to build parameters for execution.

Note: Positional and named parameters can't be mixed within the single statement.

Examples:

let pool = Pool::new(get_opts())?;

let mut conn = pool.get_conn()?;
let stmt = conn.prep("SELECT :foo, :bar, :foo")?;

let foo = 42;

let val_13 = conn.exec_first(&stmt, params! { "foo" => 13, "bar" => foo })?.unwrap();
// Short syntax is available when param name is the same as variable name:
let val_42 = conn.exec_first(&stmt, params! { foo, "bar" => 13 })?.unwrap();

assert_eq!((foo, 13, foo), val_42);
assert_eq!((13, foo, 13), val_13);

Buffer pool

Crate uses the global lock-free buffer pool for the purpose of IO and data serialization/deserialization, that helps to avoid allocations for basic scenarios. You can control it's characteristics using the following environment variables:

  • RUST_MYSQL_BUFFER_POOL_CAP (defaults to 128) – controls the pool capacity. Dropped buffer will be immediately deallocated if the pool is full. Set it to 0 to disable the pool at runtime.

  • RUST_MYSQL_BUFFER_SIZE_CAP (defaults to 4MiB) – controls the maximum capacity of a buffer stored in the pool. Capacity of a dropped buffer will be shrunk to this value when buffer is returned to the pool.

To completely disable the pool (say you are using jemalloc) please remove the buffer-pool feature from the set of default crate features (see the Crate Features section).

BinQuery and BatchQuery traits.

BinQuery and BatchQuery traits covers the set of Queryable::exec* methods from the perspective of a query, i.e. BinQuery is something, that can be performed if suitable connection is given (see TextQuery section for the list of suitable connections).

As with the TextQuery you can give away the connection and acquire QueryResult that satisfies 'static.

BinQuery is for prepared statements, and prepared statements requires a set of parameters, so BinQuery is implemented for QueryWithParams structure, that can be acquired, using WithParams trait.

Example:

use mysql::*;
use mysql::prelude::*;

let pool = Pool::new(get_opts())?;

let result: Option<(u8, u8, u8)> = "SELECT ?, ?, ?"
    .with((1, 2, 3)) // <- WithParams::with will construct an instance of QueryWithParams
    .first(&pool)?;  // <- QueryWithParams is executed on the given pool

assert_eq!(result.unwrap(), (1, 2, 3));

The BatchQuery trait is a helper for batch statement execution. It's implemented for QueryWithParams where parameters is an iterator over parameters:

use mysql::*;
use mysql::prelude::*;

let pool = Pool::new(get_opts())?;
let mut conn = pool.get_conn()?;

"CREATE TEMPORARY TABLE batch (x INT)".run(&mut conn)?;
"INSERT INTO batch (x) VALUES (?)"
    .with((0..3).map(|x| (x,))) // <- QueryWithParams constructed with an iterator
    .batch(&mut conn)?;         // <- batch execution is preformed here

let result: Vec<u8> = "SELECT x FROM batch".fetch(conn)?;

assert_eq!(result, vec![0, 1, 2]);

Queryable

The Queryable trait defines common methods for Conn, PooledConn and Transaction. The set of basic methods consts of:

  • query_iter - basic methods to execute text query and get QueryResult;
  • prep - basic method to prepare a statement;
  • exec_iter - basic method to execute statement and get QueryResult;
  • close - basic method to close the statement;

The trait also defines the set of helper methods, that is based on basic methods. These methods will consume only the first result set, other result sets will be dropped:

  • {query|exec} - to collect the result into a Vec<T: FromRow>;
  • {query|exec}_first - to get the first T: FromRow, if any;
  • {query|exec}_map - to map each T: FromRow to some U;
  • {query|exec}_fold - to fold the set of T: FromRow to a single value;
  • {query|exec}_drop - to immediately drop the result.

The trait also defines the exec_batch function, which is a helper for batch statement execution.

SSL Support

SSL support comes in two flavors:

  1. Based on native-tls – this is the default option, that usually works without pitfalls (see the native-tls crate feature).

  2. Based on rustls – TLS backend written in Rust. Please use the rustls-tls crate feature to enable it (see the Crate Features section).

    Please also note a few things about rustls:

    • it will fail if you'll try to connect to the server by its IP address, hostname is required;
    • it, most likely, won't work on windows, at least with default server certs, generated by the MySql installer.

Changelog

Available here

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

rust-mysql-simple's People

Contributors

0xpr03 avatar alex-gulyas avatar altmannmarcelo avatar amv-dev avatar blackbeam avatar dfaust avatar diggsey avatar fulara avatar hpca01 avatar jbmcgill avatar jonhoo avatar kubo avatar oderwat avatar pavlov-dmitry avatar php-coder avatar renato-zannon avatar robjtede avatar roguelazer avatar rtzoeller avatar spacemaniac avatar taiki-e avatar therustmonk avatar theserverasterisk avatar thomasdezeeuw avatar tobiasdebruijn avatar tolgap avatar toothbrush7777777 avatar yerke avatar zacharygolba avatar zomtir avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rust-mysql-simple's Issues

Proper Row object rather than Vec<Value>?

It would be nice if the QueryResult iterator yielded a proper Row object rather than a simple Vec<Value>. That is, it would be convenient if each row contained information about the query columns/statement. My specific use case would be a get method on Row that could take a &str (column name) or usize parameter, and return the Value for that position.

Both rusqlite and the postgres driver provide such an interface. I haven't put together a pull request since this might be a radical change to the API, and I don't know if there are performance/overhead concerns...

Just for fun, here is a workaround I am currently using (though it is kind of ugly and probably inefficient):

pub struct MyWrapper<'a>(QueryResult<'a>);

pub struct MyRow {
  pub row: RefCell<HashMap<usize, Value>>,
  pub columns: HashMap<String, usize>
}

impl MyRow {

  pub fn new(row: MyResult<Vec<Value>>, columns: &[Column]) -> Self {
    let _row = row.into_iter()
      .flat_map(|x| x)
      .enumerate()
      .collect();
    let _columns = columns.iter()
      .map(|x| x.name.clone() )
      .filter_map(|x| String::from_utf8(x).ok() )
      .enumerate()
      .map(|(x, y)| (y, x) )
      .collect();
    MyRow { row: RefCell::new(_row), columns: _columns }
  }

  fn at<T: FromValue>(&self, s: &str) -> Option<T> {
    self.columns.get(s).into_iter()
      .filter_map(|i| self.row.borrow_mut().remove(&i) )
      .filter_map(|x| from_value_opt(x).ok() )
      .next()
  }

}

impl<'a> Iterator for MyWrapper<'a> {
  type Item = MyRow;

  fn next(&mut self) -> Option<MyRow> {
    self.0.next()
      .map(|row| MyRow::new(row, self.0.columns_ref() ) )
  }
}

pub trait MyWrapped<'a> {
  fn wrapped(self) -> MyWrapper<'a>;
}

impl<'a> MyWrapped<'a> for QueryResult<'a> {
  fn wrapped(self) -> MyWrapper<'a> { MyWrapper(self) }
}

Which I can use like:

impl User {
  pub fn all(pool: &MyPool) -> Vec<User> {
    pool.prep_exec("SELECT * FROM users", ()).into_iter()
      .flat_map(|x| x.wrapped() )
      .map(|row| User { email: row.at("email"), ..Default::default() } )
      .collect()
  }
}

Issue implementating pseudocode found in issue #59

I have the following:

use mysql as my;

/// The `Record` type is meant as container for the pipe delimited values from a file.
type Record = Vec<Option<String>>;
fn bulk_insert<F, P>(pool: &my::Pool, table: String, cols: Vec<String>, objects: Vec<Record>, fun: F) -> my::Result<()>
where F: Fn(Vec<Option<String>>) -> P,
      P: Into<my::Params>,
{
    let mut stmt = format!("INSERT INTO {} ({}) VALUES ", table, cols.join(","));
    let build_row = | length: usize | {
        let temp = Vec::new();
        for x in 0..length {
            temp.push("?".to_string());
        }
        temp
    };
    let mut row = format!("({}),", build_row(cols.len()).as_slice().join(","));
    stmt.reserve(objects.len() * (cols.len() * 2 + 2));
    for _ in 0..objects.len() {
        stmt.push_str(row.as_str());
    }

    let mut params = Vec::new();
    for o in objects.iter() {
        let named_params = fun(*o).into();
        let positional_params = named_params.into_positional(&cols);
        for param in positional_params.into_iter() {
            params.push(param);
        }
    }
    try!(pool.prep_exec(stmt, params));
}

The stack trace I get is:

error[E0277]: the trait bound `mysql::Value: std::convert::From<mysql::Params>` is not satisfied
  --> sql_tools/tools.rs:32:15
   |
32 |     try!(pool.prep_exec(stmt, params));
   |               ^^^^^^^^^
sql_tools/tools.rs:32:5: 32:40 note: in this expansion of try! (defined in <std macros>)
   |
   = help: the following implementations were found:
   = help:   <mysql::Value as std::convert::From<&'a T>>
   = help:   <mysql::Value as std::convert::From<std::option::Option<T>>>
   = help:   <mysql::Value as std::convert::From<i8>>
   = help:   <mysql::Value as std::convert::From<u8>>
   = help: and 54 others
   = note: required because of the requirements on the impl of `std::convert::Into<mysql::Value>` for `mysql::Params`
   = note: required because of the requirements on the impl of `std::convert::From<std::vec::Vec<mysql::Params>>` for `mysql::Params`
   = note: required because of the requirements on the impl of `std::convert::Into<mysql::Params>` for `std::vec::Vec<mysql::Params>`

error: aborting due to previous error

error: Could not compile `import_export`.

Not sure if this is incorrect usage on my part. However, not being able to have a collection of records for a mass insert is difficult. Unfortunately, option 1 mentioned in #59 is not an option for me and I am hesitant to implement a trait from an external crate.

Problem compiling in OS X

Hi!

Im trying to compile a simple example in OS X but I get an error.
These are my specs:

$ rustc --version
rustc 1.8.0 (db2939409 2016-04-11)

$ cargo --version
cargo 0.9.0-nightly (8fc3fd8 2016-02-29)

$  uname -a
Darwin mac-plant 15.0.0 Darwin Kernel Version 15.0.0: Wed Aug 26 16:57:32 PDT 2015; root:xnu-3247.1.106~1/RELEASE_X86_64 x86_64

And this is an error

mac-plant ➜  mysqlrust git:(master) ✗ cargo run
    Updating registry `https://github.com/rust-lang/crates.io-index`
   Compiling mysql v7.1.2
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/io.rs:293:26: 293:47 error: failed to resolve. Could not find `net` in `std::os::unix` [E0433]
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/io.rs:293     UnixStream(BufStream<unix::net::UnixStream>),
                                                                                                              ^~~~~~~~~~~~~~~~~~~~~
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/io.rs:293:26: 293:47 help: run `rustc --explain E0433` to see a detailed explanation
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/io.rs:293:26: 293:47 error: type name `unix::net::UnixStream` is undefined or not in scope [E0412]
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/io.rs:293     UnixStream(BufStream<unix::net::UnixStream>),
                                                                                                              ^~~~~~~~~~~~~~~~~~~~~
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/io.rs:293:26: 293:47 help: run `rustc --explain E0412` to see a detailed explanation
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/io.rs:293:26: 293:47 help: no candidates by the name of `UnixStream` found in your project; maybe you misspelled the name or forgot to import an external crate?
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/conn/mod.rs:1107:19: 1107:49 error: failed to resolve. Could not find `net` in `std::os::unix` [E0433]
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/conn/mod.rs:1107             match unix::net::UnixStream::connect(unix_addr) {
                                                                                                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/conn/mod.rs:1107:19: 1107:49 help: run `rustc --explain E0433` to see a detailed explanation
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/conn/mod.rs:1107:19: 1107:49 error: unresolved name `unix::net::UnixStream::connect` [E0425]
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/conn/mod.rs:1107             match unix::net::UnixStream::connect(unix_addr) {
                                                                                                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/conn/mod.rs:1107:19: 1107:49 help: run `rustc --explain E0425` to see a detailed explanation
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/conn/mod.rs:1109:26: 1109:87 error: the type of this value must be known in this context
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/conn/mod.rs:1109                     try!(stream.set_read_timeout(self.opts.get_read_timeout().clone()));
                                                                                                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/conn/mod.rs:1109:21: 1109:89 note: in this expansion of try! (defined in <std macros>)
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/conn/mod.rs:1117:71: 1117:79 error: the type of this value must be known in this context
/Users/flp/.cargo/registry/src/github.com-88ac128001ac3a9a/mysql-7.1.2/src/conn/mod.rs:1117                     Err(DriverError(CouldNotConnect(Some((addr, desc, e.kind())))))
                                                                                                                                                                  ^~~~~~~~
error: aborting due to 2 previous errors
Could not compile `mysql`.

Thanks a lot!

PS: I'm just playing with it so no hurry or pressure, but I just wanted to do this minimal contribution to the lib.

Purpose of '' in into_str()

What exactly is the reason '' are added around strings into_str()? I'm storing names in a table, and essentially end up having to slice every single one to remove the apostrophes. Or is there some other preferred way of getting strings when parsing a row, which doesn't add the apostrophes?

why mut?

All the methods of MyPooledConn , need pass a mut self to, if only borrow, it'll be better to integrate between r2d2, and rustorm.

Typos

Just some quick fixes. IsolationLevel has typos, and some of the documentation has typos. Thanks!

Need more convenient methods of Value

After executing a normal query pool.query("SELECT * FROM users");, the values I get is all Value::Bytes(...), even if the real db field's type is int, and when i try to call value.get_int() it panics.

How about change Value::get_int()'s behavior, helping user convert to int from Value::Bytes()?

Use native-tls instead of openssl

The openssl library is a PITA to build on windows, the native-tls library uses the host's tls implementation (schannel on windows, security-framework on osx and openssl on other platforms)

Wrong implementation of from_value called

I have the following code to run a SELECT query and map the result to a struct vec:

#[derive(Debug, PartialEq, Eq)]
struct Terminal {
    did: u64,
    tid: String,
    rn_cd: u32,
    rn_a: u32,
}

pool.prep_exec("SELECT d.id, d_v.tid \
    FROM d_v \
    INNER JOIN d on d_v.o_d_id = d.id \
    WHERE d_v.t_is_i = 1 \
    AND d.active = 1", ())
.map(|result| {
    result.map(|x| x.unwrap()).map(|mut row| {
        Terminal {
            did: from_value::<u64>(row.pop().unwrap()),
            tid: from_value::<String>(row.pop().unwrap()),
            rn_cn: 0,
            rn_a: 0,
        }
    }).collect()
}).unwrap();

When I run this, the program panics with Error retrieving String from value. If I change the first line of the query to SELECT CONVERT(d.id, CHAR) as id, d_v.tid \, then this doesn't happen and the program runs fine.

So my question is, why does it appear that the String implementation of from_value is being called for the first column, when I'm explicitly calling the u64 implementation with from_value::<u64>()?

compile error with rustc 0.13.0-nightly (40b244973 2014-10-14 23:22:20 +0000)

src/scramble/mod.rs:12:53: 12:63 error: type collections::vec::Vec<u8> does not implement any method in scope named into_vec
src/scramble/mod.rs:12 .chain(double_sha_pass.into_vec().into_iter())
^~~~~~~~~~

rustc --version
rustc 0.13.0-nightly (40b244973 2014-10-14 23:22:20 +0000)

cargo --version
cargo 0.0.1-pre-nightly (6ad622f 2014-10-14 23:44:54 +0000)

How to fetch datetime

Please show an example how to fetch datetime field. What type I should use in structure declaration?

Implement ToRow for T where T: IntoValue

I was trying to use a single value as the argument to prep_exec, and I was surprised when I couldn't pass the value val or (val) to the function. I had to pass (val,), which makes sense in retrospect, but I don't see why ToRow couldn't just be implemented for the type T directly.

How to work with dynamic rows / rows that i do not know

Is there a way to use mysql::from_row(row) for tables that i do not know?
Basically i want to show whats in a certain database, without knowing the structure and number of columns it has. (i therefore can not implement from_row for my struct)

Reduce features list

I want to discuss current features state of the crate and future improvements.

@blackbeam You proposed to join pipe with socket feature there #71 . It's first possible and necessary strategy. But what about to remove features at all? I see we haven't strong reasons to keep and maintain features list.

What kind of simplification strategy we choose?

crates.io not up to date?

I can compile master but can't use mysql in another project using crates.io?

src/lib.rs:50:1: 50:27 error: can't find crate for `regex_macros`

Obtaining chunked results

I can't figure out how to get results grouped into chunks of specified size. Is it possible without implementing it myself?

Relicense under dual MIT/Apache-2.0

This issue was automatically generated. Feel free to close without ceremony if
you do not agree with re-licensing or if it is not possible for other reasons.
Respond to @cmr with any questions or concerns, or pop over to
#rust-offtopic on IRC to discuss.

You're receiving this because someone (perhaps the project maintainer)
published a crates.io package with the license as "MIT" xor "Apache-2.0" and
the repository field pointing here.

TL;DR the Rust ecosystem is largely Apache-2.0. Being available under that
license is good for interoperation. The MIT license as an add-on can be nice
for GPLv2 projects to use your code.

Why?

The MIT license requires reproducing countless copies of the same copyright
header with different names in the copyright field, for every MIT library in
use. The Apache license does not have this drawback. However, this is not the
primary motivation for me creating these issues. The Apache license also has
protections from patent trolls and an explicit contribution licensing clause.
However, the Apache license is incompatible with GPLv2. This is why Rust is
dual-licensed as MIT/Apache (the "primary" license being Apache, MIT only for
GPLv2 compat), and doing so would be wise for this project. This also makes
this crate suitable for inclusion and unrestricted sharing in the Rust
standard distribution and other projects using dual MIT/Apache, such as my
personal ulterior motive, the Robigalia project.

Some ask, "Does this really apply to binary redistributions? Does MIT really
require reproducing the whole thing?" I'm not a lawyer, and I can't give legal
advice, but some Google Android apps include open source attributions using
this interpretation. Others also agree with
it
.
But, again, the copyright notice redistribution is not the primary motivation
for the dual-licensing. It's stronger protections to licensees and better
interoperation with the wider Rust ecosystem.

How?

To do this, get explicit approval from each contributor of copyrightable work
(as not all contributions qualify for copyright) and then add the following to
your README:

## License

Licensed under either of
 * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
 * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
additional terms or conditions.

and in your license headers, use the following boilerplate (based on that used in Rust):

// Copyright (c) 2016 rust-mysql-simple developers
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

Be sure to add the relevant LICENSE-{MIT,APACHE} files. You can copy these
from the Rust repo for a plain-text
version.

And don't forget to update the license metadata in your Cargo.toml to:

license = "MIT/Apache-2.0"

I'll be going through projects which agree to be relicensed and have approval
by the necessary contributors and doing this changes, so feel free to leave
the heavy lifting to me!

Contributor checkoff

To agree to relicensing, comment with :

I license past and future contributions under the dual MIT/Apache-2.0 license, allowing licensees to chose either at their option

Or, if you're a contributor, you can check the box in this repo next to your
name. My scripts will pick this exact phrase up and check your checkbox, but
I'll come through and manually review this issue later as well.

Batch execution

Hey I'm working on a SQL migration CLI (https://github.com/Keats/dbmigrate) and just started adding mysql support.

Is there a method to run several statements in one go?
Such as:

        self.pool.prep_exec("
            CREATE TABLE IF NOT EXISTS migrations_table(id INTEGER, current INTEGER);
            INSERT INTO migrations_table (id, current)
            SELECT 1, 0 FROM DUAL
            WHERE NOT EXISTS(SELECT * FROM migrations_table WHERE id = 1);
        ", ()).unwrap();

which currently fails with:

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INSERT INTO migrations_table (id, current)
            SELECT 1, 0 FROM DUAL
   ' at line 2', ../src/libcore/result.rs:738

(and obviously the SQL migration files would contain several statements)

The postgres crate provides batch_execute for that purpose

Some(NULL) Should Be Convert to None or Some Default Value

I can see see this being fairly useful unless of coarse I am missing some feature in the library which makes this easy already and I haven't found it.

The main issue I am facing is the when one does row.take on mysql row where the value is Some(NULL) it tries to bind it to some type be it a String or a i32 or whatever data type and will produce the error Could not retrieve <type> for value which makes sense from the code's perspective, but in terms of usability I don't think this makes sense. If I have a NULL it should be the rust's loose equivalent of a NULL, namely None. Of coarse if I am misunderstanding the why here please let me know.

How can I debug `Could not convert row to (T1 .. T7)`?

I'm quite new to Rust and slowly getting the hang of language, one of the things I'd like to do is number crunching with Rust (instead of using C++) but some of the data is in mysql database. As a first step I need to get all that data out, I have started tinkering with this library - but I get a run time error as mentioned in the subject. Digging into the code I'm not quite sure if I understand it, what is the usual cause for this issue? Any pointers would be greatly useful.

Is it only way to implement FromRow?

Hello. I have table with 18 columns, so I have to implement FromRow for my structure.

pub struct Contact {
    id: u64,
    fname: String,
    lname: String,
    address: String,
    address2: String,
    city: String,
    state: String,
    zip: String,
    email: String,
    phone: String,
    manager_id: u64,
    user_id: u64,
    location_1_id: String,
    location_2_id: String,
    location_3_id: String,
    position: String,
    facility_name: String,
    speciality: String
}

impl FromRow for Contact {
    fn from_row(row: Vec<Value>) -> Contact {
        FromRow::from_row_opt(row)
        .ok().expect("Could not convert row to (T1-T18)")
    }
    fn from_row_opt(mut row: Vec<Value>) -> Result<Contact, Vec<Value>> {
        let vs = (from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap()),
        from_value_opt(row.pop().unwrap())
        );
        match vs {
            (Ok(t18),Ok(t17),Ok(t16),Ok(t15),Ok(t14),Ok(t13),Ok(t12), Ok(t11), Ok(t10), Ok(t9), Ok(t8), Ok(t7), Ok(t6), Ok(t5), Ok(t4), Ok(t3), Ok(t2), Ok(t1)) =>  {
                Ok(Contact{id: t1,
                    fname: t2,
                    lname: t3,
                    address: t4,
                    address2: t5,
                    city: t6,
                    state: t7,
                    zip: t8,
                    email: t9,
                    phone: t10,
                    manager_id: t11,
                    user_id: t12,
                    location_1_id: t13,
                    location_2_id: t14,
                    location_3_id: t15,
                    position: t16,
                    facility_name:t17,
                    speciality: t18})
            },
            _ => {
                Err(row)
            }
        }
    }
}

Is it the only way? With all of these copy-pasted lines and unwraps?

4-byte utf-8 codepoints not supported

The utf8 charset encoding in MySQL is broken (it only supports up to 3-byte codepoints) and the "fixed" charset is called utf8mb4. By default this library seems to use utf8, which results in not being able to insert some characters into the database

Straightfoward way to run queries

There are many times where I just want to get one row, for queries like "select username where id=3".

It would be nice to have shortcut for that like:

let username = pool.get_row(query).unwrap()[0]

without writing 10 lines to get through all map calls, or writing that:

let number = pool.prep_exec("select 1", ()).unwrap().next().unwrap().unwrap().unwrap()[0].clone();

"Will not check or fix connection health."

After this change I have to stick to old version of this lib, where connection health was checked. Please consider to create some kind of flag in settings to use that boolean argument you introduced in private function _get_conn. Or, if it exists, please add information to docs.
Otherwise it will fail again by timeout after each night without visitors.

Bulk inserts are tricky to do efficiently

I had to implement some kind of batching myself to improve performance. This involved building up a query string over time, something like INSERT INTO table (fields...) VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?), ...

The query builder is not particularly well suited to this, and building up the query via string concatenation seems relatively inefficient. Also I could only support positional parameters, since the name parsing code is not exposed (which means I couldn't use Params::make_positional).

I don't know if there's a more efficient way to do this at the protocol level?

At the very least I think one or more of the following would be useful:

  • Provide some guidelines in the documentation for how to do this
  • Expose the named parameter parsing functionality to allow building up the batching functionality oneself
  • Add some new functionality to assist with this usecase

Benches

Is there any bench available?

In particular, I'd like to see if we could try changing the write_paquet part (for large paquets) to:

// ...
let mut writer = Vec::with_capacity(4); // could probably be out of the chunks loop
try!(writer.write_le_uint_n(chunk_len as u64, 3));
try!(writer.write_u8(seq_id));
// try!(writer.write_u8(chunk));
try!(self.write_all(&writer[..]));
// instead of copying the entire chunk into the writer, 
// we could write it directly to the stream
try!(self.write_all(chunk));
// ... 

On my test it seems to be better but I am really not certain.

Provide access to QueryResult columns

Just started using your library for simple mysql database client and cannot seem to find a way to get hold query result columns.

It would be quite simple feature, or you could just make it public same as with Column struct.

This comes handy when you have user provided query results.

docs site: invalid link from Pool::prepare to Conn::prepare

Link is here:

http://blackbeam.org/doc/mysql/struct.Pool.html#method.prepare

Will prepare statement. See Conn::prepare.

Conn::prepare links to http://blackbeam.org/doc/struct.Conn.html#method.prepare (note: missing mysql/ part), which just shows an nginx 404 error page.

Multiple Conn::prepare links on the Pool page are broken. Haven't checked any other pages for broken links. Not sure where to find the docs on GH, would have sent PR otherwise.

Problem with `select @@slow_query_log`

Hi!

Im trying to read if the mysql slow query log is enabled or not.
Here is the program and this is the repo:

#[macro_use]
extern crate mysql;

use mysql as my;
use mysql::value::from_row;

fn main() {
    let pool = my::Pool::new("mysql://root:root@localhost:3306").unwrap();
    let mut stmt = pool.prepare("SELECT ?").unwrap();

    for result in stmt.execute(("@@slow_query_log",)).unwrap() {
        let row = result.unwrap();
        let row_tuple = from_row::<String>(row);
        println!("TUPPLE ROW: {}", row_tuple);
    }
}

This program compiles and runs but it prints this

@@slow_query_log

Instead of the expected value which is 0 or 1.

Here is how that same query looks in mysql cli:

mysql> select @@slow_query_log;
+------------------+
| @@slow_query_log |
+------------------+
|               0 |
+------------------+
1 row in set (0.00 sec)

I suspect that the lib is only telling me about the first "row" in the previous example.

Any ideas?

Thanks a lot in advance!
Fran

Appropriate for use with libmysqld?

Howdy!

Before I were to get started attempting to add support for embedded mysql; is there any reason this wouldn't work with rust-mysql-simple?

And barring any technical problems; what about licensing if I wanted to link statically?

openssl depency seems broken

failed to run custom build command for openssl-sys v0.6.0  
fatal error: openssl/hmac.h: No such file or directory`
panicked at 'explicit panic' .. src/lib.rs:380

With dault-features = false it's working.

Opened for further upgrading to a new lib version & just in case sb other also receives this error.

How to fetch 1 value

Hello.
Please help me to understand how to fetch 1 value. I'm trying:

    let mut q_check = match pool.prepare(r"SELECT id FROM user WHERE email = ? LIMIT 1") {
        Err(_) => return None,
        Ok(st1) => st1
    };
    let q_check_executed = q_check.execute((email,));
    match q_check_executed {
        Ok(check_result) => {
            match check_result.next() {
                Some(Ok(id)) => if id>0 {return None},
                None => ()
            }
        },
        Err(_) => return None
    }

I understand Some(Ok(id)) => if id>0 {return None}, is wrong, but I don't know how to do it - I'm newbie in Rust.

max_allowed_packet does not take into account packet header

If you send packets of exactly max_allowed_packet, the write_packet function will accept it, but the server will close the connection because the packet is too large.

This can be tested by using LOAD DATA LOCAL INFILE (without a handler) for any sufficiently large file (twice max_allowed_packet should do it).

error: unresolved import `std::os::unix`. There is no `unix` in `std::os` [E0432]

C:\Users_.cargo\bin\cargo.exe run --bin rust
Compiling openssl v0.7.14
Compiling openssl-sys-extras v0.7.14
Compiling mysql v7.1.2
C:\Users_.cargo\registry\src\github.com-1ecc6299db9ec823\mysql-7.1.2\src\io.rs:24:5: 24:26 error: unresolved import std::os::unix. There is no unix in std::os [E0432]
C:\Users_.cargo\registry\src\github.com-1ecc6299db9ec823\mysql-7.1.2\src\io.rs:24 use std::os::unix as unix;
^~~~~~~~~~~~~~~~~~~~~
C:\Users_.cargo\registry\src\github.com-1ecc6299db9ec823\mysql-7.1.2\src\io.rs:24:5: 24:26 help: run rustc --explain E0432 to see a detailed explanation
C:\Users_.cargo\registry\src\github.com-1ecc6299db9ec823\mysql-7.1.2\src\conn/mod.rs:64:5: 64:18 error: unresolved import std::os::unix. There is no unix in std::os [E0432]
C:\Users_.cargo\registry\src\github.com-1ecc6299db9ec823\mysql-7.1.2\src\conn/mod.rs:64 use std::os::unix;
^~~~~~~~~~~~~
C:\Users_.cargo\registry\src\github.com-1ecc6299db9ec823\mysql-7.1.2\src\conn/mod.rs:64:5: 64:18 help: run rustc --explain E0432 to see a detailed explanation
error: aborting due to 2 previous errors
error: Could not compile mysql.

To learn more, run the command again with --verbose.

Process finished with exit code 101

coordinated openssl version?

So, when one project uses a crates version of openssl and another uses a git version, the build using both projects is broken. When one project uses an upstream version and another uses a fixed version (06edff7), the build using both projects will be broken very soon.

How do we coordinate this?

rust-lang/cargo#1006 will tell us about the issue but it's little help if the issue will resurface every time somebody updates the version in one of the projects.

I'm going to open this bug in multiple projects:
hyperium/hyper#175
sfackler/rust-postgres#87

Pool Performance

Hello,

i am using mysql-simple in a batch job, i've to run such a query around 50.000.000 times:

i use the default pool:

let pool = mysql::Pool::new(connection_string).unwrap();

and this code to run the query:

let mut result = match pool.prep_exec(
    &format!("SELECT x.x_id from {}.page where x.title = :title and x.foo = 0 LIMIT 1", database),
    params!{"title" => (Value::from(title))}
) {
    Ok(r) => r,
    Err(e) => { panic!("");}
};

i tryed to run this query in multiple threads (binary and the mysql server most time dont hit 30% cpu usage).
On my local pc the throughput don't increase (or change) if i run the same query using multiple threads. i also tryed to obfuscate the query to prevent caching (using a SELECT ... and [random] = [random]).

the only real difference i can see by running multiple threads is that on osx kernel_thread starts to consume up to 100% cpu.

can you give me a hint how i can increase the load to the mysql server?

Numbers not supported in named params

A named parameter such as :description2_id will get parsed as ?2_id because the name param parser stops as soon as it hits a number. To be consistent with variable naming, it should allow numbers as well as letters after the first character.

Wrong error for: xxx.xxx.xxx.xxx is not allowed to connect to this MySQL server

When connecting to a server when there is the connection is not allowed, the library comes up with a DriverError::CouldNotParseVersion.

The variable ver_str contains the error message "j\u{4}Host \'192.168.15.250\' is not allowed to connect to this MySQL server. Adding a simple println!("{:?}", &ver_str); proved that.

So I guess there should be another error detection in an earlier stage.

Connect timeout

Please add a way to define connection timeout, so that in case of db not responsing, an error is returned. Currently it just hangs forever.

Please remove unsafe code

In source code I found just 1 kind of unsafe usage - FromRow implementation. I tried to remove it (along with mem::forget) and code compiles successfully. Please consider to remove this part - Rust will clean up memory itself, I believe.

Named placeholders

If it's not too difficult, please consider to add named placeholders.
example:

SELECT  id FROM location WHERE
facility_name LIKE :search 
OR address LIKE :search
OR address2 LIKE :search
OR city LIKE :search
OR state LIKE :search
OR zip LIKE :search
OR rep_fname LIKE :search

LLVM Error

Compiling mysql v0.3.1
LLVM ERROR: expected relocatable expression
Could not compile mysql.

This one is new as of when I updated Rust 2 hours ago. Should this be reported upstream to the Rust team?

DateTime parsing is broken

Issue:

MySQL DATE allows for 0th day of the month. While chrono::NaiveDate does not.

To Recreate:

MySQL Packet: https://s21.postimg.io/5wuv01pzb/found_you.png

The field Value::Bytes([50, 48, 49, 54, 45, 48, 55, 45, 48, 48]) (2016-07-00) cannot be parsed to a chrono::NaiveDate. Nothing in rust-mysql-simple prevents this, the issue is chrono::NaiveDate::from_ymd_opt(3) returns Option::None with the arguments 2016,7,0. It'll return a date out of range issue.

Solution:

I've created a pull request to solve this issue

#65

Pooled connections are dying after timeout

I use AWS RDS, where default timeout connection is 8 hours. After 8 hours one of pooled connections is dying because of this limit and ping() can't detect it (or reset() doesn't work as expected with really dead connections).

URL ParseError { invalid port number } if password contains "?"

let pool = mysql::Pool::new("mysql://user:password?@localhost:3306/test").unwrap();

throws the following error:

thread 'main' panicked at 'URL ParseError { invalid port number }', 
...\mysql-7.1.2\src\conn\opts.rs:580

It doesn't matter at which position the "?" is, it will always throw the error.
My specs:

D:\Rust_Training>rustc --version
rustc 1.12.1 (d4f39402a 2016-10-19)

D:\Rust_Training>cargo --version
cargo 0.13.0-nightly (109cb7c 2016-08-19)

 Fresh mysql v7.1.2

should_parse_large_binary_result and should_parse_large_text_result tests fail

With rustc 1.6.0-nightly (1a2eaffb6 2015-10-31) and either mariadb 10.0.22 or 10.1.8 on Ubuntu 14.04, running the rust-mysql-simple tests with

MYSQL_SERVER_PASS= cargo test --no-default-features --features socket > out 2>&1

shows two failures:

     Running target/debug/mysql-4000b4d1c4d19367

running 38 tests
test conn::test::my_conn::should_connect ... ok
test conn::test::my_conn::should_connect_with_database ... ok
test conn::test::my_conn::should_execute_queryes_and_parse_results ... ok
test conn::test::my_conn::should_execute_statements_and_parse_results ... ok
test conn::pool::test::pool::should_start_transaction_on_MyPooledConn ... ok
test conn::pool::test::pool::should_execute_queryes_on_MyPooledConn ... ok
test conn::test::my_conn::should_handle_multi_resultset ... ok
test conn::pool::test::pool::should_start_transaction_on_MyPool ... ok
test packet::test::should_parse_EOF_packet ... ok
test packet::test::should_parse_Error_packet ... ok
test packet::test::should_parse_OK_packet ... ok
test conn::test::my_conn::should_reset_connection ... ok
test scramble::sha1::test::should_compute_sha1_hash ... ok
test conn::test::my_conn::should_start_commit_and_rollback_transactions ... ok
test packet::test::should_parse_handshake_packet ... ok
test scramble::test::should_compute_scrambled_password ... ok
test value::test::from_value::should_convert_Bytes_to_Duration ... ok
test value::test::from_row::should_return_error_if_could_not_convert ... ok
test value::test::from_row::should_convert_to_tuples ... ok
test conn::pool::test::pool::should_execute_statements_on_MyPooledConn ... ok
test value::test::from_value::should_convert_Bytes_to_Timespec ... ok
test value::test::from_value::should_convert_signed_to_unsigned ... ok
test value::test::from_value::should_not_convert_negative_to_unsigned ... ok
test value::test::into_str::should_convert_Bytes_to_mysql_string ... ok
test value::test::into_str::should_convert_Float_to_mysql_string ... ok
test value::test::into_str::should_convert_Date_to_mysql_string ... ok
test value::test::into_str::should_convert_NULL_to_mysql_string ... ok
test value::test::into_str::should_convert_Time_to_mysql_string ... ok
test value::test::into_str::should_convert_Int_to_mysql_string ... ok
test conn::pool::test::pool::should_execute_statements_on_MyPool ... ok
test value::test::into_str::should_convert_UInt_to_mysql_string ... ok
test value::test::into_str::should_use_hex_literals_for_binary_Bytes ... ok
test value::test::into_str::should_escape_specials_while_converting_Bytes ... ok
test value::test::to_value::should_convert_Time_to_Date ... ok
test value::test::to_value::should_convert_Duration_to_Time ... ok
test conn::test::my_conn::should_handle_LOCAL_INFILE ... ok
test conn::test::my_conn::should_parse_large_binary_result ... FAILED
test conn::test::my_conn::should_parse_large_text_result ... FAILED

failures:

---- conn::test::my_conn::should_parse_large_binary_result stdout ----
    thread 'conn::test::my_conn::should_parse_large_binary_result' panicked at 'assertion failed: `(left == right)` (left: `[NULL]`, right: `[Bytes([65, 65, 65, 65, 65, 65, 65, 6

---- conn::test::my_conn::should_parse_large_text_result stdout ----
    thread 'conn::test::my_conn::should_parse_large_text_result' panicked at 'assertion failed: `(left == right)` (left: `[NULL]`, right: `[Bytes([65, 65, 65, 65, 65, 65, 65, 65,


failures:
    conn::test::my_conn::should_parse_large_binary_result
    conn::test::my_conn::should_parse_large_text_result

test result: FAILED. 36 passed; 2 failed; 0 ignored; 0 measured

I've truncated the 65, 65... lines because they are very long.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.