Sebastien Durand
Sebastien Durand

Reputation: 33

How to pass table name as a variable to execute() with the postgres crate?

I'm new to Rust and I was trying to play with the postgres crate. I was able to create a table by hardcoding the table name, but I'm always having code going panic when trying to pass the table name from a variable.

rustc --version 1.36.0
cargo --version 1.36.0
postgres = "0.15"
fn main() {
  let conn = Connection::connect("postgresql://postgres:postgres@localhost/db1",
                                    TlsMode::None).unwrap();

  let tname = "message";
  conn.execute("CREATE TABLE IF NOT EXISTS $1 (
                    id              SERIAL PRIMARY KEY,
                    title           VARCHAR NOT NULL,
                    body            VARCHAR,
                )", &[&tname]).ok().expect("Table message creation failed");
thread 'main' panicked at 'Table message creation failed', src/libcore/option.rs:1036:5
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
             at src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:39
   1: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:71
   2: std::panicking::default_hook::{{closure}}
             at src/libstd/sys_common/backtrace.rs:59
             at src/libstd/panicking.rs:197
   3: std::panicking::default_hook
             at src/libstd/panicking.rs:211
   4: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:474
   5: std::panicking::continue_panic_fmt
             at src/libstd/panicking.rs:381
   6: rust_begin_unwind
             at src/libstd/panicking.rs:308
   7: core::panicking::panic_fmt
             at src/libcore/panicking.rs:85
   8: core::option::expect_failed
             at src/libcore/option.rs:1036
   9: core::option::Option<T>::expect
             at /rustc/a53f9df32fbb0b5f4382caaad8f1a46f36ea887c/src/libcore/option.rs:314
  10: rustdb::main
             at ./main.rs:27
  11: std::rt::lang_start::{{closure}}
             at /rustc/a53f9df32fbb0b5f4382caaad8f1a46f36ea887c/src/libstd/rt.rs:64
  12: std::panicking::try::do_call
             at src/libstd/rt.rs:49
             at src/libstd/panicking.rs:293
  13: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:85
  14: std::rt::lang_start_internal
             at src/libstd/panicking.rs:272
             at src/libstd/panic.rs:394
             at src/libstd/rt.rs:48
  15: std::rt::lang_start
             at /rustc/a53f9df32fbb0b5f4382caaad8f1a46f36ea887c/src/libstd/rt.rs:64
  16: main
  17: __libc_start_main
  18: _start

Upvotes: 3

Views: 1393

Answers (1)

harmic
harmic

Reputation: 30587

You cannot use placeholders (eg. $1) to substitute a table name into a query.

One of the functions of placeholders is to allow a query to be prepared once, then executed multiple times. This saves on the overhead of planning the query each time you want to use it, which can be substantial. However it would not be possible to plan the query if the database did not even know which table was being queried.

If you need to dynamically insert the table name at runtime, you will need to do that in rust before passing the SQL to the database:

let sql = format!("CREATE TABLE IF NOT EXISTS {} (
                    id              SERIAL PRIMARY KEY,
                    title           VARCHAR NOT NULL,
                    body            VARCHAR,
                )", tname);

If the table name is being passed in from user input, don't forget to guard against SQL injection by validating it beforehand.

Also note that the panic is due to the use of .ok().expect(....).

ok() will take the result of executing the SQL and convert it into an Option. If the result was an error it will be discarded, so you never get to see the error message which would probably have helped you diagnose the problem. Result implements expect directly, with the advantage that instead of discarding the error, it will display it as part of the Panic. So, you would be better off with:

conn.execute(sql, &[] as &[String]).expect("Failed creating table");

However, if there is a realistic chance that the SQL statement is going to fail you would be better off to check the result and handle it more gracefully than crashing the program.

Upvotes: 6

Related Questions