Reputation: 33
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
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