Chapter 8 Connector/C++ Tutorials

Table of Contents

8.1 Prerequisites and Background Information
8.2 Calling Stored Procedures with Statement Objects
8.2.1 Using a Statement for a Stored Procedure That Returns No Result
8.2.2 Using a Statement for a Stored Procedure That Returns an Output Parameter
8.2.3 Using a Statement for a Stored Procedure That Returns a Result Set
8.3 Calling Stored Procedures with PreparedStatement Objects
8.3.1 Using a PreparedStatement for a Stored Procedure That Returns No Result
8.3.2 Using a PreparedStatement for a Stored Procedure That Returns an Output Parameter
8.3.3 Using a PreparedStatement for a Stored Procedure That Returns a Result Set

The following tutorials illustrate various aspects of using MySQL Connector/C++. Also consult the examples in Chapter 7, Getting Started with Connector/C++: Usage Examples.

8.1 Prerequisites and Background Information

This section describes the prerequisites that must be satisifed before you work through the remaining tutorial sections, and shows how to set up the framework code that serves as the basis for the tutorial applications.

These tutorials refer to tables and sample data from the world database, which you can download from the Example Databases section of the MySQL Documentation page.

Each tutorial application uses a framework consisting of the following code. The examples vary at the line that says /* INSERT TUTORIAL CODE HERE! */ within the try block, which is replaced for each application with the application-specific code.


#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <stdexcept>
/* uncomment for applications that use vectors */
/*#include <vector>*/

#include "mysql_connection.h"

#include <cppconn/driver.h>
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>

#define EXAMPLE_HOST "localhost"
#define EXAMPLE_USER "worlduser"
#define EXAMPLE_PASS "worldpass"
#define EXAMPLE_DB "world"

using namespace std;

int main(int argc, const char **argv)
{
  string url(argc >= 2 ? argv[1] : EXAMPLE_HOST);
  const string user(argc >= 3 ? argv[2] : EXAMPLE_USER);
  const string pass(argc >= 4 ? argv[3] : EXAMPLE_PASS);
  const string database(argc >= 5 ? argv[4] : EXAMPLE_DB);

  cout << "Connector/C++ tutorial framework..." << endl;
  cout << endl;

  try {

    /* INSERT TUTORIAL CODE HERE! */

  } catch (sql::SQLException &e) {
    /*
      MySQL Connector/C++ throws three different exceptions:

      - sql::MethodNotImplementedException (derived from sql::SQLException)
      - sql::InvalidArgumentException (derived from sql::SQLException)
      - sql::SQLException (derived from std::runtime_error)
    */
    cout << "# ERR: SQLException in " << __FILE__;
    cout << "(" << __FUNCTION__ << ") on line " << __LINE__ << endl;
    /* what() (derived from std::runtime_error) fetches error message */
    cout << "# ERR: " << e.what();
    cout << " (MySQL error code: " << e.getErrorCode();
    cout << ", SQLState: " << e.getSQLState() << " )" << endl;

    return EXIT_FAILURE;
  }

  cout << "Done." << endl;
  return EXIT_SUCCESS;
}

To try the framework code as a standalone program, use this procedure:

  1. Copy and paste the framework code to a file such as framework.cpp. Edit the #define statements to reflect your connection parameters (server, user, password, database). Also, because the file contains those parameters, set its access mode to be readable only to yourself.

  2. Compile the framework. For example, on OS X, the command might look like this (enter the command on one line):

    shell> g++ -o framework
      -I/usr/local/include -I/usr/local/include/cppconn
      -lmysqlcppconn framework.cpp
    

    Adapt the command as necessary for your system. A similar command is needed for the tutorial applications that follow.

  3. To run the framework, enter the following:

    shell> ./framework
    

    You will see a simple message:

    Connector/C++ tutorial framework...
    
    Done.
    

You are now ready to continue to the tutorials.

8.2 Calling Stored Procedures with Statement Objects

