3. Tutorial

This tutorial is meant to give you a jump start in using MySQL++. While it is a very complicated and powerful library, it's possible to make quite functional programs without tapping but a fraction of its power. This section will introduce you to the most useful fraction.

This tutorial assumes you know C++ fairly well, in particuler the Standard Template Library (STL) and exceptions.

3.1. Running the Examples

All of the examples are complete running programs. They may or may not be built for you already, depending on how you installed the library.

If you installed MySQL++ from the source tarball on a Unixy system, the examples should have been built along with the library. If not, simply go into the examples directory and type make.

If you installed the library via RPM, the examples are in the mysql++-devel RPM. After installing that, the examples are in /usr/src/mysql++/examples. To build them, go into that directory and type make -f Makefile.simple. See the file /usr/share/doc/mysql++-devel*/README.examples for more details.

If you are on a Windows system, the build process for the library should have built the examples as well. Where the programs are depends on which compiler you're using. There should be a README.* file in the distribution specific to your compiler with further instructions.

Once you have the examples building, you need to initialize the sample database by running the resetdb example. The usage of resetdb is as follows:

	resetdb [host [user [password [port]]]]

If you leave off host, localhost is assumed. If you leave off user, your current username is assumed. If you leave of the password, it is assumed that you don't need one. And if you leave off the port, it will use the standard MySQL port number.

The user you give resetdb needs to be an account with permission to create databases. Once the database is created you can use any account that has full permission to the sample database mysql_cpp_data.

You may also have to re-run resetdb after running some of the other examples, as they change the database.

3.2. The Basics

A simple example

The following example demonstrates how to open a connection, execute a simple query, and display the results. This is examples/simple1.cpp:

#include "util.h"

#include <mysql++.h>

#include <iostream>
#include <iomanip>

using namespace std;

int
main(int argc, char *argv[])
{
    // Wrap all MySQL++ interactions in one big try block, so any
    // errors are handled gracefully.
    try {
        // Connect to the sample database
        mysqlpp::Connection con(mysqlpp::use_exceptions);
        if (!connect_to_db(argc, argv, con)) {
            return 1;
        }

        // Create a query object that is bound to con.
        mysqlpp::Query query = con.query();

        // Print out the stock table
        print_stock_table(query);
    }
    catch (mysqlpp::BadQuery& er) {
        // handle any connection or query errors that may come up
        cerr << "Error: " << er.what() << endl;
        return -1;
    }
    catch (mysqlpp::BadConversion& er) {
        // handle bad conversions
        cerr << "Error: " << er.what() << "\"." << endl
            << "retrieved data size: " << er.retrieved
            << " actual data size: " << er.actual_size << endl;
        return -1;
    }
    catch (exception & er) {
        cerr << "Error: " << er.what() << endl;
        return -1;
    }

    return 0;
}

The example programs' utility module

If anything, the code above is "too" simple, because all it really does is drive some functions in examples/util.cpp. Most of the examples link to this module, because it contains code that would otherwise have to be repeated in these examples.

Here's a simplified version of the utility module:

#include "util.h"

#include <iostream>
#include <iomanip>
#include <stdlib.h>

using namespace std;

const char* kpcSampleDatabase = "mysql_cpp_data";


//// print_stock_header ////////////////////////////////////////////////
// Display a header suitable for use with print_stock_rows().

void
print_stock_header(int rows)
{
    cout << "Records found: " << rows << endl << endl;
    cout.setf(ios::left);
    cout << setw(21) << "Item" <<
            setw(10) << "Num" <<
            setw(10) << "Weight" <<
            setw(10) << "Price" <<
            "Date" << endl << endl;
}


//// print_stock_row ///////////////////////////////////////////////////
// Print out a row of data from the stock table, in a format
// compatbile with the header printed out in the previous function.

void
print_stock_row(const char* item, mysqlpp::longlong num, double weight,
        double price, mysqlpp::Date date)
{
    // Output first column, the item string.
    cout << setw(20) << item << ' ' <<
            setw(9) << num << ' ' <<
            setw(9) << weight << ' ' <<
            setw(9) << price << ' ' <<
            date << endl;
}


