Table of Contents
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.
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:
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.
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.
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.
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:
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;
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;
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.
This example shows how to call a stored procedure that returns no result set.
Make a copy of the tutorial framework code:
shell> cp framework.cpp sp_scenario1.cpp
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')");
Compile the program as described in Section 8.1, “Prerequisites and Background Information”.
Run the program:
shell> ./sp_scenario1
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.
This example shows how to handle a stored procedure that returns an output parameter.
Make a copy of the tutorial framework code:
shell> cp framework.cpp sp_scenario2.cpp
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;
Compile the program as described in Section 8.1, “Prerequisites and Background Information”.
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:
Execute the CALL statement.
Obtain the output parameter by executing an additional
query. The query produces a ResultSet
object.
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.
This example shows how to handle result sets produced by a stored procedure.
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.
Make a copy of the tutorial framework code:
shell> cp framework.cpp sp_scenario3.cpp
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());
Compile the program as described in Section 8.1, “Prerequisites and Background Information”.
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);
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.
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.
This example shows how to call a stored procedure that returns no result set.
Make a copy of the tutorial framework code:
shell> cp framework.cpp ps_scenario1.cpp
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.
Compile the program as described in Section 8.1, “Prerequisites and Background Information”.
Run the program:
shell> ./ps_scenario1
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.
This example shows how to handle a stored procedure that returns an output parameter.
Make a copy of the tutorial framework code:
shell> cp framework.cpp ps_scenario2.cpp
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.
Compile the program as described in Section 8.1, “Prerequisites and Background Information”.
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.
This example shows how to handle result sets produced by a stored procedure.
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.
Make a copy of the tutorial framework code:
shell> cp framework.cpp ps_scenario3.cpp
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;
}
Compile the program as described in Section 8.1, “Prerequisites and Background Information”.
Run the program:
shell> ./ps_scenario3
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.