A stored procedure can be called using a Statement or PreparedStatement object. This section shows how to call stored procedures using Statement objects. To see how to use PreparedStatement objects, see Section 8.3, “Calling Stored Procedures with PreparedStatement Objects”.

The following list describes different types of stored procedures that you can construct and call, along with example stored procedures that illustrate each type:

  1. A stored procedure that returns no result. For example, such a stored procedure can log non-critical information, or change database data in a straightforward way.

    The following procedure adds a country to the world database, but does not return a result:

    CREATE PROCEDURE add_country (IN country_code CHAR(3),
                                  IN country_name CHAR(52),
                                  IN continent_name CHAR(30))
    BEGIN
      INSERT INTO Country(Code, Name, Continent)
        VALUES (country_code, country_name, continent_name);
    END;
    
  2. A stored procedure that returns one or more values using output parameters. For example, such a procedure can indicate success or failure, or retrieve and return data items.

    The following procedures use an output parameter to return the population of a specified country or continent, or the entire world:

    CREATE PROCEDURE get_pop (IN country_name CHAR(52),
                              OUT country_pop BIGINT)
    BEGIN
      SELECT Population INTO country_pop FROM Country
        WHERE Name = country_name;
    END;
    
    CREATE PROCEDURE get_pop_continent (IN continent_name CHAR(30),
                                        OUT continent_pop BIGINT)
    BEGIN
      SELECT SUM(Population) INTO continent_pop FROM Country
        WHERE Continent = continent_name;
    END;
    
    CREATE PROCEDURE get_pop_world (OUT world_pop BIGINT)
    BEGIN
      SELECT SUM(Population) INTO world_pop FROM Country;
    END;
    
  3. A stored procedure that returns one or more result sets. The procedure can execute one or more queries, each of which returns an arbitrary number of rows. Your application loops through each result set to display, transform, or otherwise process each row in it.

    This procedure returns several result sets:

    
    CREATE PROCEDURE get_data ()
    BEGIN
      SELECT Code, Name, Population, Continent FROM Country
        WHERE Continent = 'Oceania' AND Population < 10000;
      SELECT Code, Name, Population, Continent FROM Country
        WHERE Continent = 'Europe' AND Population < 10000;
      SELECT Code, Name, Population, Continent FROM Country
        WHERE Continent = 'North America' AND Population < 10000;
    END;
    
    

Enter and test the stored procedures manually to ensure that they will be available to your C++ applications. (Select world as the default database before you create them.) You are now ready to start writing applications using Connector/C++ that call stored procedures.

8.2.1 Using a Statement for a Stored Procedure That Returns No Result

This example shows how to call a stored procedure that returns no result set.

  1. Make a copy of the tutorial framework code:

    shell> cp framework.cpp sp_scenario1.cpp
    
  2. Add the following code to the try block of the tutorial framework:

    
    sql::Driver* driver = get_driver_instance();
    std::auto_ptr<sql::Connection> con(driver->connect(url, user, pass));
    con->setSchema(database);
    std::auto_ptr<sql::Statement> stmt(con->createStatement());
    
    // We need not check the return value explicitly. If it indicates
    // an error, Connector/C++ generates an exception.
    stmt->execute("CALL add_country('ATL', 'Atlantis', 'North America')");
    
    
  3. Compile the program as described in Section 8.1, “Prerequisites and Background Information”.

  4. Run the program:

    shell> ./sp_scenario1
    
  5. Using the mysql command-line client or other suitable program, check the world database to determine that it has been updated correctly. You can use this query:

    mysql> SELECT Code, Name, Continent FROM Country WHERE Code='ATL';
    +------+----------+---------------+
    | Code | Name     | Continent     |
    +------+----------+---------------+
    | ATL  | Atlantis | North America |
    +------+----------+---------------+
    

The code in this application simply invokes the execute method, passing to it a statement that calls the stored procedure. The procedure itself returns no value, although it is important to note there is always a return value from the CALL statement; this is the execute status. Connector/C++ handles this status for you, so you need not handle it explicitly. If the execute call fails for some reason, it raises an exception that the catch block handles.