//// print_stock_rows //////////////////////////////////////////////////
// Print out a number of rows from the example 'stock' table.

void
print_stock_rows(mysqlpp::Result& res)
{
    print_stock_header(res.size());

    // Use the Result class's read-only random access iterator to walk
    // through the query results.
    mysqlpp::Result::iterator i;
    for (i = res.begin(); i != res.end(); ++i) {
        // Convert the Result iterator into a Row object, for easier
        // access.
        mysqlpp::Row row(*i);

        // Notice that you can use either the column index or name to
        // retrieve the data.  Also notice that we do no explicit
        // conversions to match print_stock_row()'s parameter types:
        // Row elements are ColData strings, so they auto-convert to
        // any standard C++ type.
        print_stock_row(row[0], row[1], row.lookup_by_name("weight"),
                row[3], row[4]);
    }
}


//// print_stock_table /////////////////////////////////////////////////
// Print the entire contents of the example 'stock' table.

void
print_stock_table(mysqlpp::Query& query)
{
    // You must reset the query object when re-using it.
    query.reset();

    // You can write to the query object like you would any ostream.
    query << "select * from stock";

    // Show the query string.  If you do this, you have to do it before
    // you execute() or store() or use() it.
    cout << "Query: " << query.preview() << endl;

    // Execute the query and display the result set.
    mysqlpp::Result res = query.store();
    print_stock_rows(res);
}


//// connect_to_db /////////////////////////////////////////////////////
// Establishes a connection to a MySQL database server, optionally
// attaching to database kdb.  This is basically a command-line parser
// for the examples, since the example programs' arguments give us the
// information we need to establish the server connection.

bool
connect_to_db(int argc, char *argv[], mysqlpp::Connection& con,
        const char *kdb)
{
    if (argc < 1) {
        cerr << "Bad argument count: " << argc << '!' << endl;
        return false;
    }

    if ((argc > 1) && (argv[1][0] == '-')) {
        cout << "usage: " << argv[0] <<
                " [host] [user] [password] [port]" << endl;
        cout << endl << "\tConnects to database ";
        if (kdb) {
            cout << '"' << kdb << '"';
        }
        else {
            cout << "server";
        }
        cout << " on localhost using your user" << endl;
        cout << "\tname and no password by default." << endl << endl;
        return false;
    }

    if (!kdb) {
        kdb = kpcSampleDatabase;
    }

    bool success = false;
    if (argc == 1) {
        success = con.connect(kdb);
    }
    else if (argc == 2) {
        success = con.connect(kdb, argv[1]);
    }
    else if (argc == 3) {
        success = con.connect(kdb, argv[1], argv[2]);
    }
    else if (argc == 4) {
        success = con.connect(kdb, argv[1], argv[2], argv[3]);
    }
    else if (argc >= 5) {
        success = con.real_connect(kdb, argv[1], argv[2], argv[3],
                atoi(argv[4]));
    }

    if (!success) {
        cerr << "Database connection failed." << endl << endl;
    }

    return success;
}

The only simplication done to the code is to remove the Unicode conversion stuff. That is covered in a later chapter, Using Unicode with MySQL++.

Notes on exceptions

Aside from driving the connect_to_db() and print_stock_table() functions in the utility module, the simple1 example mainly consists of exception handlers. This block of code will become familiar to you in the following examples, because it's identical for all of them.

A few notes about exceptions:

  1. Exceptions are the default way for MySQL++ to signal errors, but some MySQL++ classes (Connection, Query, Result, ResUse, and Row) allow you to disable these exceptions. The methods vary, but some classes' constructors accept a flag to disable exceptions, some allow you to disable exceptions on a per-method-call basis, and some let you turn them on and off for the object with a method call.

  2. The state of the object's exception enabling flag is passed down to child objects. For example, if you create a Connection object with exceptions disabled and then call its query() method to get a Query object, that object will also have exceptions disabled.

  3. The exception enabling flag is copied from object to object, so if you create a child object and then change the flag in the parent, the child object's exception enabling state is not changed.

  4. ColData will always throw an exception when it is unable to do a requested conversion. A bad conversion is defined as one where: a) not all the characters from the string have yet been processed, and b) the remaining characters are something other than whitespace, zeros (0), or periods (.). This means that an exception is thrown when ``1.25'' is converted into an int, but not when ``1.00'' is converted into an int. In the latter case, MySQL++ knows that it can safely throw away the fractional part.

  5. An out-of-bounds subscript when accessing a MySQL++ container may throw an exception. Whether it does or not depends on whether the underlying STL container throws exceptions in this situation, so it's compiler-dependent.

