How to Connect C++ Programs to MariaDB

Today, we announced the general availability of MariaDB Connector/C++. The C++ language is known for its efficiency, versatility and extensibility. In fact, it’s often used in industries like fintech and gaming where applications require predictable concurrency and latency. With MariaDB Connector/C++, developers can use the new connector within their applications to natively connect to MariaDB Server on premises and in the cloud on MariaDB SkySQL.

In this article I’ll be introducing you to MariaDB Connector/C++ with simple examples that demonstrate create, read, update, and delete (CRUD) interactions with data stored in MariaDB. More specifically, I’m going to walk through the process of creating a C++ console application that will allow you to perform simple operations for managing tasks.  But enough talk, let’s get started!

Downloading and Installing

In order to use MariaDB Connector/C++ you need to have access to an instance of MariaDB Server. There are a variety of ways you can get started with MariaDB on your local machine, on-premises, or even in the cloud.

  1. Download and install MariaDB Community Server
    1. Directly
    2. Using a Docker image
  2. Download and install MariaDB Enterprise Server 
  3. Deploy with MariaDB SkySQL, the ultimate MariaDB cloud database

After you’ve set up a MariaDB Server instance you can follow the steps provided within the MariaDB Enterprise documentation for instructions on how to download and install MariaDB Connector/C++.

Preparing MariaDB Server

In this example, before you can use the new MariaDB C++ connector you will need to have a database and table. Using a client of your choosing, connect to your MariaDB database instance and:

1. Create a new database and a single table that will be responsible for storing the task records.

CREATE DATABASE IF NOT EXISTS todo;

CREATE TABLE todo.tasks (
  id INT(11) unsigned NOT NULL AUTO_INCREMENT,
  description VARCHAR(500) NOT NULL,
  completed BOOLEAN NOT NULL DEFAULT 0,
  PRIMARY KEY (id)
);

2. Create a new database user that Connector/C++ will use to connect to MariaDB.

CREATE USER IF NOT EXISTS app_user@localhost IDENTIFIED BY 
 'Password123!';

GRANT ALL PRIVILEGES ON todo.* TO app_user@localhost;

Connecting to MariaDB Server

Now that you have your database instance, schema, and user set up, it’s time to jump into C++. It all starts by establishing a connection to MariaDB. Of course, to do that, you need to have included the MariaDB connector header files, specifically conncpp.hpp, at the top of the file that you’re adding your C++ code to.

#include <mariadb/conncpp.hpp>

To establish a connection, start by retrieving a Driver object that can then be used, in combination with Java Database Connectivity (JDBC) configuration information, to obtain a Connection object.

// Instantiate Driver
sql::Driver* driver = sql::mariadb::get_driver_instance();

// Configure Connection
sql::SQLString url("jdbc:mariadb://localhost:3306/todo");
sql::Properties properties({{"user", "app_user"}, {"password", "Password123!"}});

// Establish Connection
std::unique_ptr<sql::Connection> conn(driver->connect(url, properties));

For more information on MariaDB’s Connector/C++’s connection functionality be sure to check out our official enterprise documentation.

Adding Tasks

Once you’ve obtained a Connection object, you’re off to the races! Using conn, from the previous section, you’re now able to execute SQL through the use of a prepared statement to insert data into MariaDB.

// Create a new PreparedStatement
std::unique_ptr<sql::PreparedStatement> stmnt(conn->prepareStatement("insert into tasks (description) values (?)"));

// Bind values to SQL statement
stmnt->setString(1, description);

// Execute query
stmnt->executeQuery();

Retrieving Tasks

Using a very similar approach, as within the last section, you can also create a Statement object to retrieve and print out all of the task records within the tasks table.

// Create a new Statement
std::unique_ptr stmnt(conn->createStatement());

// Execute query
sql::ResultSet *res = stmnt->executeQuery("select * from tasks");

// Loop through and print results
while (res->next()) {
       std::cout << "id = " << res->getInt(1);
       std::cout << ", description = " << res->getString(2);
       std::cout << ", completed = " << res->getBoolean(3) << "\n";
}

Updating Tasks

You can also use a PreparedStatement and specifying parameters (and values) to locate and modify an existing task record.

// Create a new PreparedStatement
std::unique_ptr stmnt(conn->prepareStatement("update tasks set completed = ? where id = ?"));

// Bind values to SQL statement
stmnt->setBoolean(1, completed);
stmnt->setInt(2, id);

// Execute query
stmnt->executeQuery();

Deleting Tasks

And, of course, you also have the ability to remove data from MariaDB by using a DELETE SQL statement with a PreparedStatement, to target a specific record.

// Create a new PreparedStatement
std::unique_ptr stmnt(conn->prepareStatement("delete from tasks where id = ?"));

// Bind values to SQL statement
stmnt->setInt(1, id);

// Execute query
stmnt->executeQuery();

Bringing It All Together

Finally, you can put everything together in one, self-contained example by copying and pasting the code below into a new file called tasks.cpp. The sample below combines all of the CRUD operations I’ve gone through into a collection of functions that can be executed from the main method.