8.2.2 Using a Statement for a Stored Procedure That Returns an Output Parameter

This example shows how to handle a stored procedure that returns an output parameter.

  1. Make a copy of the tutorial framework code:

    shell> cp framework.cpp sp_scenario2.cpp
    
  2. Add the following code to the try block of the tutorial framework:

    
    sql::Driver* driver = get_driver_instance();
    std::auto_ptr<sql::Connection> con(driver->connect(url, user, pass));
    con->setSchema(database);
    std::auto_ptr<sql::Statement> stmt(con->createStatement());
    
    stmt->execute("CALL get_pop('Uganda', @pop)");
    
    std::auto_ptr<sql::ResultSet> res(stmt->executeQuery("SELECT @pop AS _reply"));
    while (res->next())
      cout << "Population of Uganda: " << res->getString("_reply") << endl;
    
    stmt->execute("CALL get_pop_continent('Asia', @pop)");
    
    res.reset(stmt->executeQuery("SELECT @pop AS _reply"));
    while (res->next())
      cout << "Population of Asia: " << res->getString("_reply") << endl;
    
    stmt->execute("CALL get_pop_world(@pop)");
    
    res.reset(stmt->executeQuery("SELECT @pop AS _reply"));
    while (res->next())
      cout << "Population of World: " << res->getString("_reply") << endl;
    
    
  3. Compile the program as described in Section 8.1, “Prerequisites and Background Information”.

  4. Run the program:

    shell> ./sp_scenario2
    Connector/C++ tutorial framework...
    
    Population of Uganda: 21778000
    Population of Asia: 3705025700
    Population of World: 6078749450
    Done.
    

In this scenario, each stored procedure sets the value of an output parameter. This is not returned directly to the execute method, but needs to be obtained using a subsequent query. If you were executing the SQL statements directly, you might use statements similar to these:

CALL get_pop('Uganda', @pop);
SELECT @pop;
CALL get_pop_continent('Asia', @pop);
SELECT @pop;
CALL get_pop_world(@pop);
SELECT @pop;

In the C++ code, a similar sequence is carried out for each procedure call:

  1. Execute the CALL statement.

  2. Obtain the output parameter by executing an additional query. The query produces a ResultSet object.

  3. Retrieve the data using a while loop. The simplest way to do this is to use a getString method on the ResultSet, passing the name of the variable to access. In this example _reply is used as a placeholder for the variable and therefore is used as the key to access the correct element of the result dictionary.

    Although the query used to obtain the output parameter returns only a single row, it is important to use the while loop to catch more than one row, to avoid the possibility of the connection becoming unstable.

8.2.3 Using a Statement for a Stored Procedure That Returns a Result Set

This example shows how to handle result sets produced by a stored procedure.

Note

This scenario requires MySQL 5.5.3 or higher. The client/server protocol does not support fetching multiple result sets from stored procedures prior to 5.5.3.

  1. Make a copy of the tutorial framework code:

    shell> cp framework.cpp sp_scenario3.cpp
    
  2. Add the following code to the try block of the tutorial framework:

    
    sql::Driver* driver = get_driver_instance();
    std::auto_ptr<sql::Connection> con(driver->connect(url, user, pass));
    con->setSchema(database);
    std::auto_ptr<sql::Statement> stmt(con->createStatement());
    
    stmt->execute("CALL get_data()");
    std::auto_ptr< sql::ResultSet > res;
    do {
      res.reset(stmt->getResultSet());
      while (res->next()) {
        cout << "Name: " << res->getString("Name")
             << " Population: " << res->getInt("Population")
             << endl;
      }
    } while (stmt->getMoreResults());
    
    
  3. Compile the program as described in Section 8.1, “Prerequisites and Background Information”.

  4. Run the program:

    shell> ./sp_scenario3
    Connector/C++ tutorial framework...
    
    Name: Cocos (Keeling) Islands Population: 600
    Name: Christmas Island Population: 2500
    Name: Norfolk Island Population: 2000
    Name: Niue Population: 2000
    Name: Pitcairn Population: 50
    Name: Tokelau Population: 2000
    Name: United States Minor Outlying Islands Population: 0
    Name: Svalbard and Jan Mayen Population: 3200
    Name: Holy See (Vatican City State) Population: 1000
    Name: Anguilla Population: 8000
    Name: Atlantis Population: 0
    Name: Saint Pierre and Miquelon Population: 7000
    Done.
    