To see how the exceptions work, try creating an error. Some good things to try would be misspelling a table name or trying to cast a floating-point column value to an int.

Getting info about the fields

The following example demonstrates how to get some basic information about the fields, including the name of the field and the SQL type. This is examples/fieldinf1.cpp:

#include "util.h"

#include <mysql++.h>

#include <iostream>
#include <iomanip>

using namespace std;
using namespace mysqlpp;

int
main(int argc, char *argv[])
{
    try {
        Connection con(use_exceptions);
        if (!connect_to_db(argc, argv, con)) {
            return 1;
        }

        Query query = con.query();
        query << "select * from stock";
        cout << "Query: " << query.preview() << endl;

        Result res = query.store();
        cout << "Records Found: " << res.size() << endl << endl;

        cout << "Query Info:\n";
        cout.setf(ios::left);

        for (unsigned int i = 0; i < res.names().size(); i++) {
            cout << setw(2) << i
                    // this is the name of the field
                    << setw(15) << res.names(i).c_str()
                    // this is the SQL identifier name
                    // Result::types(unsigned int) returns a mysql_type_info which in many
                    // ways is like type_info except that it has additional sql type
                    // information in it. (with one of the methods being sql_name())
                    << setw(15) << res.types(i).sql_name()
                    // this is the C++ identifier name which most closely resembles
                    // the sql name (its is implementation defined and often not very readable)
                    << setw(20) << res.types(i).name()
                    << endl;
        }

        cout << endl;

        if (res.types(0) == typeid(string)) {
            // this is demonstrating how a mysql_type_info can be 
            // compared with a C++ type_info.
            cout << "Field 'item' is of an SQL type which most "
                    "closely resembles\nthe C++ string type\n";
        }

        if (res.types(1) == typeid(longlong)) {
            cout << "Field 'num' is of an SQL type which most "
                    "closely resembles\nC++ long long int type\n";
        }
        else if (res.types(1).base_type() == typeid(longlong)) {
            // you have to be careful as if it can be null the actual
            // type is Null<TYPE> not TYPE.  So you should always use
            // the base_type method to get at the underlying type.
            // If the type is not null than this base type would be
            // the same as its type.
            cout << "Field 'num' base type is of an SQL type which "
                    "most closely\nresembles the C++ long long int type\n";
        }
    }
    catch (BadQuery& er) {
        // Handle any connection or query errors
        cerr << "Error: " << er.what() << endl;
        return -1;
    }
    catch (BadConversion& er) {
        // Handle bad conversions
        cerr << "Error: " << er.what() << "\"." << endl <<
                "retrieved data size: " << er.retrieved <<
                " actual data size: " << er.actual_size << endl;
        return -1;
    }
    catch (exception& er) {
        // Catch-all for any other standard C++ exceptions
        cerr << "Error: " << er.what() << endl;
        return -1;
    }

    return 0;
}

3.3. Quoting and Escaping

SQL syntax often requires certain data to be quoted. Consider this query:

        SELECT * FROM stock WHERE item = 'Hotdog Buns' 

Because the string "Hotdog Buns" contains a space, it must be quoted. With MySQL++, you don't have to add these quote marks manually:

        string s = "Hotdog Buns";
        Query q = conn.query();
        q << "SELECT * FROM stock WHERE item = " << quote_only << s; 

That code produces the same query string as in the previous example. We used the MySQL++ quote_only manipulator, which causes single quotes to be added around the next item inserted into the stream. This works for various string types, for any type of data that can be converted to MySQL++'s ColData type, and for Specialized SQL Structures. (The next section introduces the SSQLS feature.)

