0

It is shown in this answer in response to its question how string replacement in Python works.

I believe it works like

string toSql(string table, string field, string value)
{
    string out;
    return out = "INSERT INTO %s (%s) VALUES (%s)" % (table,field,value);
}

Is there a similar way of doing this in C++ without file i/o?

I am trying to use this to form a query for a SQLite database.

Edit

I am avoiding using external libraries. ex Boost

Also, the inputs are supplied from the program, not the user. So i do not believe i would run into injection vulnerabilities

Community
  • 1
  • 1
Slvrfn
  • 644
  • 2
  • 7
  • 17
  • You can look at http://www.boost.org/doc/libs/1_55_0/libs/format/doc/format.html – ForEveR Jul 15 '15 at 13:23
  • 4
    1. C invented string formatting; 2. Don't use string manipulation to create database queries. That way lies hard to debug bugs and sql injection vulnerabilities. Use prepared statements and a library which manages query creation. – Marcin Jul 15 '15 at 13:26
  • Or you should look at your SQL library for the prepare statement. The prepare/execute cycle will allow you to bind parameters safely. – Martin York Jul 15 '15 at 13:53

2 Answers2

3

In the interest of answering the asked question, the idiomatic C++ way to do this is with std::ostringstream. Note that this stream class is backed by memory, not a file on disk.

(There is also the snprintf() option, which looks closer to Python string formatting but has a C-style interface and shouldn't be used from modern C++ without a good reason. If you are writing C++ then write C++, not C.)

std::string toSql(
    std::string const & table,
    std::string const & field,
    std::string const & value
) {
    std::ostringstream s;

    s << "INSERT INTO " << table
      << " (" << field << ") VALUES (" << value << ")";

    return s.str();
}

However, beware that this can open your program to SQL injection attacks if any of the arguments are unsanitized user input. You should instead use a prepared statement (sqlite3_prepare() followed by sqlite3_bind_...()) to bind value into the statement -- but you will still have to build the string from the table and field arguments since database object names can't be bound this way.


You can use prepared statements "the C++ way" like this (C++11 or better required for std::unique_ptr):

#include <memory>
#include <string>
#include <sqlite3.h>

// Deleter functor to properly sqlite3_finalize() statements when we
// are done with them.
struct sqlite3_stmt_deleter
{
    void operator()(sqlite3_stmt * p) const {
        sqlite3_finalize(p);
    }
};

// Type alias for a std::unique_ptr that uses the above functor to
// clean up statements.
using sqlite3_prepared_stmt = std::unique_ptr<sqlite3_stmt, sqlite3_stmt_deleter>;

sqlite3_prepared_stmt prepare(sqlite3 * db, std::string const & sql)
{
    sqlite3_stmt * stmt = nullptr;

    // Note that we don't allow the caller to see any error information. A
    // proper wrapper will want to throw if the return isn't SQLITE3_OK.
    sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr);

    return sqlite3_prepared_stmt(stmt);
}

void example() {
    auto insert_stmt = prepare(
        your_db,
        "INSERT INTO foo (bar) VALUES (?)");

    std::string value{"baz"};

    // Bind value to the ? in the prepared statement
    sqlite3_bind_text(insert_stmt.get(), 1, value.c_str(), -1, SQLITE_TRANSIENT);

    // Execute statement.
    sqlite3_step(insert_stmt.get());

    // Reset statement so it can be used again with bind/step.
    sqlite3_reset(insert_stmt.get());

    // std::unique_ptr destructor will call sqlite3_finalize() for us.
}

Using this code, you can store a sqlite3_prepared_stmt somewhere and reuse it.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • Great! This seems like exactly what I was looking for. Are there any benefits to using things like `sqlite3_prepare()` ... `sqlite3_bind()` etc? I ask because I am not the most familier with these, and have been just using `sqlite3_exec()` – Slvrfn Jul 15 '15 at 13:45
  • 2
    @Slvrfn User input that is bound with the `sqlite3_bind` family of functions cannot cause SQL injection. Additionally, you can prepare a statement once and then execute it multiple times with different bound data, if you have a query that will be repeatedly executed with different data. This saves time because it doesn't have to parse the same SQL text multiple times, nor does it have to parse out your data as part of the query. Binding the data gives it to SQLite out of band. – cdhowie Jul 15 '15 at 13:47
  • can you provide an example of how to do this, here or externally? – Slvrfn Jul 15 '15 at 15:48
  • 1
    @Slvrfn I added such an example to my answer. – cdhowie Jul 15 '15 at 17:52
0

Take a look at the printf family of functions.

string toSql( string table, string field, string value )
{
  char buffer[ 100 ];
  size_t length = sprintf( buffer, "INSERT INTO %s (%s) VALUES (%s)", table.data( ), field.data( ), value.data( ) );

  if ( lenght < 0 )
  {
      //increase buffer size and try again.
  }

  printf("SQL Query = '%s'\n", buffer );

  return buffer
}
Ben Crowhurst
  • 8,204
  • 6
  • 48
  • 78
  • Downvoter -- care to share why the downvote? This is perfectly valid. C-style, but valid nonetheless. – erip Jul 15 '15 at 13:49
  • 2
    Two criticisms: use of `sprintf()` makes this vulnerable to a stack smash attack (see `snprintf()` -- recommending `sprintf()`, especially to someone who doesn't know what they are doing with it, is a terrible idea), and C++ code should be C++, not C. "Technically valid C++" doesn't mean "good, idiomatic C++." (My downvote is related to the security problem, not the C/C++ problem.) – cdhowie Jul 15 '15 at 13:50
  • 1
    As an additional comment on the C/C++ issue, note that the proper usage of C++ features like `std::ostringstream` prevents one from having to know or care how big the formatting buffer needs to be. The idiomatic C++ way will not only be safer but easier to get right. – cdhowie Jul 15 '15 at 13:52