The code is similar to the examples shown previously. The code of particular interest here is:


do {
  res.reset(stmt->getResultSet());
  while (res->next()) {
    cout << "Name: " << res->getString("Name")
         << " Population: " << res->getInt("Population")
         << endl;
  }
} while (stmt->getMoreResults());

The CALL is executed as before, but this time the results are returned into multiple ResultSet objects because the stored procedure executes multiple SELECT statements. In this example, the output shows that three result sets are processed, because there are three SELECT statements in the stored procedure. Each result set returns more than one row.

The results are processed using this code pattern:


do {
  Get Result Set
  while (Get Result) {
    Process Result
  }
} while (Get More Result Sets);

Note

Use this pattern even if the stored procedure executes only a single SELECT and produces only one result set. This is a requirement of the underlying protocol.

8.3 Calling Stored Procedures with PreparedStatement Objects

This section shows how to call stored procedures using prepared statements. It is recommended that, before working through it, you first work through the previous tutorial Section 8.2, “Calling Stored Procedures with Statement Objects”. That section shows the stored procedures required by the applications in this section.

8.3.1 Using a PreparedStatement for a Stored Procedure That Returns No Result

This example shows how to call a stored procedure that returns no result set.

  1. Make a copy of the tutorial framework code:

    shell> cp framework.cpp ps_scenario1.cpp
    
  2. Add the following code to the try block of the tutorial framework:

    
    vector<string> code_vector;
    code_vector.push_back("SLD");
    code_vector.push_back("DSN");
    code_vector.push_back("ATL");
    
    vector<string> name_vector;
    name_vector.push_back("Sealand");
    name_vector.push_back("Disneyland");
    name_vector.push_back("Atlantis");
    
    vector<string> cont_vector;
    cont_vector.push_back("Europe");
    cont_vector.push_back("North America");
    cont_vector.push_back("Oceania");
    
    sql::Driver * driver = get_driver_instance();
    
    std::auto_ptr< sql::Connection > con(driver->connect(url, user, pass));
    con->setSchema(database);
    
    std::auto_ptr< sql::PreparedStatement >  pstmt;
    
    pstmt.reset(con->prepareStatement("CALL add_country(?,?,?)"));
    for (int i=0; i<3; i++)
    {
      pstmt->setString(1,code_vector[i]);
      pstmt->setString(2,name_vector[i]);
      pstmt->setString(3,cont_vector[i]);
    
      pstmt->execute();
    }
    
    

    Also, uncomment #include <vector> near the top of the code, because vectors are used to store sample data.

  3. Compile the program as described in Section 8.1, “Prerequisites and Background Information”.

  4. Run the program:

    shell> ./ps_scenario1
    
  5. You can check whether the database has been updated correctly by using this query:

    mysql> SELECT Code, Name, Continent FROM Country
        -> WHERE Code IN('DSN','ATL','SLD');
    +------+------------+---------------+
    | Code | Name       | Continent     |
    +------+------------+---------------+
    | ATL  | Atlantis   | Oceania       |
    | DSN  | Disneyland | North America |
    | SLD  | Sealand    | Europe        |
    +------+------------+---------------+
    

The code is relatively simple, as no processing is required to handle result sets. The procedure call, CALL add_country(?,?,?), is made using placeholders for input parameters denoted by '?'. These placeholders are replaced by the appropriate data values using the PreparedStatement object's setString method. The for loop is set up to iterate 3 times, as there are three data sets in this example. The same PreparedStatement is executed three times, each time with different input parameters.