Quoting is pretty simple, but SQL syntax also often requires that certain characters be "escaped". Imagine if the string in the previous example was "Frank's Brand Hotdog Buns" instead. The resulting query would be:

        SELECT * FROM stock WHERE item = 'Frank's Brand Hotdog Buns' 

That's not valid SQL syntax. The correct syntax is:

        SELECT * FROM stock WHERE item = 'Frank''s Brand Hotdog Buns' 

As you might expect, MySQL++ provides that feature, too, through its 'escape' manipulator. But here, we want both quoting and escaping. That brings us to the most widely useful manipulator:

        string s = "Hotdogs' Buns";
        Query q = conn.query();
        q << "SELECT * FROM stock WHERE item = " << quote << s; 

The regular quote manipulator both quotes strings, and escapes any characters that are special in SQL.

3.4. Specialized SQL Structures

Retrieving data

The next example introduces one of the most powerful features of MySQL++: Specialized SQL Structures (SSQLS). This is examples/custom1.cpp:

#include "util.h"

#include <mysql++.h>
#include <custom.h>

#include <iostream>
#include <iomanip>
#include <vector>

using namespace std;
using namespace mysqlpp;

// The following is calling a very complex macro which will create
// "struct stock", which has the member variables:
//
//   string item
//   ...
//   Date sdate
//
// plus methods to help populate the class from a MySQL row
// among other things that I'll get to in a later example.
sql_create_5(stock,
             1, 5,              // explained in the user manual
             string, item,
             longlong, num,
             double, weight,
             double, price,
             Date, sdate)

int
main(int argc, char *argv[])
{
    try {                       
        // Establish the connection to the database server.
        Connection con(use_exceptions);
        if (!connect_to_db(argc, argv, con)) {
            return 1;
        }

        // Retrieve the entire contents of the stock table, and store
        // the data in a vector of 'stock' SSQLS structures.
        Query query = con.query();
        query << "select * from stock";
        vector<stock> res;
        query.storein(res);

        // Display the result set
        print_stock_header(res.size());
        vector<stock>::iterator it;
        for (it = res.begin(); it != res.end(); ++it) {
            print_stock_row(it->item.c_str(), it->num, it->weight,
                    it->price, it->sdate);
        }
    }
    catch (BadQuery& er) {
        // Handle any connection or query errors
        cerr << "Error: " << er.what() << endl;
        return -1;
    }
    catch (BadConversion& er) {
        // Handle bad conversions
        cerr << "Error: " << er.what() << "\"." << endl <<
                "retrieved data size: " << er.retrieved <<
                " actual data size: " << er.actual_size << endl;
        return -1;
    }
    catch (exception& er) {
        // Catch-all for any other standard C++ exceptions
        cerr << "Error: " << er.what() << endl;
        return -1;
    }

    return 0;
}

As you can see, SSQLS is very powerful.

Adding data

SSQLS can also be used to add data to a table. This is examples/custom2.cpp:

#include "util.h"

#include <mysql++.h>
#include <custom.h>

#include <iostream>
#include <string>
#include <vector>

using namespace std;
using namespace mysqlpp;

sql_create_5(stock,
            1, 5,
            string, item,
            longlong, num,
            double, weight,
            double, price,
            Date, sdate)

int
main(int argc, char *argv[])
{
    try {
        // Establish the connection to the database server.
        Connection con(use_exceptions);
        if (!connect_to_db(argc, argv, con)) {
            return 1;
        }

        // Create and populate a stock object.  We could also have used
        // the set() member, which takes the same parameters as this
        // constructor.
        stock row("Hot Dogs", 100, 1.5, 1.75, "1998-09-25");

        // Form the query to insert the row into the stock table.
        Query query = con.query();
        query.insert(row);

        // Show the query about to be executed.
        cout << "Query: " << query.preview() << endl;

        // Execute the query.  We use execute() because INSERT doesn't
        // return a result set.
        query.execute();

        // Print the new table.
        print_stock_table(query);
    }
    catch (BadQuery& er) {
        // Handle any connection or query errors
        cerr << "Error: " << er.what() << endl;
        return -1;
    }
    catch (BadConversion& er) { 
        // Handle bad conversions
        cerr << "Error: " << er.what() << "\"." << endl <<
                "retrieved data size: " << er.retrieved <<
                " actual data size: " << er.actual_size << endl;
        return -1;
    }
    catch (exception& er) {
        // Catch-all for any other standard C++ exceptions
        cerr << "Error: " << er.what() << endl;
        return -1;
    }

    return 0;
}