#include <iostream> 
#include <cstring>
#include <mariadb/conncpp.hpp>

// Delete a task record (indicated by id)
void deleteTask(std::unique_ptr &conn, int id) {
    try {
        // Create a new PreparedStatement
        std::unique_ptr stmnt(conn->prepareStatement("delete from tasks where id = ?"));
        // Bind values to SQL statement
        stmnt->setInt(1, id);
        // Execute query
        stmnt->executeQuery();
    }
    catch(sql::SQLException& e){
      std::cerr << "Error deleting task: " << e.what() << std::endl;
   }
}

// Update the completed value of a task record (indicated by id)
void updateTaskStatus(std::unique_ptr &conn, int id, bool completed) {
    try {
        // Create a new PreparedStatement
        std::unique_ptr stmnt(conn->prepareStatement("update tasks set completed = ? where id = ?"));
        // Bind values to SQL statement
        stmnt->setBoolean(1, completed);
        stmnt->setInt(2, id);
        // Execute query
        stmnt->executeQuery();
    }
    catch(sql::SQLException& e){
      std::cerr << "Error updating task status: " << e.what() << std::endl;
   }
}

// Create a new task record
void addTask(std::unique_ptr &conn, std::string description) {
    try {
        // Create a new PreparedStatement
        std::unique_ptr stmnt(conn->prepareStatement("insert into tasks (description) values (?)"));
        // Bind values to SQL statement
        stmnt->setString(1, description);
        // Execute query
        stmnt->executeQuery();
    }
    catch(sql::SQLException& e){
      std::cerr << "Error inserting new task: " << e.what() << std::endl;
   }
}

// Print all records in tasks table 
void showTasks(std::unique_ptr &conn) {
    try {
        // Create a new Statement
        std::unique_ptr stmnt(conn->createStatement());
        // Execute query
        sql::ResultSet *res = stmnt->executeQuery("select * from tasks");
        // Loop through and print results
        while (res->next()) {
            std::cout << "id = " << res->getInt(1);
            std::cout << ", description = " << res->getString(2);
            std::cout << ", completed = " << res->getBoolean(3) << "\n";
        }
    }
    catch(sql::SQLException& e){
      std::cerr << "Error selecting tasks: " << e.what() << std::endl;
   }
}

// Main Process
int main(int argc, char **argv){
    if (argc==1){
        std::cout << "Please provide an argument.\n";
    }
    else {
        try {
            // Instantiate Driver
            sql::Driver* driver = sql::mariadb::get_driver_instance();

            // Configure Connection
            sql::SQLString url("jdbc:mariadb://localhost:3306/todo");
            sql::Properties properties({{"user", "app_user"}, {"password", "Password123!"}});

            // Establish Connection
            std::unique_ptr conn(driver->connect(url, properties));

            // Use arguments to determine execution next steps
            if (!strcmp(argv[1],"showTasks")) {
                showTasks(conn);
            }
            else if (!strcmp(argv[1],"addTask")) {
                if (argc != 3) {
                    std::cout << "Invalid arguments";
                    return 1;
                }
                addTask(conn, argv[2]);
            }
            else if (!strcmp(argv[1],"updateTaskStatus")) {
                if (argc != 4) {
                    std::cout << "Invalid arguments";
                    return 1;
                }
                updateTaskStatus(conn, atoi(argv[2]), argv[3]);
            }
            else if (!strcmp(argv[1],"deleteTask")) {
                if (argc != 3) {
                    std::cout << "Invalid arguments"; return 1; } deleteTask(conn, atoi(argv[2])); } // Close Connection conn->close();
        }
        catch(sql::SQLException& e){
            std::cerr << "Error Connecting to MariaDB Platform: " << e.what() << std::endl;
            // Exit (Failed)
            return 1;
        }
    }

    // Exit (Success)
    return 0;
}

You can also find the full source of this sample application here. The source for this C++ example is contained within a repository for an application called “TODO”, which also includes a web front-end and several other MariaDB connector integration sample applications spanning a multitude of languages.

Running the Application

Within a Linux environment, after you’ve created tasks.cpp, you can build an executable file, called tasks, using a C++ compiler.

$ g++ -o tasks tasks.cpp -std=c++11 -lmariadbcpp

Using the executable file you can supply various argument values to test the CRUD operations.

  • Inserting a new task record by supplying a value for description.
./tasks addTask ‘A New Task’
  • Printing out all task records.
./tasks showTasks
  • Updating a task record’s completed value. Simply supply values for id and completed.
./tasks updateTaskStatus 1 1
  • Delete a task record by supplying an id value.
./tasks deleteTask 1

Just the Beginning

Hopefully, you enjoyed seeing how easy it is to get started using C++ with MariaDB. While this is a great introduction to C++ programming with MariaDB, we’ve only started to scratch the surface of what’s possible!

Looking to dive deeper into MariaDB Connector/C++? Be sure to check out the MariaDB Developer Hub for even more content on the new C++ connector as well as many other MariaDB features and capabilities.

Find the MariaDB Connector/C++ enterprise documentation.