8.3.2 Using a PreparedStatement for a Stored Procedure That Returns an Output Parameter

This example shows how to handle a stored procedure that returns an output parameter.

  1. Make a copy of the tutorial framework code:

    shell> cp framework.cpp ps_scenario2.cpp
    
  2. Add the following code to the try block of the tutorial framework:

    
    vector<string> cont_vector;
    cont_vector.push_back("Europe");
    cont_vector.push_back("North America");
    cont_vector.push_back("Oceania");
    
    sql::Driver * driver = get_driver_instance();
    
    std::auto_ptr< sql::Connection > con(driver->connect(url, user, pass));
    con->setSchema(database);
    
    std::auto_ptr< sql::Statement > stmt(con->createStatement());
    std::auto_ptr< sql::PreparedStatement >  pstmt;
    std::auto_ptr< sql::ResultSet > res;
    
    pstmt.reset(con->prepareStatement("CALL get_pop_continent(?,@pop)"));
    
    for (int i=0; i<3; i++)
    {
      pstmt->setString(1,cont_vector[i]);
      pstmt->execute();
      res.reset(stmt->executeQuery("SELECT @pop AS _population"));
      while (res->next())
        cout << "Population of "
             << cont_vector[i]
             << " is "
             << res->getString("_population") << endl;
    }
    
    

    Also, uncomment #include <vector> near the top of the code, because vectors are used to store sample data.

  3. Compile the program as described in Section 8.1, “Prerequisites and Background Information”.

  4. Run the program:

    shell> ./ps_scenario2
    Connector/C++ tutorial framework...
    
    Population of Europe is 730074600
    Population of North America is 482993000
    Population of Oceania is 30401150
    Done.
    

In this scenario a PreparedStatement object is created that calls the get_pop_continent stored procedure. This procedure takes an input parameter, and also returns an output parameter. The approach used is to create another statement that can be used to fetch the output parameter using a SELECT query. Note that when the PreparedStatement is created, the input parameter to the stored procedure is denoted by '?'. Prior to execution of the prepared statement, it is necessary to replace this placeholder by an actual value. This is done using the setString method:

pstmt->setString(1,cont_vector[i]);

Although the query used to obtain the output parameter returns only a single row, it is important to use the while loop to catch more than one row, to avoid the possibility of the connection becoming unstable.

8.3.3 Using a PreparedStatement for a Stored Procedure That Returns a Result Set

This example shows how to handle result sets produced by a stored procedure.

Note

This scenario requires MySQL 5.5.3 or higher. The client/server protocol does not support fetching multiple result sets from stored procedures prior to 5.5.3.

  1. Make a copy of the tutorial framework code:

    shell> cp framework.cpp ps_scenario3.cpp
    
  2. Add the following code to the try block of the tutorial framework:

    
    sql::Driver * driver = get_driver_instance();
    
    std::auto_ptr< sql::Connection > con(driver->connect(url, user, pass));
    con->setSchema(database);
    
    std::auto_ptr< sql::PreparedStatement >  pstmt;
    std::auto_ptr< sql::ResultSet > res;
    
    pstmt.reset(con->prepareStatement("CALL get_data()"));
    res.reset(pstmt->executeQuery());
    
    for(;;)
    {
      while (res->next()) {
        cout << "Name: " << res->getString("Name")
             << " Population: " << res->getInt("Population")
             << endl;
      }
      if (pstmt->getMoreResults())
      {
        res.reset(pstmt->getResultSet());
        continue;
      }
      break;
    }
    
    
  3. Compile the program as described in Section 8.1, “Prerequisites and Background Information”.

  4. Run the program:

    shell> ./ps_scenario3
    
  5. Make a note of the output generated.

The code executes the stored procedure using a PreparedStatement object. The standard do/while construct is used to ensure that all result sets are fetched. The returned values are fetched from the result sets using the getInt and getString methods.