That's all there is to it!

There is one subtlety: MySQL++ automatically quotes and escapes the data when building SQL queries using SSQLS structures. It's efficient, too: MySQL++ is smart enough to apply quoting and escaping only for those data types that actually require it.

Because this example modifies the sample database, you may want to run resetdb after running this program.

Modifying data

It almost as easy to modify data with SSQLS. This is examples/custom3.cpp:

#include "util.h"

#include <mysql++.h>
#include <custom.h>

#include <iostream>
#include <string>
#include <vector>

using namespace std;
using namespace mysqlpp;

sql_create_5(stock,
            1, 5,
            string, item,
            longlong, num,
            double, weight,
            double, price,
            Date, sdate)

int
main(int argc, char *argv[])
{
    try {
        // Establish the connection to the database server.
        Connection con(use_exceptions);
        if (!connect_to_db(argc, argv, con)) {
            return 1;
        }

        // Build a query to retrieve the stock item that has Unicode
        // characters encoded in UTF-8 form.
        Query query = con.query();
        query << "select * from stock where item = \"Nürnberger Brats\"";

        // Retrieve the row, throwing an exception if it fails.
        Result res = query.store();
        if (res.empty()) {
            throw BadQuery("UTF-8 bratwurst item not found in "
                    "table, run resetdb");
        }

        // Because there should only be one row in the result set,
        // there's no point in storing the result in an STL container.
        // We can store the first row directly into a stock structure
        // because one of an SSQLS's constructors takes a Row object.
        stock row = res[0];

        // Create a copy so that the replace query knows what the
        // original values are.
        stock orig_row = row;

        // Change the stock object's item to use only 7-bit ASCII, and
        // to deliberately be wider than normal column widths printed
        // by print_stock_table().
        row.item = "Nuerenberger Bratwurst";

        // Form the query to replace the row in the stock table.
        query.update(orig_row, row);

        // Show the query about to be executed.
        cout << "Query: " << query.preview() << endl;

        // Run the query with execute(), since UPDATE doesn't return a
        // result set.
        query.execute();

        // Print the new table contents.
        print_stock_table(query);
    }
    catch (BadQuery& er) {
        // Handle any connection or query errors
        cerr << "Error: " << er.what() << endl;
        return -1;
    }
    catch (BadConversion& er) {
        // Handle bad conversions
        cerr << "Error: " << er.what() << "\"." << endl <<
                "retrieved data size: " << er.retrieved <<
                " actual data size: " << er.actual_size << endl;
        return -1;
    }
    catch (exception& er) {
        // Catch-all for any other standard C++ exceptions
        cerr << "Error: " << er.what() << endl;
        return -1;
    }

    return 0;
}

When you run the example you will notice that in the WHERE clause only the 'item' field is checked for. This is because SSQLS also also less-than-comparable.

Don't forget to run resetdb after running the example.

Less-than-comparable

SSQLS structures can be sorted and stored in STL associative containers as demonstrated in the next example. This is custom4.cpp:

#include "util.h"

#include <mysql++.h>
#include <custom.h>

#include <iostream>
#include <iomanip>
#include <string>
#include <vector>

using namespace std;
using namespace mysqlpp;

sql_create_5(stock,
    1,  // This number is used to make a SSQLS less-than-comparable.
        // When comparing two SSQLS structures, the first N elements are
        // compared.  In this instance, we are saying that we only want
        // the first element ('item') to be used when comparing two
        // stock structures.

    5,  // Each SSQLS structure includes a number of constructors.  Some
        // of these are fixed in nature, but one of these will have this
        // number of arguments, one for each of the first N elements in
        // the structure; it is an initialization ctor.  Since N is the
        // same as the number of structure elements in this instance,
        // that ctor will be able to fully initialize the structure. This
        // behavior is not always wanted, however, so the macro allows
        // you make the constructor take fewer parameters, leaving the
        // remaining elements uninitialized.  An example of when this is
        // necessary is when you have a structure containing only two
        // integer elements: one of the other ctors defined for SSQLS
        // structures takes two ints, so the compiler barfs if you pass
        // 2 for this argument.  You would need to pass 0 here to get
        // that SSQLS structure to compile.
    string, item,
    longlong, num,
    double, weight,
    double, price,
    Date, sdate)

int
main(int argc, char *argv[])
{
    try {
        // Establish the connection to the database server.
        Connection con(use_exceptions);
        if (!connect_to_db(argc, argv, con)) {
            return 1;
        }

        // Retrieve all rows from the stock table and put them in an
        // STL set.  Notice that this works just as well as storing them
        // in a vector, which we did in custom1.cpp.  It works because
        // SSQLS objects are less-than comparable.
        Query query = con.query();
        query << "select * from stock";
        set<stock> res;
        query.storein(res);

        // Display the result set.  Since it is an STL set and we set up
        // the SSQLS to compare based on the item column, the rows will
        // be sorted by item.
        print_stock_header(res.size());
        set<stock>::iterator it;
        cout.precision(3);
        for (it = res.begin(); it != res.end(); ++it) {
            print_stock_row(it->item.c_str(), it->num, it->weight,
                    it->price, it->sdate);
        }

        // Use set's find method to look up a stock item by item name.
        // This also uses the SSQLS comparison setup.
        it = res.find(stock("Hotdog Buns"));
        if (it != res.end()) {
            cout << endl << "Currently " << it->num <<
                    " hotdog buns in stock." << endl;
        }
        else {
            cout << endl << "Sorry, no hotdog buns in stock." << endl;
        }
    }
    catch (BadQuery& er) {
        // Handle any connection or query errors
        cerr << "Error: " << er.what() << endl;
        return -1;
    }
    catch (BadConversion& er) {
        // Handle bad conversions
        cerr << "Error: " << er.what() << "\"." << endl <<
                "retrieved data size: " << er.retrieved <<
                " actual data size: " << er.actual_size << endl;
        return -1;
    }
    catch (exception& er) {
        // Catch-all for any other standard C++ exceptions
        cerr << "Error: " << er.what() << endl;
        return -1;
    }

    return 0;
}

3.5. Handling SQL Nulls

There is no equivalent of SQL's null in the standard C++ type system.

The primary distinction is one of type: in SQL, null is a column attribute, which affects whether that column can hold a SQL null. This effectively doubles the number of types in SQL. MySQL++ handles this the same way SQL does: it provides the Null template to allow the creation of distinct "nullable" versions of existing C++ types. For each column type MySQL understands, the library instantiates this template for the closest C++ type. (See the top of lib/type_info.cpp for the list.)

Template instantiations are first-class types in the C++ language, on par with any other type. You can use nullable MySQL++ types anywhere you'd use the plain version of that type, you can assign plain values to a nullable object and vice versa, etc.

There's a secondary distinction between SQL null and anything available in the standard C++ type system: SQL null is a distinct value, equal to nothing else. C++'s NULL is ambiguous, being equal to 0 in integer context, so MySQL++ has a global null object which which you can assign to any nullable object to get a SQL null.

By default, if you try to convert a SQL null to a plain C++ data type, MySQL++ will throw a BadNullConversion exception to enforce this distinction. If you insert a SQL null into a C++ stream, you get "(NULL)". The NullisNull "behavior" type encapsulates these two rules; it is the default for one of template Null's parameters. To relax this distinction, you can instantiate the Null template with a different behavior type: NullisZero or NullisBlank. As you might guess from their names, SQL nulls using these behaviors get converted to 0 or a blank C string, respectively.

3.6. Which Query Type to Use?

There are three major ways to execute a query in MySQL++: Query::execute(), Query::store(), and Query::use(). Which should you use, and why?

execute() is for queries that do not return data per se. For instance, CREATE INDEX. You do get back some information from the MySQL server, which execute() returns to its caller in a ResNSel object. In addition to the obvious — a flag stating whether the query succeeded or not — this object also contains things like the number of rows that the query affected. If you only need the success status, there's Query::exec(), which just returns bool.

If your query does pull data from the database, the simplest option is store(). This returns a Result object, which contains an in-memory copy of the result set. The nice thing about this is that Result is a sequential container, like std::vector, so you can iterate through it forwards and backwards, access elements with subscript notation, etc. There are also the storein() methods, which actually put the result set into an STL container of your choice. The downside of these methods is that a sufficiently large result set will give your program memory problems.

For these large result sets, you should use the use() method instead. This returns a ResUse object, which is similar to Result, but without all of the random-access features. This is because a "use" query tells the database server to send the results back one row at a time, to be processed linearly. It's analogous to a C++ stream's input iterator, as opposed to a random-access iterator that a container like vector offers. By accepting this limitation, you can process arbitrarily large result sets.

3.7. Let's Do Something Useful

These next few examples demonstrate just how powerful C++ can be, allowing you to do a lot of work in few lines of code without losing efficiency.

Since the code is meant to be re-used as-is, constants that can differ from one case to another have been grouped in order to simplify editing. Also, all of these examples have full error checking code, showing off the power of MySQL++'s exception handling features.

Loading binary file in a BLOB column

Since MySQL 3.23, BLOB columns have been available, but their use is sometimes not straightforward. Besides showing how easy it can be with MySQL++, this example demonstrates several features of MySQL++. The program requires one command line parameter, which is a full path to the binary file. This is examples/load_file.cpp:

#include <mysql++.h>

#include <sys/stat.h>

#include <fstream>

#include <errno.h>
#include <stdlib.h>

using namespace std;
using namespace mysqlpp;

const char MY_DATABASE[] = "telcent";
const char MY_TABLE[] = "fax";
const char MY_HOST[] = "localhost";
const char MY_USER[] = "root";
const char MY_PASSWORD[] = "";
const char MY_FIELD[] = "fax";  // BLOB field

int
main(int argc, char *argv[])
{
    if (argc < 2) {
        cerr << "Usage : load_file full_file_path" << endl << endl;
        return -1;
    }

    Connection con(use_exceptions);
    try {
        con.real_connect(MY_DATABASE, MY_HOST, MY_USER, MY_PASSWORD, 3306,
                         0, 60, NULL);
        Query query = con.query();
        ostringstream strbuf;
        ifstream In(argv[1], ios::in | ios::binary);
        struct stat for_len;
        if ((In.rdbuf())->is_open()) {
            if (stat(argv[1], &for_len) == -1)
                return -1;
            unsigned int blen = for_len.st_size;
            if (!blen)
                return -1;
            char *read_buffer = new char[blen];
            In.read(read_buffer, blen);
            string fill(read_buffer, blen);
            strbuf << "INSERT INTO " << MY_TABLE << " (" << MY_FIELD <<
                ") VALUES(\"" << mysqlpp::escape << fill << "\")" << ends;
            query.exec(strbuf.str());
            delete[]read_buffer;
        }
        else
            cerr << "Your binary file " << argv[1] <<
                "could not be open, errno = " << errno;
        return 0;

    }
    catch (BadQuery& er) {
        // Handle any connection or query errors
        cerr << "Error: " << er.what() << " " << con.errnum() << endl;
        return -1;
    }
    catch (BadConversion& er) {
        // Handle bad conversions
        cerr << "Error: " << er.what() << "\"." << endl <<
                "retrieved data size: " << er.retrieved <<
                " actual data size: " << er.actual_size << endl;
        return -1;
    }
    catch (exception& er) {
        // Catch-all for any other standard C++ exceptions
        cerr << "Error: " << er.what() << endl;
        return -1;
    }
} 

Notice that we used the escape manipulator when building the INSERT query above. This is because we're not using one of the MySQL++ types that does automatic escaping and quoting.

Displaying images in HTML from BLOB column

This example is also a very short one, considering the function that it performs. Although all modern versions of MySQL have a command that dumps data from a BLOB column to a binary file, this example shows how to do it in your code instead, without requiring an temporary file on disk. This is examples/cgi_image.cpp:

#include <mysql++.h>

using namespace std;
using namespace mysqlpp;

#define MY_DATABASE "telcent"
#define MY_TABLE    "fax"
#define MY_HOST     "localhost"
#define MY_USER     "root"
#define MY_PASSWORD ""
#define MY_FIELD    "fax"       // BLOB field
#define MY_KEY      "datet"     // PRIMARY KEY

int
main(int argc, char *argv[])
{
    if (argc < 2) {
        cerr << "Usage : cgi_image primary_key_value" << endl << endl;
        return -1;
    }

    cout << "Content-type: image/jpeg" << endl;
    Connection con(use_exceptions);
    try {
        con.real_connect(MY_DATABASE, MY_HOST, MY_USER, MY_PASSWORD, 3306,
                         0, 60, NULL);
        Query query = con.query();
        query << "SELECT " << MY_FIELD << " FROM " << MY_TABLE << " WHERE "
            << MY_KEY << " = " << argv[1];
        ResUse res = query.use();
        Row row = res.fetch_row();
        long unsigned int *jj = res.fetch_lengths();
        cout << "Content-length: " << *jj << endl << endl;
        fwrite(row.raw_data(0), 1, *jj, stdout);
        return 0;
    }
    catch (BadQuery& er) {
        cerr << "Error: " << er.what() << " " << con.errnum() << endl;
        return -1;
    }
    catch (exception& er) {
        cerr << "Error: " << er.what() << endl;
        return -1;
    }
} 

DELETE or UPDATE from SELECT

MySQL's SELECT statement has more power to winnow out just the items of interest from the database than do DELETE or UPDATE queries. Therefore, many people have wanted the ability to execute a SELECT statement that in fact deletes or updates the rows matched, rather than returning them. This example implements that feature in just a few lines of code. It is examples/updel.cpp:

#include <mysql++.h>

#include <string>

using namespace std;
using namespace mysqlpp;

#define MY_DATABASE "telcent"
#define MY_TABLE    "nazivi"
#define MY_HOST     "localhost"
#define MY_USER     "root"
#define MY_PASSWORD ""
#define MY_FIELD    "naziv"
#define MY_QUERY    "SELECT URL from my_table as t1, my_table as t2 where t1.field = t2.field"

int
main()
{
    Connection con(use_exceptions);
    try {
        ostringstream strbuf;
        unsigned int i = 0;
        con.real_connect(MY_DATABASE, MY_HOST, MY_USER, MY_PASSWORD, 3306,
                         0, 60, NULL);
        Query query = con.query();
        query << MY_QUERY;
        ResUse res = query.use();
        Row row;
        strbuf << "delete from " << MY_TABLE << " where " << MY_FIELD <<
            " in (";
        //  for UPDATE just replace the above DELETE FROM with UPDATE statement
        for (; row = res.fetch_row(); i++)
            strbuf << row[0] << ",";
        if (!i)
            return 0;
        string output(strbuf.str());
        output.erase(output.size() - 1, 1);
        output += ")";
        query.exec(output);
        //cout << output << endl;
    }
    catch (BadQuery& er) {
        // Handle any connection or query errors
        cerr << "Error: " << er.what() << " " << con.errnum() << endl;
        return -1;
    }
    catch (BadConversion& er) {
        // Handle bad conversions
        cerr << "Error: " << er.what() << "\"." << endl
            << "retrieved data size: " << er.retrieved
            << " actual data size: " << er.actual_size << endl;
        return -1;
    }
    catch (exception& er) {
        // Catch-all for any other standard C++ exceptions
        cerr << "Error: " << er.what() << endl;
        return -1;
    }

    return 0;
} 

Notice that the row values used in the IN clause aren't escaped or quoted. This is because row elements are ColData types, so they have automatic escaping and quoting, as appropriate to the type being inserted. If you want to disable this feature, it's easily done: click the ColData link for the details.

Users of this example should beware that one more check is required in order to run this query safely: in some extreme cases, the size of the query might grow larger than MySQL's maximum allowed packet size. This check should be added.